from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session, joinedload
from sqlalchemy import and_
from sqlalchemy.exc import IntegrityError
from typing import List, Optional
from datetime import date
from decimal import Decimal
from ..database import get_db
from ..models import DailyShift, PumpReading, CashDenomination, CreditSale, CreditCustomer, Pump, Staff, ShiftCollectionCycle, ShiftHandoverNote, CashHandover, ShiftTemplate
from ..schemas import ShiftCreate, ShiftUpdate, ShiftOut, ShiftListItem, CreditSaleOut, PumpReadingOut, PumpReadingPatch, ShiftHandoverNoteUpdate, ShiftHandoverNoteOut
from ..dependencies import get_current_user
from ..models import User
from ..shift_utils import recalc_shift_totals

router = APIRouter()


def _calc_denom_total(d) -> Decimal:
    return Decimal(
        d.note_5000 * 5000 + d.note_2000 * 2000 + d.note_1000 * 1000
        + d.note_500 * 500 + d.note_100 * 100 + d.note_50 * 50
        + d.note_20 * 20 + d.note_10 * 10
    )


def _build_shift_out(shift: DailyShift) -> dict:
    staff_name = shift.staff_member.full_name if shift.staff_member else None
    pump_readings = []
    for pr in shift.pump_readings:
        pump_readings.append({
            "id": pr.id,
            "pump_id": pr.pump_id,
            "pump_code": pr.pump.pump_code if pr.pump else None,
            "pump_number": pr.pump.pump_number if pr.pump else None,
            "fuel_type": pr.pump.fuel_type if pr.pump else None,
            "staff_id": pr.staff_id,
            "pumper_name": pr.pumper.full_name if pr.pumper else None,
            "starting_meter": pr.starting_meter,
            "ending_meter": pr.ending_meter,
            "meter_out": pr.meter_out,
            "testing_ltr": pr.testing_ltr,
            "sale_ltr": pr.sale_ltr,
            "fuel_rate": pr.fuel_rate,
            "sale_amount": pr.sale_amount,
        })
    credit_sales = []
    for cs in shift.credit_sales:
        credit_sales.append({
            "id":            cs.id,
            "customer_id":   cs.customer_id,
            "customer_name": cs.customer.company_name if cs.customer else None,
            "pumper_id":     cs.pumper_id,
            "pumper_name":   cs.pumper.full_name if cs.pumper else None,
            "bill_no":       cs.bill_no,
            "amount":        cs.amount,
            "vehicle_no":    cs.vehicle_no,
        })
    denominations = []
    for d in sorted(shift.cash_denominations, key=lambda x: x.bag_number):
        denominations.append({
            "id": d.id,
            "bag_number": d.bag_number,
            "staff_id": d.staff_id,
            "pumper_name": d.pumper.full_name if d.pumper else None,
            "note_5000": d.note_5000,
            "note_2000": d.note_2000,
            "note_1000": d.note_1000,
            "note_500":  d.note_500,
            "note_100":  d.note_100,
            "note_50":   d.note_50,
            "note_20":   d.note_20,
            "note_10":   d.note_10,
            "calculated_total": d.calculated_total,
        })

    collection_cycles = []
    for cc in getattr(shift, 'collection_cycles', []):
        collection_cycles.append({
            "id":           cc.id,
            "shift_id":     cc.shift_id,
            "cycle_number": cc.cycle_number,
            "cash_total":   float(cc.cash_total),
            "card_visa":    float(cc.card_visa),
            "card_amex":    float(cc.card_amex),
            "card_touch":   float(cc.card_touch),
            "credit_total": float(cc.credit_total),
            "other_income": float(cc.other_income),
            "shortage":     float(cc.shortage),
            "advance":      float(cc.advance),
            "is_final":     bool(cc.is_final),
            "collected_by": cc.collected_by,
            "collected_at": cc.collected_at.isoformat() if cc.collected_at else None,
            "notes":        cc.notes,
        })

    return {
        "id":               shift.id,
        "record_date":      shift.record_date,
        "shift_type":       shift.shift_type,
        "staff_id":         shift.staff_id,
        "staff_name":       staff_name,
        "cash_collected":   shift.cash_collected,
        "card_visa":        shift.card_visa,
        "card_amex":        shift.card_amex,
        "card_touch":       shift.card_touch,
        "credit_total":     shift.credit_total,
        "other_income":     shift.other_income,
        "shortage":         shift.shortage,
        "advance":          shift.advance,
        "total_sale_calc":  shift.total_sale_calc,
        "total_collected":  shift.total_collected,
        "difference":       shift.difference,
        "is_locked":        shift.is_locked,
        "status":           shift.status if shift.status else 'DRAFT',
        "notes":            shift.notes,
        "pump_readings":    pump_readings,
        "credit_sales":     credit_sales,
        "denominations":    denominations,
        "collection_cycles": collection_cycles,
        "created_at":       shift.created_at,
    }



