from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from datetime import datetime, timedelta
import random
from typing import Optional, List
import os
import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
from utils.database import DatabaseManager

app = FastAPI(title="Schedule Service")

# Database setup
db = DatabaseManager()
schedules_collection = db.get_collection("schedules")
services_collection = db.get_collection("services")
businesses_collection = db.get_collection("businesses")

class TimeSlotCheck(BaseModel):
    business_id: str
    service_id: str
    requested_date: datetime
    requested_time: str

class Address(BaseModel):
    street: str
    city: str
    postal_code: str

class Schedule(BaseModel):
    business_id: str
    service_id: str
    customer_name: str
    customer_phone: str
    address: Address
    start_datetime: datetime

class ScheduleUpdate(BaseModel):
    booking_id: str
    new_datetime: datetime

def generate_booking_id():
    return str(random.randint(10000, 99999))

def get_service_duration(service_id: str):
    service =  services_collection.find_one({"_id": ObjectId(service_id)})
    return service["duration_minutes"]

def get_business_schedule(business_id: str):
    business =  businesses_collection.find_one({"_id": ObjectId(business_id)})
    return business["schedule"]

@app.post("/check-availability")
async def check_availability(slot: TimeSlotCheck):
    try:
        # Get service duration
        service = await services_collection.find_one({"_id": ObjectId(slot.service_id)})
        if not service:
            raise HTTPException(status_code=404, detail="Service not found")
        
        duration = service["duration_minutes"]
        
        # Get business schedule
        business = await businesses_collection.find_one({"_id": ObjectId(slot.business_id)})
        if not business:
            raise HTTPException(status_code=404, detail="Business not found")
        
        work_schedule = business["schedule"]
        
        # Check if requested day is a working day
        requested_day = slot.requested_date.strftime("%A")
        if requested_day in work_schedule["off_days"]:
            return {
                "available": False,
                "reason": "Business is closed on this day",
                "next_available": find_next_available_slot(slot.business_id, slot.service_id, slot.requested_date)
            }
        
        # Check if time is within working hours
        requested_time = datetime.strptime(slot.requested_time, "%H:%M").time()
        work_start = datetime.strptime(work_schedule["work_hours"]["start"], "%H:%M").time()
        work_end = datetime.strptime(work_schedule["work_hours"]["end"], "%H:%M").time()
        
        if requested_time < work_start or requested_time > work_end:
            return {
                "available": False,
                "reason": "Outside business hours",
                "next_available": find_next_available_slot(slot.business_id, slot.service_id, slot.requested_date)
            }
        
        # Check for overlapping appointments
        requested_datetime = datetime.combine(slot.requested_date, requested_time)
        service_end_time = requested_datetime + timedelta(minutes=duration)
        
        existing_appointments = await schedules_collection.find({
            "business_id": slot.business_id,
            "start_datetime": {
                "$lt": service_end_time
            },
            "end_datetime": {
                "$gt": requested_datetime
            }
        }).to_list(length=None)
        
        if existing_appointments:
            return {
                "available": False,
                "reason": "Time slot already booked",
                "next_available": find_next_available_slot(slot.business_id, slot.service_id, slot.requested_date)
            }
        
        return {
            "available": True,
            "slot_duration": duration,
            "start_time": requested_datetime,
            "end_time": service_end_time
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.post("/create-schedule")
async def create_schedule(schedule: Schedule):
    try:
        # Check availability first
        availability = await check_availability(TimeSlotCheck(
            business_id=schedule.business_id,
            service_id=schedule.service_id,
            requested_date=schedule.start_datetime.date(),
            requested_time=schedule.start_datetime.strftime("%H:%M")
        ))
        
        if not availability["available"]:
            raise HTTPException(status_code=400, detail="Time slot not available")
        
        # Generate booking ID
        booking_id = generate_booking_id()
        
        # Create schedule
        schedule_doc = {
            "booking_id": booking_id,
            **schedule.dict(),
            "end_datetime": availability["end_time"],
            "status": "confirmed",
            "created_at": datetime.utcnow()
        }
        
        result = await schedules_collection.insert_one(schedule_doc)
        
        # Send confirmation SMS
        async with httpx.AsyncClient() as client:
            await client.post(
                "http://sms-service:8003/send-sms",
                json={
                    "phone": schedule.customer_phone,
                    "message": f"Your appointment has been confirmed. Booking ID: {booking_id}"
                }
            )
        
        return {
            "message": "Schedule created successfully",
            "booking_id": booking_id,
            "schedule_id": str(result.inserted_id)
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.put("/update-schedule")
async def update_schedule(update: ScheduleUpdate):
    try:
        # Find existing schedule
        schedule = await schedules_collection.find_one({"booking_id": update.booking_id})
        if not schedule:
            raise HTTPException(status_code=404, detail="Booking not found")
        
        # Check new time availability
        availability = await check_availability(TimeSlotCheck(
            business_id=schedule["business_id"],
            service_id=schedule["service_id"],
            requested_date=update.new_datetime.date(),
            requested_time=update.new_datetime.strftime("%H:%M")
        ))
        
        if not availability["available"]:
            raise HTTPException(status_code=400, detail="New time slot not available")
        
        # Update schedule
        result = await schedules_collection.update_one(
            {"booking_id": update.booking_id},
            {
                "$set": {
                    "start_datetime": update.new_datetime,
                    "end_datetime": availability["end_time"],
                    "updated_at": datetime.utcnow()
                }
            }
        )
        
        # Send update confirmation
        async with httpx.AsyncClient() as client:
            await client.post(
                "http://sms-service:8003/send-sms",
                json={
                    "phone": schedule["customer_phone"],
                    "message": f"Your appointment has been rescheduled. Booking ID: {update.booking_id}"
                }
            )
        
        return {"message": "Schedule updated successfully"}
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/schedules")
async def get_schedules(
    start_date: Optional[datetime] = None,
    end_date: Optional[datetime] = None,
    customer_phone: Optional[str] = None,
    business_id: Optional[str] = None
):
    try:
        query = {}
        
        # Add date range filter
        if start_date or end_date:
            query["start_datetime"] = {}
            if start_date:
                query["start_datetime"]["$gte"] = start_date
            if end_date:
                query["start_datetime"]["$lte"] = end_date
        
        # Add customer filter
        if customer_phone:
            query["customer_phone"] = customer_phone
            
        # Add business filter
        if business_id:
            query["business_id"] = business_id
        
        schedules = await schedules_collection.find(query).sort("start_datetime", 1).to_list(length=None)
        
        return {
            "schedules": [
                {
                    **schedule,
                    "_id": str(schedule["_id"]),
                    "business_id": str(schedule["business_id"])
                }
                for schedule in schedules
            ]
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

async def find_next_available_slot(business_id: str, service_id: str, from_date: datetime):
    # Implementation to find next available slot
    # This would check the next few days/times based on business schedule
    # and existing appointments
    pass 