"""
Track history endpoints — DVR replay for vessels and AtoNs.

GET  /tracks/vessels              → list MMSIs that have track data
GET  /tracks/vessels/{mmsi}       → track points for one vessel  (?from=&to=&limit=)
GET  /tracks/atons                → list AtoN MMSIs with track data
GET  /tracks/atons/{mmsi}         → track points for one AtoN    (?from=&to=&limit=)
GET  /recordings                  → list RecordingEvents          (?mmsi=&open=)
GET  /recordings/{event_id}/track → vessel track for that event window

Access control:
  - ADMIN / SUPERADMIN : see ALL recordings
  - USER with company  : see ONLY recordings where aid_id belongs to their company's buoys
  - Anonymous          : see ALL (legacy / unauthenticated clients)
"""

from fastapi import APIRouter, Depends, Query, HTTPException, Header
from sqlalchemy.orm import Session
from sqlalchemy import distinct
from typing import Optional
from datetime import datetime

from database import get_db
from models.vessel import VesselTrack, AtonTrack, RecordingEvent
from models.org import BuoyOwnership
from models.user import User, Role

router = APIRouter(tags=["tracks"])


def _optional_user(
    authorization: Optional[str] = Header(default=None),
    db: Session = Depends(get_db),
) -> Optional[User]:
    """Resolve JWT token from Authorization header if present. Returns None if absent/invalid."""
    if not authorization or not authorization.startswith("Bearer "):
        return None
    token = authorization.split(" ", 1)[1]
    try:
        from jose import jwt as _jwt, JWTError
        from routers.auth import SECRET_KEY, ALGORITHM
        payload  = _jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username = payload.get("sub")
        if not username:
            return None
        return db.query(User).filter(User.username == username, User.activo == True).first()
    except Exception:
        return None


def _company_aid_ids(company_id: str, db: Session) -> Optional[set]:
    """Return the set of aid_ids owned by this company, or None if unrestricted."""
    rows = db.query(BuoyOwnership).filter(
        BuoyOwnership.company_id == company_id,
        BuoyOwnership.aid_id.isnot(None),
    ).all()
    return {r.aid_id for r in rows} if rows is not None else set()


# ── Vessel tracks ─────────────────────────────────────────────────────────────

@router.get("/tracks/vessels")
def list_tracked_vessels(db: Session = Depends(get_db)):
    """Return distinct MMSIs that have at least one VesselTrack row."""
    rows = db.query(distinct(VesselTrack.mmsi)).all()
    return [r[0] for r in rows]


@router.get("/tracks/vessels/{mmsi}")
def get_vessel_track(
    mmsi: str,
    from_dt: Optional[str] = Query(None, alias="from"),
    to_dt:   Optional[str] = Query(None, alias="to"),
    limit:   int           = Query(10_000, le=100_000),
    db:      Session       = Depends(get_db),
):
    """
    Return time-ordered track points for *mmsi*.
    Optionally filter by ISO-8601 timestamps: ?from=2026-05-01T00:00:00&to=2026-05-02T00:00:00
    """
    q = db.query(VesselTrack).filter(VesselTrack.mmsi == mmsi)
    if from_dt:
        try:
            q = q.filter(VesselTrack.timestamp >= datetime.fromisoformat(from_dt))
        except ValueError:
            raise HTTPException(400, "Invalid 'from' datetime (use ISO-8601)")
    if to_dt:
        try:
            q = q.filter(VesselTrack.timestamp <= datetime.fromisoformat(to_dt))
        except ValueError:
            raise HTTPException(400, "Invalid 'to' datetime (use ISO-8601)")
    rows = q.order_by(VesselTrack.timestamp.asc()).limit(limit).all()
    return [
        {
            "mmsi":    r.mmsi,
            "ts":      r.timestamp.isoformat() if r.timestamp else None,
            "lat":     r.lat,
            "lon":     r.lon,
            "sog":     r.sog,
            "cog":     r.cog,
            "heading": r.heading,
        }
        for r in rows
    ]


# ── AtoN tracks ───────────────────────────────────────────────────────────────

@router.get("/tracks/atons")
def list_tracked_atons(db: Session = Depends(get_db)):
    """Return distinct MMSIs that have at least one AtonTrack row."""
    rows = db.query(distinct(AtonTrack.mmsi)).all()
    return [r[0] for r in rows]


@router.get("/tracks/atons/{mmsi}")
def get_aton_track(
    mmsi:    str,
    from_dt: Optional[str] = Query(None, alias="from"),
    to_dt:   Optional[str] = Query(None, alias="to"),
    limit:   int           = Query(10_000, le=100_000),
    db:      Session       = Depends(get_db),
):
    """Time-ordered position history for an AIS AtoN (Type 21)."""
    q = db.query(AtonTrack).filter(AtonTrack.mmsi == mmsi)
    if from_dt:
        try:
            q = q.filter(AtonTrack.timestamp >= datetime.fromisoformat(from_dt))
        except ValueError:
            raise HTTPException(400, "Invalid 'from' datetime")
    if to_dt:
        try:
            q = q.filter(AtonTrack.timestamp <= datetime.fromisoformat(to_dt))
        except ValueError:
            raise HTTPException(400, "Invalid 'to' datetime")
    rows = q.order_by(AtonTrack.timestamp.asc()).limit(limit).all()
    return [
        {
            "mmsi":         r.mmsi,
            "ts":           r.timestamp.isoformat() if r.timestamp else None,
            "lat":          r.lat,
            "lon":          r.lon,
            "voltage_v":    r.voltage_v,
            "off_position": r.off_position,
        }
        for r in rows
    ]