def _apply_shift_data(shift: DailyShift, body: ShiftCreate | ShiftUpdate, db: Session):
    """Compute derived fields and apply to a DailyShift instance."""
    pump_readings = body.pump_readings or []
    credit_sales  = body.credit_sales  if body.credit_sales  is not None else []
    denominations = body.denominations if body.denominations is not None else []

    # ── Pump readings ──────────────────────────────────────────
    # Delete existing pump readings and re-insert
    db.query(PumpReading).filter(PumpReading.shift_id == shift.id).delete()

    total_sale_calc = Decimal('0')
    for pr_in in pump_readings:
        meter_out = pr_in.ending_meter - pr_in.starting_meter
        sale_ltr  = meter_out - pr_in.testing_ltr
        sale_amt  = sale_ltr * pr_in.fuel_rate
        total_sale_calc += sale_amt
        pr = PumpReading(
            shift_id       = shift.id,
            pump_id        = pr_in.pump_id,
            staff_id       = pr_in.staff_id,
            starting_meter = pr_in.starting_meter,
            ending_meter   = pr_in.ending_meter,
            meter_out      = round(meter_out, 3),
            testing_ltr    = pr_in.testing_ltr,
            sale_ltr       = round(sale_ltr, 3),
            fuel_rate      = pr_in.fuel_rate,
            sale_amount    = round(sale_amt, 2),
        )
        db.add(pr)

    # ── Collections ────────────────────────────────────────────
    cash    = body.cash_collected or Decimal('0')
    visa    = body.card_visa      or Decimal('0')
    amex    = body.card_amex      or Decimal('0')
    touch   = body.card_touch     or Decimal('0')
    credit  = body.credit_total   or Decimal('0')
    other   = body.other_income   or Decimal('0')
    short   = body.shortage       or Decimal('0')
    advance = body.advance        or Decimal('0')

    total_collected = cash + visa + amex + touch + credit + other + short + advance
    difference      = total_sale_calc - total_collected

    shift.cash_collected  = cash
    shift.card_visa       = visa
    shift.card_amex       = amex
    shift.card_touch      = touch
    shift.credit_total    = credit
    shift.other_income    = other
    shift.shortage        = short
    shift.advance         = advance
    shift.total_sale_calc = round(total_sale_calc, 2)
    shift.total_collected = round(total_collected, 2)
    shift.difference      = round(difference, 2)

    # ── Denominations ──────────────────────────────────────────
    db.query(CashDenomination).filter(CashDenomination.shift_id == shift.id).delete()
    for d in denominations:
        total = _calc_denom_total(d)
        db.add(CashDenomination(
            shift_id         = shift.id,
            bag_number       = d.bag_number,
            staff_id         = d.staff_id,
            note_5000        = d.note_5000,
            note_2000        = d.note_2000,
            note_1000        = d.note_1000,
            note_500         = d.note_500,
            note_100         = d.note_100,
            note_50          = d.note_50,
            note_20          = d.note_20,
            note_10          = d.note_10,
            calculated_total = total,
        ))

    # ── Credit sales ───────────────────────────────────────────
    # Only replace if explicitly provided
    if body.credit_sales is not None:
        db.query(CreditSale).filter(
            CreditSale.shift_id == shift.id
        ).delete()
        for cs in credit_sales:
            db.add(CreditSale(
                sale_date   = shift.record_date,
                shift_id    = shift.id,
                customer_id = cs.customer_id,
                pumper_id   = getattr(cs, 'pumper_id', None),
                bill_no     = cs.bill_no,
                amount      = cs.amount,
                vehicle_no  = cs.vehicle_no,
                fuel_type   = cs.fuel_type,
                liters      = cs.liters,
                notes       = cs.notes,
            ))
        # Recalculate customer balances for affected customers
        customer_ids = {cs.customer_id for cs in credit_sales}
        for cid in customer_ids:
            _recalc_customer_balance(cid, db)


