from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy.dialects.mysql import insert as mysql_insert
from sqlalchemy import and_, func
from ..database import get_db
from ..dependencies import get_current_user
from ..models import Allowance, Staff, SystemSetting, DailyShift, PumpReading
from ..schemas import AllowanceCreate, AllowanceUpdate, AllowanceOut

router = APIRouter()


def _out(a: Allowance) -> dict:
    return {
        "id":             a.id,
        "staff_id":       a.staff_id,
        "staff_name":     a.staff_member.full_name if a.staff_member else None,
        "month":          a.month,
        "year":           a.year,
        "allowance_type": a.allowance_type,
        "amount":         a.amount,
        "notes":          a.notes,
        "created_at":     a.created_at,
    }


@router.get("/allowances", response_model=list[AllowanceOut])
def list_allowances(
    month: int = Query(..., ge=1, le=12),
    year:  int = Query(..., ge=2020),
    db:    Session = Depends(get_db),
    _=Depends(get_current_user),
):
    rows = (
        db.query(Allowance)
        .filter(and_(Allowance.month == month, Allowance.year == year))
        .order_by(Allowance.staff_id, Allowance.allowance_type)
        .all()
    )
    return [_out(r) for r in rows]


@router.post("/allowances", response_model=AllowanceOut, status_code=201)
def upsert_allowance(
    body: AllowanceCreate,
    db:   Session = Depends(get_db),
    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")

    existing = db.query(Allowance).filter(
        and_(
            Allowance.staff_id       == body.staff_id,
            Allowance.month          == body.month,
            Allowance.year           == body.year,
            Allowance.allowance_type == body.allowance_type,
        )
    ).first()

    from .audit import log_audit
    if existing:
        existing.amount = body.amount
        existing.notes  = body.notes
        log_audit(db, user.id, 'UPDATE', 'allowances', existing.id,
                  f"Updated {body.allowance_type} allowance for staff #{body.staff_id}: Rs {body.amount}")
        db.commit()
        db.refresh(existing)
        return _out(existing)

    al = Allowance(
        staff_id=body.staff_id,
        month=body.month,
        year=body.year,
        allowance_type=body.allowance_type,
        amount=body.amount,
        notes=body.notes,
        created_by=user.id,
    )
    db.add(al)
    db.flush()
    log_audit(db, user.id, 'CREATE', 'allowances', al.id,
              f"Created {body.allowance_type} allowance for staff #{body.staff_id}: Rs {body.amount}")
    db.commit()
    db.refresh(al)
    return _out(al)


@router.put("/allowances/{al_id}", response_model=AllowanceOut)
def update_allowance(
    al_id: int,
    body:  AllowanceUpdate,
    db:    Session = Depends(get_db),
    current_user=Depends(get_current_user),
):
    al = db.query(Allowance).filter(Allowance.id == al_id).first()
    if not al:
        raise HTTPException(status_code=404, detail="Allowance not found")
    al.amount = body.amount
    if body.notes is not None:
        al.notes = body.notes
    from .audit import log_audit
    log_audit(db, current_user.id, 'UPDATE', 'allowances', al_id,
              f"Updated {al.allowance_type} allowance for staff #{al.staff_id}: Rs {body.amount}")
    db.commit()
    db.refresh(al)
    return _out(al)


@router.delete("/allowances/{al_id}", status_code=204)
def delete_allowance(
    al_id: int,
    db:    Session = Depends(get_db),
    current_user=Depends(get_current_user),
):
    al = db.query(Allowance).filter(Allowance.id == al_id).first()
    if not al:
        raise HTTPException(status_code=404, detail="Allowance not found")
    from .audit import log_audit
    log_audit(db, current_user.id, 'DELETE', 'allowances', al_id,
              f"Deleted {al.allowance_type} allowance for staff #{al.staff_id}")
    db.delete(al)
    db.commit()


@router.post("/allowances/generate", status_code=201)
def generate_allowances(
    month: int = Query(..., ge=1, le=12),
    year:  int = Query(..., ge=2020),
    db:    Session = Depends(get_db),
    user=Depends(get_current_user),
):
    """Auto-generate allowances based on system settings for active staff."""
    import calendar
    from datetime import date as date_type

    def setting(key: str) -> tuple[bool, float]:
        enabled_key = f"{key}.enabled"
        amount_key  = f"{key}.amount"
        e = db.query(SystemSetting).filter(SystemSetting.setting_key == enabled_key).first()
        a = db.query(SystemSetting).filter(SystemSetting.setting_key == amount_key).first()
        enabled = (e.setting_value.lower() == 'true') if e else False
        amount  = float(a.setting_value) if a else 0.0
        return enabled, amount

    ot_en, ot_amt  = setting('allowance.overtime')
    pf_en, pf_amt  = setting('allowance.performance')
    at_en, at_amt  = setting('allowance.attendance')

    active_staff = db.query(Staff).filter(Staff.status == 'ACTIVE').all()
    active_ids   = {s.id for s in active_staff}
    created = 0

    # ── OVERTIME / PERFORMANCE / ATTENDANCE (flat per-staff amounts) ──────────
    for s in active_staff:
        for al_type, enabled, amount in [
            ('OVERTIME',    ot_en, ot_amt),
            ('PERFORMANCE', pf_en, pf_amt),
            ('ATTENDANCE',  at_en, at_amt),
        ]:
            if not enabled:
                continue
            exists = db.query(Allowance).filter(
                and_(
                    Allowance.staff_id       == s.id,
                    Allowance.month          == month,
                    Allowance.year           == year,
                    Allowance.allowance_type == al_type,
                )
            ).first()
            if not exists:
                db.add(Allowance(
                    staff_id=s.id,
                    month=month,
                    year=year,
                    allowance_type=al_type,
                    amount=amount,
                    notes='Auto-generated',
                    created_by=user.id,
                ))
                created += 1

    # ── DAILY — calculated from shift daily allowance setting ─────────────────
    shift_rates: dict[str, float] = {}
    for st in ['EM', 'DAY', 'MS', 'MG', 'ES']:
        s = db.query(SystemSetting).filter(
            SystemSetting.setting_key == f'shift.{st}.daily_allowance'
        ).first()
        if s and s.setting_value:
            shift_rates[st] = float(s.setting_value)

    start_date = date_type(year, month, 1)
    end_date   = date_type(year, month, calendar.monthrange(year, month)[1])

    daily_by_staff: dict[int, float] = {}

    # Managers: counted via DailyShift.staff_id
    manager_counts = (
        db.query(DailyShift.staff_id, DailyShift.shift_type, func.count(DailyShift.id))
        .filter(
            DailyShift.record_date >= start_date,
            DailyShift.record_date <= end_date,
            DailyShift.staff_id.isnot(None),
        )
        .group_by(DailyShift.staff_id, DailyShift.shift_type)
        .all()
    )
    for staff_id, shift_type, count in manager_counts:
        if staff_id not in active_ids:
            continue
        rate = shift_rates.get(shift_type, 0.0)
        daily_by_staff[staff_id] = daily_by_staff.get(staff_id, 0.0) + count * rate

    # Pumpers: counted via PumpReading.staff_id (distinct shifts per pumper)
    pumper_counts = (
        db.query(PumpReading.staff_id, DailyShift.shift_type, func.count(func.distinct(DailyShift.id)))
        .join(DailyShift, PumpReading.shift_id == DailyShift.id)
        .filter(
            DailyShift.record_date >= start_date,
            DailyShift.record_date <= end_date,
            PumpReading.staff_id.isnot(None),
        )
        .group_by(PumpReading.staff_id, DailyShift.shift_type)
        .all()
    )
    for staff_id, shift_type, count in pumper_counts:
        if staff_id not in active_ids:
            continue
        rate = shift_rates.get(shift_type, 0.0)
        daily_by_staff[staff_id] = daily_by_staff.get(staff_id, 0.0) + count * rate

    for staff_id, amount in daily_by_staff.items():
        if amount <= 0:
            continue
        exists = db.query(Allowance).filter(
            and_(
                Allowance.staff_id       == staff_id,
                Allowance.month          == month,
                Allowance.year           == year,
                Allowance.allowance_type == 'DAILY',
            )
        ).first()
        if not exists:
            db.add(Allowance(
                staff_id=staff_id,
                month=month,
                year=year,
                allowance_type='DAILY',
                amount=amount,
                notes='Auto-generated from shift roster',
                created_by=user.id,
            ))
            created += 1

    db.commit()
    return {"created": created, "month": month, "year": year}
