from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy import and_, extract
from datetime import date, datetime
from typing import Optional
from ..database import get_db
from ..dependencies import get_current_user
from ..models import LeaveRequest, LeaveBalance, Staff, SystemSetting
from ..schemas import LeaveRequestCreate, LeaveRequestUpdate, LeaveRequestOut, LeaveBalanceOut

router = APIRouter()


def _balance_out(lb: LeaveBalance) -> dict:
    return {
        "id":               lb.id,
        "staff_id":         lb.staff_id,
        "staff_name":       lb.staff_member.full_name if lb.staff_member else None,
        "year":             lb.year,
        "annual_total":     lb.annual_total,
        "sick_total":       lb.sick_total,
        "casual_total":     lb.casual_total,
        "annual_used":      lb.annual_used,
        "sick_used":        lb.sick_used,
        "casual_used":      lb.casual_used,
        "annual_remaining": lb.annual_total - lb.annual_used,
        "sick_remaining":   lb.sick_total  - lb.sick_used,
        "casual_remaining": lb.casual_total - lb.casual_used,
    }


def _request_out(lr: LeaveRequest) -> dict:
    return {
        "id":          lr.id,
        "staff_id":    lr.staff_id,
        "staff_name":  lr.staff_member.full_name if lr.staff_member else None,
        "leave_type":  lr.leave_type,
        "date_from":   lr.date_from,
        "date_to":     lr.date_to,
        "days_count":  lr.days_count,
        "reason":      lr.reason,
        "status":      lr.status,
        "reviewed_by": lr.reviewed_by,
        "reviewed_at": lr.reviewed_at,
        "review_note": lr.review_note,
        "created_at":  lr.created_at,
    }


@router.get("/leave/requests", response_model=list[LeaveRequestOut])
def list_leave_requests(
    staff_id: Optional[int] = None,
    status:   Optional[str] = None,
    month:    Optional[int] = None,
    year:     Optional[int] = None,
    db:       Session = Depends(get_db),
    _=Depends(get_current_user),
):
    q = db.query(LeaveRequest)
    if staff_id:
        q = q.filter(LeaveRequest.staff_id == staff_id)
    if status:
        q = q.filter(LeaveRequest.status == status)
    if year:
        q = q.filter(extract('year', LeaveRequest.date_from) == year)
    if month:
        q = q.filter(extract('month', LeaveRequest.date_from) == month)
    rows = q.order_by(LeaveRequest.created_at.desc()).all()
    return [_request_out(r) for r in rows]


@router.post("/leave/requests", response_model=LeaveRequestOut, status_code=201)
def create_leave_request(
    body: LeaveRequestCreate,
    db:   Session = Depends(get_db),
    current_user=Depends(get_current_user),
):
    staff = db.query(Staff).filter(Staff.id == body.staff_id).first()
    if not staff:
        raise HTTPException(status_code=404, detail="Staff not found")

    lr = LeaveRequest(
        staff_id=body.staff_id,
        leave_type=body.leave_type,
        date_from=body.date_from,
        date_to=body.date_to,
        days_count=body.days_count,
        reason=body.reason,
    )
    db.add(lr)
    db.flush()
    from .audit import log_audit
    log_audit(db, current_user.id, 'CREATE', 'leave_requests', lr.id,
              f"Leave request: {staff.full_name} {body.leave_type} {body.date_from} to {body.date_to}")
    db.commit()
    db.refresh(lr)
    return _request_out(lr)


@router.put("/leave/requests/{req_id}", response_model=LeaveRequestOut)
def review_leave_request(
    req_id: int,
    body:   LeaveRequestUpdate,
    db:     Session = Depends(get_db),
    user=Depends(get_current_user),
):
    if body.status not in ('APPROVED', 'REJECTED'):
        raise HTTPException(status_code=400, detail="status must be APPROVED or REJECTED")

    lr = db.query(LeaveRequest).filter(LeaveRequest.id == req_id).first()
    if not lr:
        raise HTTPException(status_code=404, detail="Leave request not found")
    if lr.status != 'PENDING':
        raise HTTPException(status_code=400, detail="Request already reviewed")

    lr.status      = body.status
    lr.reviewed_by = user.id
    lr.reviewed_at = datetime.utcnow()
    lr.review_note = body.review_note
    from .audit import log_audit
    log_audit(db, user.id, 'UPDATE', 'leave_requests', req_id,
              f"Leave request #{req_id} {body.status}: {lr.leave_type} for staff #{lr.staff_id}")

    if body.status == 'APPROVED':
        year = lr.date_from.year
        lb = db.query(LeaveBalance).filter(
            LeaveBalance.staff_id == lr.staff_id,
            LeaveBalance.year     == year,
        ).first()
        if lb:
            field_map = {
                'ANNUAL': 'annual_used',
                'SICK':   'sick_used',
                'CASUAL': 'casual_used',
            }
            setattr(lb, field_map[lr.leave_type], getattr(lb, field_map[lr.leave_type]) + lr.days_count)

    db.commit()
    db.refresh(lr)
    return _request_out(lr)


@router.get("/leave/balance/{staff_id}", response_model=LeaveBalanceOut)
def get_leave_balance(
    staff_id: int,
    year:     int = Query(default=None),
    db:       Session = Depends(get_db),
    _=Depends(get_current_user),
):
    if year is None:
        year = date.today().year
    lb = db.query(LeaveBalance).filter(
        LeaveBalance.staff_id == staff_id,
        LeaveBalance.year     == year,
    ).first()
    if not lb:
        raise HTTPException(status_code=404, detail="Leave balance not found; call /leave/balance/init first")
    return _balance_out(lb)


@router.get("/leave/calendar", response_model=list[LeaveRequestOut])
def get_leave_calendar(
    month: int = Query(..., ge=1, le=12),
    year:  int = Query(..., ge=2020),
    db:    Session = Depends(get_db),
    _=Depends(get_current_user),
):
    """All approved leaves overlapping the given month."""
    start = date(year, month, 1)
    from calendar import monthrange
    end = date(year, month, monthrange(year, month)[1])

    rows = (
        db.query(LeaveRequest)
        .filter(
            LeaveRequest.status   == 'APPROVED',
            LeaveRequest.date_from <= end,
            LeaveRequest.date_to   >= start,
        )
        .all()
    )
    return [_request_out(r) for r in rows]


@router.post("/leave/balance/init", status_code=201)
def init_leave_balances(
    year: int = Query(..., ge=2020),
    db:   Session = Depends(get_db),
    _=Depends(get_current_user),
):
    """Initialize leave balances for all active staff for the given year."""
    # Get entitlements from system settings
    def setting(key: str, default: int) -> int:
        s = db.query(SystemSetting).filter(SystemSetting.setting_key == key).first()
        return int(s.setting_value) if s else default

    annual  = setting('leave.annual_days',  14)
    sick    = setting('leave.sick_days',     7)
    casual  = setting('leave.casual_days',   3)

    active_staff = db.query(Staff).filter(Staff.status == 'ACTIVE').all()
    created = 0
    for s in active_staff:
        exists = db.query(LeaveBalance).filter(
            LeaveBalance.staff_id == s.id,
            LeaveBalance.year     == year,
        ).first()
        if not exists:
            lb = LeaveBalance(
                staff_id=s.id,
                year=year,
                annual_total=annual,
                sick_total=sick,
                casual_total=casual,
            )
            db.add(lb)
            created += 1
    db.commit()
    return {"created": created, "year": year}