def _recalc_customer_balance(customer_id: int, db: Session):
    from sqlalchemy import func, text
    result = db.execute(
        text("""
            UPDATE credit_customers cc
            SET current_balance = (
                SELECT COALESCE(SUM(cs.amount),0) FROM credit_sales cs WHERE cs.customer_id = cc.id
            ) - (
                SELECT COALESCE(SUM(cp.amount),0) FROM credit_payments cp WHERE cp.customer_id = cc.id
            )
            WHERE cc.id = :cid
        """),
        {"cid": customer_id}
    )


def _adopt_shift(shift: DailyShift, body, current_user, db: Session):
    """Promote a bare roster DRAFT into a real started shift."""
    from ..models import ShiftTemplate
    if shift.staff_id is None and body.staff_id:
        shift.staff_id = body.staff_id
    if body.notes:
        shift.notes = body.notes
    if not shift.shift_template_id:
        template = db.query(ShiftTemplate).filter(
            ShiftTemplate.code == shift.shift_type.upper()
        ).first()
        if template:
            shift.shift_template_id = template.id
            shift.shift_start_time  = template.start_time
            shift.shift_end_time    = template.end_time
    shift.created_by = shift.created_by or current_user.id
    shift.updated_by = current_user.id
    _apply_shift_data(shift, body, db)
    if shift.total_sale_calc > 0:
        shift.status = 'ACTIVE'


# ── Routes ─────────────────────────────────────────────────────

