from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy import func
from decimal import Decimal
from ..database import get_db
from ..models import ShiftCollectionCycle, DailyShift
from ..dependencies import get_current_user
from ..models import User
from ..shift_utils import recalc_shift_totals

router = APIRouter()


def _serialize(c: ShiftCollectionCycle) -> dict:
    return {
        'id':           c.id,
        'shift_id':     c.shift_id,
        'pump_id':      c.pump_id,
        'cycle_number': c.cycle_number,
        'cash_total':   float(c.cash_total),
        'card_visa':    float(c.card_visa),
        'card_amex':    float(c.card_amex),
        'card_touch':   float(c.card_touch),
        'credit_total': float(c.credit_total),
        'other_income': float(c.other_income),
        'shortage':     float(c.shortage),
        'advance':      float(c.advance),
        'is_final':     bool(c.is_final),
        'collected_by': c.collected_by,
        'collected_at': c.collected_at.isoformat() if c.collected_at else None,
        'notes':        c.notes,
    }


@router.get("/collection-cycles")
def list_cycles(
    shift_id: int = Query(...),
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    cycles = (
        db.query(ShiftCollectionCycle)
        .filter(ShiftCollectionCycle.shift_id == shift_id)
        .order_by(ShiftCollectionCycle.cycle_number)
        .all()
    )
    return [_serialize(c) for c in cycles]


@router.post("/collection-cycles", status_code=201)
def create_cycle(
    data: dict,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    shift_id = data.get('shift_id')
    if not shift_id:
        raise HTTPException(status_code=400, detail="shift_id is required")

    shift = db.query(DailyShift).filter(DailyShift.id == shift_id).first()
    if not shift:
        raise HTTPException(status_code=404, detail="Shift not found")
    if shift.status in ('FINALIZED', 'LOCKED'):
        raise HTTPException(status_code=403, detail="Cannot add cycles to a finalized or locked shift")

    # Auto-number
    max_num = db.query(func.max(ShiftCollectionCycle.cycle_number)).filter(
        ShiftCollectionCycle.shift_id == shift_id
    ).scalar() or 0

    cycle = ShiftCollectionCycle(
        shift_id     = shift_id,
        pump_id      = data.get('pump_id') or None,
        cycle_number = max_num + 1,
        cash_total   = data.get('cash_total',   0),
        card_visa    = data.get('card_visa',    0),
        card_amex    = data.get('card_amex',    0),
        card_touch   = data.get('card_touch',   0),
        credit_total = data.get('credit_total', 0),
        other_income = data.get('other_income', 0),
        shortage     = data.get('shortage',     0),
        advance      = data.get('advance',      0),
        is_final     = bool(data.get('is_final', False)),
        collected_by = current_user.id,
        notes        = data.get('notes') or None,
    )
    db.add(cycle)
    db.flush()

    recalc_shift_totals(shift_id, db)
    from .audit import log_audit
    log_audit(db, current_user.id, 'CREATE', 'shift_collection_cycles', cycle.id,
              f"Collection cycle #{cycle.cycle_number} created for shift #{shift_id}")
    db.commit()
    db.refresh(cycle)
    return _serialize(cycle)


@router.put("/collection-cycles/{cycle_id}")
def update_cycle(
    cycle_id: int,
    data: dict,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    cycle = db.query(ShiftCollectionCycle).filter(ShiftCollectionCycle.id == cycle_id).first()
    if not cycle:
        raise HTTPException(status_code=404, detail="Collection cycle not found")

    shift = db.query(DailyShift).filter(DailyShift.id == cycle.shift_id).first()
    if shift and shift.status in ('FINALIZED', 'LOCKED'):
        raise HTTPException(status_code=403, detail="Cannot edit cycles on a finalized or locked shift")

    for field in ('cash_total', 'card_visa', 'card_amex', 'card_touch',
                  'credit_total', 'other_income', 'shortage', 'advance', 'notes', 'pump_id'):
        if field in data:
            setattr(cycle, field, data[field])
    if 'is_final' in data:
        cycle.is_final = bool(data['is_final'])
        if cycle.is_final:
            # Ensure only one final cycle per shift
            db.query(ShiftCollectionCycle).filter(
                ShiftCollectionCycle.shift_id == cycle.shift_id,
                ShiftCollectionCycle.id != cycle_id,
            ).update({'is_final': False})

    recalc_shift_totals(cycle.shift_id, db)
    from .audit import log_audit
    log_audit(db, current_user.id, 'UPDATE', 'shift_collection_cycles', cycle_id,
              f"Collection cycle #{cycle.cycle_number} updated for shift #{cycle.shift_id}")
    db.commit()
    db.refresh(cycle)
    return _serialize(cycle)


@router.delete("/collection-cycles/{cycle_id}", status_code=204)
def delete_cycle(
    cycle_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    cycle = db.query(ShiftCollectionCycle).filter(ShiftCollectionCycle.id == cycle_id).first()
    if not cycle:
        raise HTTPException(status_code=404, detail="Collection cycle not found")

    shift = db.query(DailyShift).filter(DailyShift.id == cycle.shift_id).first()
    if shift and shift.status in ('FINALIZED', 'LOCKED'):
        raise HTTPException(status_code=403, detail="Cannot delete cycles on a finalized or locked shift")

    shift_id = cycle.shift_id
    cycle_num = cycle.cycle_number
    from .audit import log_audit
    log_audit(db, current_user.id, 'DELETE', 'shift_collection_cycles', cycle_id,
              f"Deleted collection cycle #{cycle_num} from shift #{shift_id}")
    db.delete(cycle)
    db.flush()

    # Re-index remaining cycles sequentially
    remaining = (
        db.query(ShiftCollectionCycle)
        .filter(ShiftCollectionCycle.shift_id == shift_id)
        .order_by(ShiftCollectionCycle.cycle_number)
        .all()
    )
    for i, c in enumerate(remaining, start=1):
        c.cycle_number = i

    recalc_shift_totals(shift_id, db)
    db.commit()


@router.post("/collection-cycles/{cycle_id}/mark-final")
def mark_cycle_final(
    cycle_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    cycle = db.query(ShiftCollectionCycle).filter(ShiftCollectionCycle.id == cycle_id).first()
    if not cycle:
        raise HTTPException(status_code=404, detail="Collection cycle not found")

    shift = db.query(DailyShift).filter(DailyShift.id == cycle.shift_id).first()
    if shift and shift.status in ('FINALIZED', 'LOCKED'):
        raise HTTPException(status_code=403, detail="Shift is finalized or locked")

    # Clear is_final on all other cycles for this shift
    db.query(ShiftCollectionCycle).filter(
        ShiftCollectionCycle.shift_id == cycle.shift_id,
        ShiftCollectionCycle.id != cycle_id,
    ).update({'is_final': False})

    cycle.is_final = True
    from .audit import log_audit
    log_audit(db, current_user.id, 'UPDATE', 'shift_collection_cycles', cycle_id,
              f"Marked cycle #{cycle.cycle_number} as final for shift #{cycle.shift_id}")
    db.commit()
    db.refresh(cycle)
    return _serialize(cycle)