# ── Recording events ──────────────────────────────────────────────────────────

@router.get("/recordings")
def list_recordings(
    mmsi:    Optional[str]  = Query(None),
    open_:   Optional[bool] = Query(None, alias="open"),
    from_dt: Optional[str]  = Query(None, alias="from"),
    to_dt:   Optional[str]  = Query(None, alias="to"),
    limit:   int            = Query(200, le=2000),
    db:      Session        = Depends(get_db),
    caller:  Optional[User] = Depends(_optional_user),
):
    """
    List RecordingEvents (most-recent first).
    Supports ?mmsi=, ?open=true, ?from=YYYY-MM-DD, ?to=YYYY-MM-DD

    Access control:
      ADMIN/SUPERADMIN → all recordings
      USER with company → only recordings for their company's aids
      No token          → all recordings (legacy)
    """
    from models.vessel import Vessel
    from models.aid import Aid

    # ── Company filter for USER role ─────────────────────────────────────────
    allowed_aid_ids: Optional[set] = None
    if caller and caller.role == Role.USER:
        cid = getattr(caller, "company_id", None)
        if cid:
            allowed_aid_ids = _company_aid_ids(cid, db)

    q = db.query(RecordingEvent)

    # Restrict to owned aids if caller is a company user
    if allowed_aid_ids is not None:
        q = q.filter(RecordingEvent.aid_id.in_(allowed_aid_ids))

    if mmsi:
        q = q.filter(RecordingEvent.mmsi == mmsi)
    if open_ is not None:
        q = q.filter(RecordingEvent.cerrado == (not open_))
    if from_dt:
        try:
            q = q.filter(RecordingEvent.inicio >= datetime.fromisoformat(from_dt))
        except ValueError:
            pass
    if to_dt:
        try:
            # Include the full end date day
            to_end = datetime.fromisoformat(to_dt).replace(hour=23, minute=59, second=59)
            q = q.filter(RecordingEvent.inicio <= to_end)
        except ValueError:
            pass

    events = q.order_by(RecordingEvent.inicio.desc()).limit(limit).all()

    # Enrich with vessel / aid names in one pass
    mmsis   = {e.mmsi for e in events}
    aid_ids = {e.aid_id for e in events}
    vessels = {v.mmsi: v.nombre for v in db.query(Vessel).filter(Vessel.mmsi.in_(mmsis)).all()}
    aids    = {a.id:   a.nombre for a in db.query(Aid).filter(Aid.id.in_(aid_ids)).all()}

    return [
        {
            "id":              e.id,
            "mmsi":            e.mmsi,
            "vessel_nombre":   vessels.get(e.mmsi),
            "aid_id":          e.aid_id,
            "aid_nombre":      aids.get(e.aid_id),
            "inicio_utc":      e.inicio.isoformat() if e.inicio else None,
            "fin_utc":         e.fin.isoformat() if e.fin else None,
            "distancia_min_m": e.distancia_min_m,
            "trigger":         e.trigger,
            "cerrado":         e.cerrado,
        }
        for e in events
    ]


@router.get("/recordings/{event_id}/track")
def get_recording_track(event_id: str, db: Session = Depends(get_db)):
    """
    Return the VesselTrack points that fall within the time window of a
    specific RecordingEvent — ready for DVR replay.
    """
    event = db.query(RecordingEvent).filter(RecordingEvent.id == event_id).first()
    if not event:
        raise HTTPException(404, "Recording event not found")

    q = (
        db.query(VesselTrack)
        .filter(VesselTrack.mmsi == event.mmsi)
        .filter(VesselTrack.timestamp >= event.inicio)
    )
    if event.fin:
        q = q.filter(VesselTrack.timestamp <= event.fin)

    rows = q.order_by(VesselTrack.timestamp.asc()).all()
    return {
        "event": {
            "id":     event.id,
            "mmsi":   event.mmsi,
            "aid_id": event.aid_id,
            "inicio": event.inicio.isoformat(),
            "fin":    event.fin.isoformat() if event.fin else None,
            "trigger": event.trigger,
        },
        "track": [
            {
                "ts":      r.timestamp.isoformat() if r.timestamp else None,
                "lat":     r.lat,
                "lon":     r.lon,
                "sog":     r.sog,
                "cog":     r.cog,
                "heading": r.heading,
            }
            for r in rows
        ],
    }
