from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from sqlalchemy import and_
from typing import List
from datetime import date, timedelta
from typing import Optional
from decimal import Decimal
from pydantic import BaseModel
from ..database import get_db
from ..models import Pump, FuelRate, PumpShiftConfig, ShiftTemplate
from ..schemas import PumpOut, FuelRateOut, PumpCreate, PumpUpdate, PumpStatusBody
from ..dependencies import get_current_user, require_owner
from ..config import SHIFT_PUMP_MAP, SHIFT_LABELS

VALID_FUEL_TYPES = ('LP92', 'EURO3', 'LAD', 'LADXM')
VALID_STATUSES   = ('ACTIVE', 'MAINTENANCE', 'INACTIVE')

class FuelRateSet(BaseModel):
    rate: Decimal
    effective_from: Optional[date] = None   # defaults to today (midnight)

router = APIRouter()


@router.get("/pumps", response_model=List[PumpOut])
def list_pumps(
    include_all: bool = False,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    q = db.query(Pump)
    if not include_all:
        q = q.filter(Pump.is_active == True)
    return q.order_by(Pump.sort_order, Pump.pump_number, Pump.pump_code).all()


@router.post("/pumps", response_model=PumpOut)
def create_pump(body: PumpCreate, db: Session = Depends(get_db), user=Depends(get_current_user)):
    if user.role not in ('OWNER', 'SUPER_ADMIN'):
        raise HTTPException(403, "Owner access required")
    fuel = body.fuel_type.upper()
    if fuel not in VALID_FUEL_TYPES:
        raise HTTPException(400, f"Invalid fuel type. Use: {', '.join(VALID_FUEL_TYPES)}")
    if db.query(Pump).filter(Pump.pump_code == body.pump_code.upper()).first():
        raise HTTPException(409, f"Pump code '{body.pump_code}' already exists")
    pump = Pump(
        pump_code=body.pump_code.upper(),
        pump_number=body.pump_number,
        fuel_type=fuel,
        description=body.description,
        sort_order=body.sort_order,
        is_active=True,
        status='ACTIVE',
    )
    db.add(pump)
    db.flush()
    # Auto-create pump-shift configs for all active shift templates
    from ..models import ShiftTemplate, PumpShiftConfig
    templates = db.query(ShiftTemplate).filter(ShiftTemplate.is_active == True).all()
    for t in templates:
        db.add(PumpShiftConfig(pump_id=pump.id, shift_template_id=t.id, sort_order=t.sort_order))
    from .audit import log_audit
    log_audit(db, user.id, 'CREATE', 'pumps', pump.id, f"Created pump {pump.pump_code} ({fuel})")
    db.commit()
    db.refresh(pump)
    return pump


@router.put("/pumps/{pump_id}", response_model=PumpOut)
def update_pump(pump_id: int, body: PumpUpdate, db: Session = Depends(get_db), user=Depends(get_current_user)):
    if user.role not in ('OWNER', 'SUPER_ADMIN'):
        raise HTTPException(403, "Owner access required")
    pump = db.query(Pump).filter(Pump.id == pump_id).first()
    if not pump:
        raise HTTPException(404, "Pump not found")
    if body.pump_code is not None:
        code = body.pump_code.upper()
        clash = db.query(Pump).filter(Pump.pump_code == code, Pump.id != pump_id).first()
        if clash:
            raise HTTPException(409, f"Pump code '{code}' already in use")
        pump.pump_code = code
    if body.pump_number is not None:
        pump.pump_number = body.pump_number
    if body.fuel_type is not None:
        fuel = body.fuel_type.upper()
        if fuel not in VALID_FUEL_TYPES:
            raise HTTPException(400, f"Invalid fuel type. Use: {', '.join(VALID_FUEL_TYPES)}")
        pump.fuel_type = fuel
    if body.description is not None:
        pump.description = body.description
    if body.sort_order is not None:
        pump.sort_order = body.sort_order
    from .audit import log_audit
    log_audit(db, user.id, 'UPDATE', 'pumps', pump_id, f"Updated pump {pump.pump_code}")
    db.commit()
    db.refresh(pump)
    return pump


@router.put("/pumps/{pump_id}/status", response_model=PumpOut)
def set_pump_status(pump_id: int, body: PumpStatusBody, db: Session = Depends(get_db), user=Depends(get_current_user)):
    if user.role not in ('OWNER', 'SUPER_ADMIN'):
        raise HTTPException(403, "Owner access required")
    status = body.status.upper()
    if status not in VALID_STATUSES:
        raise HTTPException(400, f"Invalid status. Use: {', '.join(VALID_STATUSES)}")
    pump = db.query(Pump).filter(Pump.id == pump_id).first()
    if not pump:
        raise HTTPException(404, "Pump not found")
    pump.status = status
    pump.is_active = (status == 'ACTIVE')
    pump.maintenance_reason = body.maintenance_reason if status == 'MAINTENANCE' else None
    from .audit import log_audit
    reason_str = f" — {body.maintenance_reason}" if body.maintenance_reason else ""
    log_audit(db, user.id, 'UPDATE', 'pumps', pump_id,
              f"Pump {pump.pump_code} status → {status}{reason_str}")
    db.commit()
    db.refresh(pump)
    return pump


@router.delete("/pumps/{pump_id}", status_code=204)
def delete_pump(pump_id: int, db: Session = Depends(get_db), user=Depends(get_current_user)):
    if user.role not in ('OWNER', 'SUPER_ADMIN'):
        raise HTTPException(403, "Owner access required")
    pump = db.query(Pump).filter(Pump.id == pump_id).first()
    if not pump:
        raise HTTPException(404, "Pump not found")
    has_readings = db.query(pump.__class__).join(pump.__class__.readings).filter(
        pump.__class__.id == pump_id
    ).first()
    # Check if pump has any readings — if yes, deactivate only (preserve history)
    from ..models import PumpReading
    readings_count = db.query(PumpReading).filter(PumpReading.pump_id == pump_id).count()
    if readings_count > 0:
        pump.status = 'INACTIVE'
        pump.is_active = False
        from .audit import log_audit
        log_audit(db, user.id, 'UPDATE', 'pumps', pump_id,
                  f"Deactivated pump {pump.pump_code} (has {readings_count} readings — history preserved)")
        db.commit()
    else:
        from .audit import log_audit
        log_audit(db, user.id, 'DELETE', 'pumps', pump_id, f"Deleted pump {pump.pump_code} (no readings)")
        db.delete(pump)
        db.commit()


@router.get("/pumps/by-shift/{shift_type}")
def pumps_for_shift(
    shift_type: str,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    shift_type = shift_type.upper()

    # Try pump_shift_configs first (DB-driven, owner-configurable)
    tmpl = db.query(ShiftTemplate).filter(ShiftTemplate.code == shift_type).first()
    if tmpl:
        configs = (
            db.query(PumpShiftConfig)
            .filter(
                PumpShiftConfig.shift_template_id == tmpl.id,
                PumpShiftConfig.is_active == True,
            )
            .order_by(PumpShiftConfig.sort_order)
            .all()
        )
        if configs:
            pump_ids = [c.pump_id for c in configs]
            pumps = db.query(Pump).filter(Pump.id.in_(pump_ids), Pump.status == 'ACTIVE').all()
            pump_map = {p.id: p for p in pumps}
            return [PumpOut.model_validate(pump_map[pid]) for pid in pump_ids if pid in pump_map]

    # Fallback: system_settings or hardcoded config
    from ..models import SystemSetting
    setting = db.query(SystemSetting).filter(
        SystemSetting.setting_key == f'shift.{shift_type}.pumps'
    ).first()
    codes = (
        [c.strip() for c in setting.setting_value.split(',') if c.strip()]
        if setting and setting.setting_value.strip()
        else SHIFT_PUMP_MAP.get(shift_type, [])
    )
    pumps = db.query(Pump).filter(Pump.pump_code.in_(codes)).all()
    pump_map = {p.pump_code: p for p in pumps}
    return [PumpOut.model_validate(pump_map[c]) for c in codes if c in pump_map]


@router.get("/fuel-rates/current", response_model=List[FuelRateOut])
def current_fuel_rates(db: Session = Depends(get_db), _=Depends(get_current_user)):
    today = date.today()
    rates = (
        db.query(FuelRate)
        .filter(
            FuelRate.effective_from <= today,
            (FuelRate.effective_to == None) | (FuelRate.effective_to >= today),
        )
        .all()
    )
    # One rate per fuel type (latest)
    seen = {}
    for r in sorted(rates, key=lambda x: x.effective_from, reverse=True):
        if r.fuel_type not in seen:
            seen[r.fuel_type] = r
    return list(seen.values())


@router.get("/fuel-rates/on-date", response_model=List[FuelRateOut])
def fuel_rates_on_date(
    on_date: Optional[date] = None,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    """Return the effective rate per fuel type for a given date (defaults to today)."""
    target = on_date or date.today()
    rates = (
        db.query(FuelRate)
        .filter(
            FuelRate.effective_from <= target,
            (FuelRate.effective_to == None) | (FuelRate.effective_to >= target),
        )
        .all()
    )
    seen: dict = {}
    for r in sorted(rates, key=lambda x: x.effective_from, reverse=True):
        if r.fuel_type not in seen:
            seen[r.fuel_type] = r
    return list(seen.values())


@router.get("/fuel-rates/history", response_model=List[FuelRateOut])
def fuel_rate_history(
    fuel_type: Optional[str] = None,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    """Return all historical rates, optionally filtered by fuel type."""
    q = db.query(FuelRate)
    if fuel_type:
        q = q.filter(FuelRate.fuel_type == fuel_type.upper())
    return q.order_by(FuelRate.fuel_type, FuelRate.effective_from.desc()).all()


@router.delete("/fuel-rates/id/{rate_id}", status_code=204)
def cancel_scheduled_rate(
    rate_id: int,
    db: Session = Depends(get_db),
    user=Depends(get_current_user),
):
    """Cancel a future-dated scheduled rate. Re-opens the preceding rate."""
    if user.role not in ('OWNER', 'SUPER_ADMIN'):
        raise HTTPException(403, "Not authorised")
    rate = db.query(FuelRate).filter(FuelRate.id == rate_id).first()
    if not rate:
        raise HTTPException(404, "Rate not found")
    if rate.effective_from <= date.today():
        raise HTTPException(400, "Cannot cancel an already-active or past rate")

    # Re-open the preceding rate that was closed when this was scheduled
    close_date = rate.effective_from - timedelta(days=1)
    prev = (
        db.query(FuelRate)
        .filter(
            FuelRate.fuel_type == rate.fuel_type,
            FuelRate.effective_to == close_date,
            FuelRate.effective_from < rate.effective_from,
        )
        .order_by(FuelRate.effective_from.desc())
        .first()
    )
    if prev:
        prev.effective_to = None

    from .audit import log_audit
    log_audit(db, user.id, 'DELETE', 'fuel_rates', rate_id,
              f"Cancelled scheduled {rate.fuel_type} rate Rs {rate.rate} from {rate.effective_from}")
    db.delete(rate)
    db.commit()


@router.put("/fuel-rates/{fuel_type}", response_model=FuelRateOut)
def set_fuel_rate(
    fuel_type: str,
    body: FuelRateSet,
    db: Session = Depends(get_db),
    user=Depends(get_current_user),
):
    if user.role not in ('OWNER', 'SUPER_ADMIN'):
        raise HTTPException(403, "Not authorised")
    """
    Set a new rate for a fuel type effective from midnight of the given date.
    Closes any overlapping open rates by setting their effective_to to
    (effective_from - 1 day).
    """
    fuel_type = fuel_type.upper()
    if fuel_type not in VALID_FUEL_TYPES:
        raise HTTPException(status_code=400, detail=f"Unknown fuel type: {fuel_type}")

    eff_from = body.effective_from or date.today()
    close_date = eff_from - timedelta(days=1)

    # Close any rate that is currently open and would overlap
    db.query(FuelRate).filter(
        FuelRate.fuel_type == fuel_type,
        FuelRate.effective_from < eff_from,
        FuelRate.effective_to == None,
    ).update({"effective_to": close_date})

    # If a rate already exists starting on this exact date, update it in-place
    existing = db.query(FuelRate).filter(
        FuelRate.fuel_type == fuel_type,
        FuelRate.effective_from == eff_from,
    ).first()

    from .audit import log_audit
    if existing:
        existing.rate   = body.rate
        existing.set_by = user.id
        log_audit(db, user.id, 'UPDATE', 'fuel_rates', existing.id,
                  f"Updated {fuel_type} rate to Rs {body.rate} from {eff_from}")
        db.commit()
        db.refresh(existing)
        return existing

    new_rate = FuelRate(
        fuel_type=fuel_type,
        rate=body.rate,
        effective_from=eff_from,
        set_by=user.id,
    )
    db.add(new_rate)
    db.flush()
    log_audit(db, user.id, 'CREATE', 'fuel_rates', new_rate.id,
              f"Set {fuel_type} rate to Rs {body.rate} from {eff_from}")
    db.commit()
    db.refresh(new_rate)
    return new_rate


@router.get("/shift-types")
def shift_types(_=Depends(get_current_user)):
    return [
        {"code": k, "label": v, "pumps": SHIFT_PUMP_MAP[k]}
        for k, v in SHIFT_LABELS.items()
    ]


@router.get("/shift-periods")
def shift_periods(_=Depends(get_current_user)):
    """Return operational shift periods with pumper counts."""
    from ..config import SHIFT_PERIODS
    return SHIFT_PERIODS


@router.get("/pump-groups")
def pump_groups(db: Session = Depends(get_db), _=Depends(get_current_user)):
    """Return physical pumps with their nozzles — built dynamically from DB."""
    pumps = (
        db.query(Pump)
        .order_by(Pump.pump_number, Pump.sort_order, Pump.pump_code)
        .all()
    )
    groups: dict[int, dict] = {}
    for p in pumps:
        num = p.pump_number
        if num not in groups:
            groups[num] = {"pump_number": num, "label": f"Pump {num}", "nozzles": []}
        groups[num]["nozzles"].append({
            "id":          p.id,
            "pump_code":   p.pump_code,
            "fuel_type":   p.fuel_type,
            "status":      p.status,
            "description": p.description,
        })
    return sorted(groups.values(), key=lambda g: g["pump_number"])
