from datetime import date, datetime, timedelta, timezone
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from typing import List, Optional

from database import get_db
from models.schedule import ScheduledShift
from models.shift import WaiterShift
from models.user import User
from schemas.schedule import ScheduledShiftCreate, ScheduledShiftUpdate, ScheduledShiftOut
from routers.deps import require_manager

router = APIRouter()


def _duration_hours(start: str, end: str) -> float:
    """Compute hours between two HH:MM strings. Handles overnight spans."""
    sh, sm = int(start[:2]), int(start[3:5])
    eh, em = int(end[:2]), int(end[3:5])
    mins = (eh * 60 + em) - (sh * 60 + sm)
    if mins <= 0:
        mins += 24 * 60  # overnight
    return round(mins / 60, 2)


def _enrich(s: ScheduledShift) -> dict:
    u = s.user
    user_name = (u.full_name or u.username) if u else f"#{s.user_id}"
    hourly_rate = u.hourly_rate if u else None
    dur = _duration_hours(s.start_time, s.end_time)
    estimated_pay = round(dur * hourly_rate, 2) if hourly_rate else None
    return {
        "id": s.id,
        "user_id": s.user_id,
        "user_name": user_name,
        "hourly_rate": hourly_rate,
        "scheduled_date": s.scheduled_date,
        "start_time": s.start_time,
        "end_time": s.end_time,
        "notes": s.notes,
        "created_by_id": s.created_by_id,
        "created_at": s.created_at,
        "duration_hours": dur,
        "estimated_pay": estimated_pay,
    }


@router.get("/", response_model=List[ScheduledShiftOut])
def list_schedule(
    from_date: Optional[date] = Query(default=None, alias="from"),
    to_date: Optional[date] = Query(default=None, alias="to"),
    user_id: Optional[int] = None,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    q = db.query(ScheduledShift)
    if from_date:
        q = q.filter(ScheduledShift.scheduled_date >= from_date)
    if to_date:
        q = q.filter(ScheduledShift.scheduled_date <= to_date)
    if user_id:
        q = q.filter(ScheduledShift.user_id == user_id)
    shifts = q.order_by(ScheduledShift.scheduled_date, ScheduledShift.start_time).all()
    return [_enrich(s) for s in shifts]


@router.post("/", response_model=ScheduledShiftOut, status_code=status.HTTP_201_CREATED)
def create_scheduled_shift(
    body: ScheduledShiftCreate,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    target = db.query(User).filter(User.id == body.user_id, User.is_active == True).first()
    if not target:
        raise HTTPException(status_code=404, detail="User not found")
    s = ScheduledShift(
        user_id=body.user_id,
        scheduled_date=body.scheduled_date,
        start_time=body.start_time,
        end_time=body.end_time,
        notes=body.notes,
        created_by_id=user.id,
    )
    db.add(s)
    db.commit()
    db.refresh(s)
    return _enrich(s)


@router.put("/{shift_id}", response_model=ScheduledShiftOut)
def update_scheduled_shift(
    shift_id: int,
    body: ScheduledShiftUpdate,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    s = db.query(ScheduledShift).filter(ScheduledShift.id == shift_id).first()
    if not s:
        raise HTTPException(status_code=404, detail="Scheduled shift not found")
    for field, value in body.model_dump(exclude_none=True).items():
        setattr(s, field, value)
    db.commit()
    db.refresh(s)
    return _enrich(s)


@router.delete("/{shift_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_scheduled_shift(
    shift_id: int,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    s = db.query(ScheduledShift).filter(ScheduledShift.id == shift_id).first()
    if not s:
        raise HTTPException(status_code=404, detail="Scheduled shift not found")
    db.delete(s)
    db.commit()


@router.get("/week")
def week_schedule(
    week_start: Optional[date] = Query(default=None),
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    """Current (or specified) week: scheduled shifts + actual WaiterShifts for comparison."""
    if week_start is None:
        today = date.today()
        week_start = today - timedelta(days=today.weekday())  # Monday
    week_end = week_start + timedelta(days=6)  # Sunday

    scheduled = db.query(ScheduledShift).filter(
        ScheduledShift.scheduled_date >= week_start,
        ScheduledShift.scheduled_date <= week_end,
    ).order_by(ScheduledShift.scheduled_date, ScheduledShift.start_time).all()

    # Actual shifts that started within the week
    week_start_dt = datetime.combine(week_start, datetime.min.time()).replace(tzinfo=timezone.utc)
    week_end_dt = datetime.combine(week_end, datetime.max.time()).replace(tzinfo=timezone.utc)
    actuals = db.query(WaiterShift).filter(
        WaiterShift.started_at >= week_start_dt,
        WaiterShift.started_at <= week_end_dt,
    ).all()

    waiters = {u.id: u for u in db.query(User).filter(User.perm_access_waiter_app == True, User.is_active == True).all()}

    def _dt(dt):
        if not dt:
            return None
        return (dt.isoformat() + "Z") if dt.tzinfo is None else dt.isoformat()

    # Build actual shifts lookup: (user_id, date_str) → list of actual shifts
    actuals_map: dict = {}
    for a in actuals:
        d = a.started_at.date() if a.started_at else None
        if d:
            key = (a.waiter_id, d.isoformat())
            if key not in actuals_map:
                actuals_map[key] = []
            actuals_map[key].append({
                "id": a.id,
                "started_at": _dt(a.started_at),
                "ended_at": _dt(a.ended_at),
                "is_active": a.ended_at is None,
            })

    # Estimate total weekly labor cost
    total_estimated_pay = 0.0
    scheduled_out = []
    for s in scheduled:
        enriched = _enrich(s)
        key = (s.user_id, s.scheduled_date.isoformat())
        enriched["actual_shifts"] = actuals_map.get(key, [])
        scheduled_out.append(enriched)
        if enriched["estimated_pay"]:
            total_estimated_pay += enriched["estimated_pay"]

    return {
        "week_start": week_start.isoformat(),
        "week_end": week_end.isoformat(),
        "scheduled": scheduled_out,
        "total_estimated_pay": round(total_estimated_pay, 2),
        "waiters": [
            {"id": u.id, "name": u.full_name or u.username, "hourly_rate": u.hourly_rate}
            for u in sorted(waiters.values(), key=lambda x: x.full_name or x.username)
        ],
    }