@router.get("/last-meters")
def last_meters(
    shift_type: Optional[str] = None,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    """Return the most recent ending_meter for every active pump (all shifts)."""
    from ..models import Pump as PumpModel
    pumps = db.query(PumpModel).filter(PumpModel.is_active == True).all()
    result = {}
    for p in pumps:
        row = (
            db.query(PumpReading.ending_meter)
            .join(DailyShift, PumpReading.shift_id == DailyShift.id)
            .filter(PumpReading.pump_id == p.id, PumpReading.ending_meter > 0)
            .order_by(DailyShift.record_date.desc(), DailyShift.id.desc())
            .first()
        )
        result[p.pump_code] = float(row.ending_meter) if row else None
    return result


@router.get("", response_model=List[ShiftListItem])
def list_shifts(
    from_date:  Optional[date] = Query(None),
    to_date:    Optional[date] = Query(None),
    shift_type: Optional[str]  = Query(None),
    staff_id:   Optional[int]  = Query(None),
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    q = db.query(DailyShift).options(
        joinedload(DailyShift.staff_member),
        joinedload(DailyShift.pump_readings),
    )
    if from_date:
        q = q.filter(DailyShift.record_date >= from_date)
    if to_date:
        q = q.filter(DailyShift.record_date <= to_date)
    if shift_type:
        q = q.filter(DailyShift.shift_type == shift_type)
    if staff_id:
        q = q.filter(DailyShift.staff_id == staff_id)

    shifts = q.order_by(DailyShift.record_date.desc(), DailyShift.shift_type).all()
    result = []
    for s in shifts:
        result.append(ShiftListItem(
            id              = s.id,
            record_date     = s.record_date,
            shift_type      = s.shift_type,
            staff_name      = s.staff_member.full_name if s.staff_member else None,
            total_sale_calc = s.total_sale_calc,
            total_collected = s.total_collected,
            difference      = s.difference,
            is_locked       = s.is_locked,
            status          = s.status if s.status else 'DRAFT',
            pump_count      = len(s.pump_readings),
        ))
    return result


@router.get("/{shift_id}")
def get_shift(
    shift_id: int,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    shift = (
        db.query(DailyShift)
        .options(
            joinedload(DailyShift.staff_member),
            joinedload(DailyShift.pump_readings).joinedload(PumpReading.pump),
            joinedload(DailyShift.pump_readings).joinedload(PumpReading.pumper),
            joinedload(DailyShift.credit_sales).joinedload(CreditSale.customer),
            joinedload(DailyShift.cash_denominations),
            joinedload(DailyShift.collection_cycles),
        )
        .filter(DailyShift.id == shift_id)
        .first()
    )
    if not shift:
        raise HTTPException(status_code=404, detail="Shift not found")
    return _build_shift_out(shift)


@router.post("", status_code=201)
def create_shift(
    body: ShiftCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    def _find_existing():
        return db.query(DailyShift).filter(
            DailyShift.record_date == body.record_date,
            DailyShift.shift_type  == body.shift_type,
        ).first()

    # Check for duplicate (same date + shift_type)
    existing = _find_existing()
    if existing:
        if existing.status in ('FINALIZED', 'LOCKED'):
            raise HTTPException(
                status_code=409,
                detail=f"Shift {body.shift_type} on {body.record_date} is already {existing.status}.",
            )
        # DRAFT/ACTIVE created by roster — adopt it as the real shift
        shift = existing
        _adopt_shift(shift, body, current_user, db)
        from .audit import log_audit
        log_audit(db, current_user.id, 'UPDATE', 'daily_shifts', shift.id,
                  f"Adopted roster DRAFT as {body.shift_type} shift on {body.record_date}")
        db.commit()
    else:
        template = db.query(ShiftTemplate).filter(
            ShiftTemplate.code == body.shift_type.upper()
        ).first()

        shift = DailyShift(
            record_date       = body.record_date,
            shift_type        = body.shift_type,
            shift_template_id = template.id if template else None,
            shift_start_time  = template.start_time if template else None,
            shift_end_time    = template.end_time   if template else None,
            staff_id          = body.staff_id,
            notes             = body.notes,
            status            = 'DRAFT',
            created_by        = current_user.id,
        )
        db.add(shift)
        try:
            db.flush()
        except IntegrityError:
            # Race condition: roster created it between our SELECT and INSERT
            db.rollback()
            existing = _find_existing()
            if not existing:
                raise
            if existing.status in ('FINALIZED', 'LOCKED'):
                raise HTTPException(
                    status_code=409,
                    detail=f"Shift {body.shift_type} on {body.record_date} is already {existing.status}.",
                )
            shift = existing
            _adopt_shift(shift, body, current_user, db)
            from .audit import log_audit
            log_audit(db, current_user.id, 'UPDATE', 'daily_shifts', shift.id,
                      f"Adopted roster DRAFT as {body.shift_type} shift on {body.record_date}")
            db.commit()
        else:
            _apply_shift_data(shift, body, db)
            if shift.total_sale_calc > 0:
                shift.status = 'ACTIVE'
            from .audit import log_audit
            log_audit(db, current_user.id, 'CREATE', 'daily_shifts', shift.id,
                      f"Created {body.shift_type} shift on {body.record_date}")
            db.commit()

    # Reload with relations
    db.refresh(shift)
    shift = (
        db.query(DailyShift)
        .options(
            joinedload(DailyShift.staff_member),
            joinedload(DailyShift.pump_readings).joinedload(PumpReading.pump),
            joinedload(DailyShift.pump_readings).joinedload(PumpReading.pumper),
            joinedload(DailyShift.credit_sales).joinedload(CreditSale.customer),
            joinedload(DailyShift.cash_denominations),
            joinedload(DailyShift.collection_cycles),
        )
        .filter(DailyShift.id == shift.id)
        .first()
    )
    return _build_shift_out(shift)


@router.put("/{shift_id}")
def update_shift(
    shift_id: int,
    body: ShiftUpdate,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    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') or shift.is_locked:
        raise HTTPException(status_code=403, detail="Shift is finalized or locked and cannot be edited")

    if body.staff_id is not None:
        shift.staff_id = body.staff_id
    if body.notes is not None:
        shift.notes = body.notes
    shift.updated_by = current_user.id

    _apply_shift_data(shift, body, db)
    from .audit import log_audit
    log_audit(db, current_user.id, 'UPDATE', 'daily_shifts', shift_id,
              f"Updated {shift.shift_type} shift on {shift.record_date}")
    db.commit()

    shift = (
        db.query(DailyShift)
        .options(
            joinedload(DailyShift.staff_member),
            joinedload(DailyShift.pump_readings).joinedload(PumpReading.pump),
            joinedload(DailyShift.pump_readings).joinedload(PumpReading.pumper),
            joinedload(DailyShift.credit_sales).joinedload(CreditSale.customer),
            joinedload(DailyShift.cash_denominations),
            joinedload(DailyShift.collection_cycles),
        )
        .filter(DailyShift.id == shift_id)
        .first()
    )
    return _build_shift_out(shift)


@router.put("/{shift_id}/pumps/{pump_id}")
def update_shift_pump(
    shift_id: int,
    pump_id: int,
    body: PumpReadingPatch,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """Update or create a single pump reading for a shift."""
    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 edit a finalized or locked shift")

    if body.ending_meter and body.ending_meter < body.starting_meter:
        raise HTTPException(status_code=400, detail="Ending meter cannot be less than starting meter")

    # ── Pump conflict check (only when ending meter is entered, i.e. shift wrap-up) ─────
    if body.ending_meter and shift.shift_start_time and shift.shift_end_time:
        def _to_min(t: str) -> int:
            h, m = t.split(':')
            return int(h) * 60 + int(m)

        s1 = _to_min(shift.shift_start_time)
        e1 = _to_min(shift.shift_end_time)
        if e1 <= s1:
            e1 += 1440  # overnight

        conflicting = (
            db.query(DailyShift)
            .join(PumpReading, PumpReading.shift_id == DailyShift.id)
            .filter(
                DailyShift.record_date == shift.record_date,
                DailyShift.id != shift_id,
                DailyShift.status.notin_(['FINALIZED', 'LOCKED']),
                PumpReading.pump_id == pump_id,
                DailyShift.shift_start_time.isnot(None),
                DailyShift.shift_end_time.isnot(None),
            )
            .all()
        )
        for other in conflicting:
            s2 = _to_min(other.shift_start_time)
            e2 = _to_min(other.shift_end_time)
            if e2 <= s2:
                e2 += 1440
            if s1 < e2 and s2 < e1:
                raise HTTPException(
                    status_code=409,
                    detail=(
                        f"Pump is already assigned to shift {other.shift_type} "
                        f"#{other.id} ({other.shift_start_time}–{other.shift_end_time}) "
                        f"which overlaps with this shift. Finalize that shift first."
                    ),
                )

    # Operator uniqueness check removed — LP2/EURO3 and LAD/LADXM share a physical pump
    # so the same operator legitimately works multiple nozzles in one shift.

    meter_out  = body.ending_meter - body.starting_meter
    sale_ltr   = max(Decimal('0'), meter_out - body.testing_ltr)
    sale_amt   = round(sale_ltr * body.fuel_rate, 2)

    pr = db.query(PumpReading).filter(
        PumpReading.shift_id == shift_id,
        PumpReading.pump_id  == pump_id,
    ).first()

    if pr:
        pr.starting_meter = body.starting_meter
        pr.ending_meter   = body.ending_meter
        pr.meter_out      = round(meter_out, 3)
        pr.testing_ltr    = body.testing_ltr
        pr.sale_ltr       = round(sale_ltr, 3)
        pr.fuel_rate      = body.fuel_rate
        pr.sale_amount    = sale_amt
        pr.staff_id       = body.staff_id
        pr.status         = 'ENTERED'
    else:
        pr = PumpReading(
            shift_id       = shift_id,
            pump_id        = pump_id,
            staff_id       = body.staff_id,
            starting_meter = body.starting_meter,
            ending_meter   = body.ending_meter,
            meter_out      = round(meter_out, 3),
            testing_ltr    = body.testing_ltr,
            sale_ltr       = round(sale_ltr, 3),
            fuel_rate      = body.fuel_rate,
            sale_amount    = sale_amt,
            status         = 'ENTERED',
        )
        db.add(pr)

    db.flush()

    # Recalculate shift totals
    all_readings = db.query(PumpReading).filter(PumpReading.shift_id == shift_id).all()
    shift.total_sale_calc = round(sum(r.sale_amount for r in all_readings), 2)
    recalc_shift_totals(shift.id, db)

    if shift.status == 'DRAFT':
        shift.status = 'ACTIVE'

    shift.updated_by = current_user.id
    from .audit import log_audit
    pump = db.query(Pump).filter(Pump.id == pump_id).first()
    log_audit(db, current_user.id, 'UPDATE', 'pump_readings', pr.id,
              f"Saved pump {pump.pump_code if pump else pump_id} for shift #{shift_id}: {float(sale_amt):.0f} Rs")
    db.commit()
    return {
        "pump_id":          pump_id,
        "pump_code":        pump.pump_code if pump else None,
        "starting_meter":   float(pr.starting_meter),
        "ending_meter":     float(pr.ending_meter),
        "meter_out":        float(pr.meter_out),
        "testing_ltr":      float(pr.testing_ltr),
        "sale_ltr":         float(pr.sale_ltr),
        "fuel_rate":        float(pr.fuel_rate),
        "sale_amount":      float(pr.sale_amount),
        "shift_status":     shift.status,
        "shift_total_sale": float(shift.total_sale_calc),
    }


@router.post("/{shift_id}/finalize")
def finalize_shift(
    shift_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """Finalize a shift: validates pump readings and final collection cycle exist."""
    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 == 'LOCKED':
        raise HTTPException(status_code=403, detail="Shift is locked and cannot be finalized")
    if shift.status == 'FINALIZED':
        return {"message": "Shift already finalized", "shift_id": shift_id, "status": "FINALIZED"}

    total_pumps = db.query(PumpReading).filter(
        PumpReading.shift_id == shift_id,
    ).count()
    completed_pumps = db.query(PumpReading).filter(
        PumpReading.shift_id     == shift_id,
        PumpReading.ending_meter  > PumpReading.starting_meter,
    ).count()
    if total_pumps == 0:
        raise HTTPException(status_code=400, detail="No pump readings found for this shift")
    if completed_pumps < total_pumps:
        raise HTTPException(
            status_code=400,
            detail=f"All pumps must be completed before finalizing ({completed_pumps}/{total_pumps} done)",
        )

    cycle_count = db.query(ShiftCollectionCycle).filter(
        ShiftCollectionCycle.shift_id == shift_id,
    ).count()
    handover_count = db.query(CashHandover).filter(
        CashHandover.shift_id == shift_id,
    ).count()
    # Fallback: handovers saved without shift_id (matched by date+type)
    orphan_count = db.query(CashHandover).filter(
        CashHandover.collection_date == shift.record_date,
        CashHandover.shift_type      == shift.shift_type,
        CashHandover.shift_id.is_(None),
    ).count()
    if cycle_count == 0 and handover_count == 0 and orphan_count == 0:
        raise HTTPException(
            status_code=400,
            detail="At least one cash collection must be recorded before finalizing",
        )

    # Auto-link orphaned handovers to this shift before recalculating
    if orphan_count > 0:
        db.query(CashHandover).filter(
            CashHandover.collection_date == shift.record_date,
            CashHandover.shift_type      == shift.shift_type,
            CashHandover.shift_id.is_(None),
        ).update({'shift_id': shift_id})
        db.flush()

    recalc_shift_totals(shift.id, db)
    shift.status     = 'FINALIZED'
    shift.updated_by = current_user.id
    from .audit import log_audit
    log_audit(db, current_user.id, 'UPDATE', 'daily_shifts', shift_id,
              f"Finalized {shift.shift_type} shift on {shift.record_date}")
    db.commit()
    return {"message": "Shift finalized successfully", "shift_id": shift_id, "status": "FINALIZED"}


@router.post("/{shift_id}/force-finalize")
def force_finalize_shift(
    shift_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """SUPER_ADMIN only: finalize shift bypassing pump-reading and collection-cycle checks."""
    if current_user.role != 'SUPER_ADMIN':
        raise HTTPException(status_code=403, detail="Super Admin access 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 == 'LOCKED':
        raise HTTPException(status_code=403, detail="Shift is locked and cannot be modified")
    recalc_shift_totals(shift.id, db)
    shift.status     = 'FINALIZED'
    shift.updated_by = current_user.id
    db.commit()
    return {"message": "Shift force-finalized", "shift_id": shift_id, "status": "FINALIZED"}


@router.delete("/{shift_id}")
def delete_shift(
    shift_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """SUPER_ADMIN only: permanently delete a shift and all related records."""
    if current_user.role != 'SUPER_ADMIN':
        raise HTTPException(status_code=403, detail="Super Admin access 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 == 'LOCKED':
        raise HTTPException(status_code=403, detail="Locked shifts cannot be deleted")
    from .audit import log_audit
    log_audit(db, current_user.id, 'DELETE', 'daily_shifts', shift_id,
              f"Deleted {shift.shift_type} shift on {shift.record_date}")
    db.delete(shift)
    db.commit()
    return {"message": "Shift deleted", "shift_id": shift_id}


@router.post("/{shift_id}/lock")
def lock_shift(
    shift_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    if current_user.role not in ('OWNER', 'SUPER_ADMIN'):
        raise HTTPException(status_code=403, detail="Only Owner can lock shifts")
    shift = db.query(DailyShift).filter(DailyShift.id == shift_id).first()
    if not shift:
        raise HTTPException(status_code=404, detail="Shift not found")
    shift.is_locked = True
    shift.updated_by = current_user.id
    from .audit import log_audit
    log_audit(db, current_user.id, 'UPDATE', 'daily_shifts', shift_id,
              f"Locked {shift.shift_type} shift on {shift.record_date}")
    db.commit()
    return {"message": "Shift locked", "shift_id": shift_id}


@router.get("/{shift_id}/handover", response_model=ShiftHandoverNoteOut)
def get_handover_note(
    shift_id: int,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    note = db.query(ShiftHandoverNote).filter(ShiftHandoverNote.shift_id == shift_id).first()
    if not note:
        raise HTTPException(status_code=404, detail="No handover note found for this shift")
    return note


@router.put("/{shift_id}/handover", response_model=ShiftHandoverNoteOut)
def upsert_handover_note(
    shift_id: int,
    body: ShiftHandoverNoteUpdate,
    db: Session = Depends(get_db),
    user=Depends(get_current_user),
):
    shift = db.query(DailyShift).filter(DailyShift.id == shift_id).first()
    if not shift:
        raise HTTPException(status_code=404, detail="Shift not found")
    note = db.query(ShiftHandoverNote).filter(ShiftHandoverNote.shift_id == shift_id).first()
    if note:
        for k, v in body.model_dump(exclude_none=True).items():
            setattr(note, k, v)
    else:
        note = ShiftHandoverNote(
            shift_id=shift_id,
            created_by=user.id,
            **body.model_dump(exclude_none=True),
        )
        db.add(note)
    db.commit()
    db.refresh(note)
    return note
