from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from database import get_db
from models.aid import Aid
from models.lamp import Lamp
from models.vessel import RecordingEvent
from models.user import User
from routers.auth import get_current_user, require_admin
from routers.lamps import compute_thresholds
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
import uuid

router = APIRouter(prefix="/aids", tags=["aids"])


def _aid_dict(aid: Aid, lamp: Lamp | None) -> dict:
    """Aid → JSON, with embedded lamp + computed battery thresholds."""
    base = {c.name: getattr(aid, c.name) for c in aid.__table__.columns}
    base["lamp"] = None
    base["battery_warn_v"]  = None
    base["battery_alarm_v"] = None
    if lamp:
        th = compute_thresholds(lamp.voltage_min, lamp.voltage_max)
        base["lamp"] = {
            "id": lamp.id, "manufacturer": lamp.manufacturer, "model": lamp.model,
            "lamp_count": lamp.lamp_count,
            "voltage_min": lamp.voltage_min, "voltage_max": lamp.voltage_max,
            **th,
        }
        base["battery_warn_v"]  = th["warn_v"]
        base["battery_alarm_v"] = th["alarm_v"]
    return base


def _lamp_for(aid: Aid, db: Session) -> Lamp | None:
    if not aid.lamp_id: return None
    return db.query(Lamp).filter(Lamp.id == aid.lamp_id).first()

class AidCreate(BaseModel):
    nombre: str
    numero_interno: Optional[str] = None
    categoria: str
    tipo: str
    tipo_ais: str = "SIN_AIS"
    lat_nominal: float
    lon_nominal: float
    fuente_posicion: str = "MANUAL"
    radio_borneo_m: float = 10.0
    mmsi: Optional[str] = None
    caracteristica_luz: Optional[str] = None
    alcance_nm: Optional[float] = None

class AidUpdate(BaseModel):
    lat_nominal: Optional[float] = None
    lon_nominal: Optional[float] = None
    puerto_responsable: Optional[str] = None
    empresa_responsable: Optional[str] = None
    caracteristica_luz: Optional[str] = None
    alcance_nm: Optional[float] = None
    radio_borneo_m: Optional[float] = None
    observaciones: Optional[str] = None
    lamp_id: Optional[str] = None
    # AIS link — operator-editable. Once set, AIS Type 21 with this MMSI
    # will update this aid's lat_actual and trigger drift/battery alerts.
    mmsi: Optional[str] = None
    tipo_ais: Optional[str] = None
    # Per-aid alarm thresholds (override global config when set)
    displacement_warn_m: Optional[float]  = None
    displacement_alarm_m: Optional[float] = None
    signal_loss_min: Optional[int]        = None
    din3_function: Optional[str] = None
    din4_function: Optional[str] = None
    # Chart link (set when aid was promoted from S-57 feature; usually
    # untouched after creation)
    source_chart: Optional[str] = None
    cell_id: Optional[str] = None
    chart_feature_id: Optional[str] = None
    motivo_cambio: str
    modificado_por: str


class AidFromChartFeature(BaseModel):
    """Payload to create an Aid record anchored to a specific S-57 chart feature."""
    cell_id: str
    chart_feature_id: str
    lat_nominal: float
    lon_nominal: float
    nombre: str
    tipo: str = "BOYA_ESPECIAL"          # caller can refine from S-57 type
    categoria: str = "FLOTANTE"
    radio_borneo_m: float = 10.0


class LampAssign(BaseModel):
    lamp_id: Optional[str] = None   # None → unassign


@router.get("/")
def list_aids(db: Session = Depends(get_db)):
    aids = db.query(Aid).filter(Aid.activa == True).all()
    return [_aid_dict(a, _lamp_for(a, db)) for a in aids]


# Static paths declared BEFORE /{aid_id} so FastAPI doesn't capture
# "by-chart-feature" as an aid_id.
@router.get("/by-chart-feature")
def aid_by_chart_feature(
    cell_id: str = Query(...),
    feature_id: str = Query(...),
    db: Session = Depends(get_db),
):
    """Return the Aid record linked to a specific S-57 chart feature, or 404."""
    aid = db.query(Aid).filter(
        Aid.cell_id == cell_id,
        Aid.chart_feature_id == feature_id,
        Aid.activa == True,
    ).first()
    if not aid:
        raise HTTPException(404, "No aid linked to this chart feature")
    return _aid_dict(aid, _lamp_for(aid, db))


@router.get("/{aid_id}")
def get_aid(aid_id: str, db: Session = Depends(get_db)):
    aid = db.query(Aid).filter(Aid.id == aid_id).first()
    if not aid:
        raise HTTPException(status_code=404, detail="Ayuda no encontrada")
    return _aid_dict(aid, _lamp_for(aid, db))


@router.patch("/{aid_id}/lamp")
def assign_lamp(aid_id: str, data: LampAssign, db: Session = Depends(get_db)):
    aid = db.query(Aid).filter(Aid.id == aid_id).first()
    if not aid:
        raise HTTPException(404, "Ayuda no encontrada")
    if data.lamp_id:
        lamp = db.query(Lamp).filter(Lamp.id == data.lamp_id).first()
        if not lamp:
            raise HTTPException(404, "Lamp not found")
    aid.lamp_id = data.lamp_id
    db.commit(); db.refresh(aid)
    return _aid_dict(aid, _lamp_for(aid, db))

@router.post("/")
def create_aid(data: AidCreate, db: Session = Depends(get_db)):
    aid = Aid(id=str(uuid.uuid4()), **data.model_dump())
    db.add(aid)
    db.commit()
    db.refresh(aid)
    return aid

@router.put("/{aid_id}")
def update_aid(aid_id: str, data: AidUpdate, db: Session = Depends(get_db),
               current_user: User = Depends(require_admin)):
    aid = db.query(Aid).filter(Aid.id == aid_id).first()
    if not aid:
        raise HTTPException(status_code=404, detail="Ayuda no encontrada")
    # Empty-string MMSI → unassign. Reject if MMSI is already used by another aid.
    if data.mmsi is not None:
        mmsi_clean = data.mmsi.strip() or None
        if mmsi_clean:
            taken = db.query(Aid).filter(
                Aid.mmsi == mmsi_clean, Aid.id != aid_id
            ).first()
            if taken:
                raise HTTPException(409,
                    f"MMSI {mmsi_clean} ya está asignado a '{taken.nombre}'")
        aid.mmsi = mmsi_clean
    for field in ["lat_nominal", "lon_nominal", "puerto_responsable",
                  "empresa_responsable", "caracteristica_luz", "alcance_nm",
                  "radio_borneo_m", "observaciones", "lamp_id",
                  "tipo_ais", "displacement_warn_m", "displacement_alarm_m",
                  "signal_loss_min", "din3_function", "din4_function",
                  "source_chart", "cell_id", "chart_feature_id"]:
        val = getattr(data, field)
        if val is not None:
            setattr(aid, field, val)
    aid.motivo_cambio = data.motivo_cambio
    aid.modificado_por = data.modificado_por
    aid.modificado_en = datetime.utcnow()
    db.commit()
    db.refresh(aid)
    return _aid_dict(aid, _lamp_for(aid, db))


@router.post("/from-chart-feature")
def create_aid_from_chart_feature(
    data: AidFromChartFeature,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin),
):
    """Promote a S-57 chart feature into a monitored Aid record.
    Idempotent: if an aid already exists for (cell_id, chart_feature_id),
    returns it instead of creating a duplicate."""
    existing = db.query(Aid).filter(
        Aid.cell_id == data.cell_id,
        Aid.chart_feature_id == data.chart_feature_id,
        Aid.activa == True,
    ).first()
    if existing:
        return _aid_dict(existing, _lamp_for(existing, db))

    aid = Aid(
        id=str(uuid.uuid4()),
        nombre=data.nombre,
        categoria=data.categoria,
        tipo=data.tipo,
        tipo_ais="SIN_AIS",                     # operator can change later
        lat_nominal=data.lat_nominal,
        lon_nominal=data.lon_nominal,
        fuente_posicion="S57",
        radio_borneo_m=data.radio_borneo_m,
        source_chart="S57",
        cell_id=data.cell_id,
        chart_feature_id=data.chart_feature_id,
        modificado_por=current_user.nombre if current_user else None,
        motivo_cambio="Promoted from S-57 chart feature",
    )
    db.add(aid)
    db.commit()
    db.refresh(aid)
    return _aid_dict(aid, _lamp_for(aid, db))

@router.get("/recordings", tags=["recordings"])
def list_recordings(
    db:    Session = Depends(get_db),
    from_: str     = Query(None, alias="from"),
    to:    str     = Query(None),
    mmsi:  str     = Query(None),
):
    q = db.query(RecordingEvent)
    if mmsi:
        q = q.filter(RecordingEvent.mmsi.ilike(f"%{mmsi}%"))
    if from_:
        try:
            q = q.filter(RecordingEvent.inicio >= datetime.fromisoformat(from_))
        except Exception:
            pass
    if to:
        try:
            q = q.filter(RecordingEvent.inicio <= datetime.fromisoformat(to + "T23:59:59"))
        except Exception:
            pass
    recs = q.order_by(RecordingEvent.inicio.desc()).limit(200).all()
    return [
        {
            "id":            r.id,
            "mmsi":          r.mmsi,
            "vessel_nombre": r.mmsi,
            "aid_id":        r.aid_id,
            "aid_nombre":    r.aid_id,
            "inicio_utc":    r.inicio.isoformat() if r.inicio else None,
            "fin_utc":       r.fin.isoformat()    if r.fin    else None,
            "distancia_min_m": round(r.distancia_min_m, 1) if r.distancia_min_m else None,
            "trigger":       r.trigger,
            "cerrado":       r.cerrado,
        }
        for r in recs
    ]
