"""
Motor de alertas: detecta proximidad, movimiento continuo y proyección de trayectoria.
"""
import math
import uuid
from datetime import datetime
from collections import defaultdict, deque

def _alert_id() -> str:
    return str(uuid.uuid4())

# Pre-buffer por barco (últimas 60 posiciones = ~5 min a 5seg/update)
vessel_buffers = defaultdict(lambda: deque(maxlen=60))
active_recordings = {}  # mmsi -> {aid_id, inicio, min_dist}
aid_position_history = defaultdict(lambda: deque(maxlen=10))
aid_alert_state = {}   # aid_id -> 'RED' | 'YELLOW' | None (evita re-emitir)

def haversine(lat1, lon1, lat2, lon2):
    R = 6371000
    phi1, phi2 = math.radians(lat1), math.radians(lat2)
    dphi = math.radians(lat2 - lat1)
    dlambda = math.radians(lon2 - lon1)
    a = math.sin(dphi/2)**2 + math.cos(phi1)*math.cos(phi2)*math.sin(dlambda/2)**2
    return R * 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))

def project_position(lat, lon, cog_deg, sog_knots, minutes):
    speed_ms = sog_knots * 0.514444
    distance_m = speed_ms * minutes * 60
    cog_rad = math.radians(cog_deg)
    dlat = (distance_m * math.cos(cog_rad)) / 111320
    dlon = (distance_m * math.sin(cog_rad)) / (111320 * math.cos(math.radians(lat)))
    return lat + dlat, lon + dlon

def detect_continuous_movement(aid_id, lat, lon):
    history = aid_position_history[aid_id]
    history.append((lat, lon))
    if len(history) < 5:
        return False
    positions = list(history)
    # Verifica si cada posición es más lejana de la nominal que la anterior
    distances = []
    for i in range(1, len(positions)):
        d = haversine(positions[i-1][0], positions[i-1][1], positions[i][0], positions[i][1])
        distances.append(d)
    # Movimiento continuo: al menos 4 de 5 pasos se aleja progresivamente
    moving = sum(1 for d in distances if d > 0.5) >= 4
    return moving

def evaluate_vessel(vessel, aids, config):
    mmsi = vessel["mmsi"]
    lat, lon = vessel["lat"], vessel["lon"]
    sog = vessel.get("sog", 0)
    cog = vessel.get("cog", 0)

    vessel_buffers[mmsi].append({
        "lat": lat, "lon": lon, "sog": sog, "cog": cog,
        "timestamp": datetime.utcnow().isoformat()
    })

    alerts = []
    proximity_m   = config.get("proximity_alert_meters", 500)
    proj_minutes  = config.get("projection_minutes", 10)
    proj_radius   = config.get("projection_radius_meters", 800)
    record_trig_m = config.get("auto_record_trigger_m", proximity_m)

    for aid in aids:
        aid_lat = aid.get("lat_actual") or aid["lat_nominal"]
        aid_lon = aid.get("lon_actual") or aid["lon_nominal"]

        dist = haversine(lat, lon, aid_lat, aid_lon)
        recording_key = f"{mmsi}_{aid['id']}"

        # Proyección
        if sog > 0.5:
            proj_lat, proj_lon = project_position(lat, lon, cog, sog, proj_minutes)
            proj_dist = haversine(proj_lat, proj_lon, aid_lat, aid_lon)
        else:
            proj_dist = 9999

        # Auto-record uses its own (usually tighter) trigger distance,
        # while proximity alerts (yellow/red on the map) use proximity_m.
        triggered = dist <= record_trig_m or proj_dist <= proj_radius

        if triggered and recording_key not in active_recordings:
            active_recordings[recording_key] = {
                "mmsi": mmsi,
                "aid_id": aid["id"],
                "aid_nombre": aid["nombre"],
                "inicio": datetime.utcnow().isoformat(),
                "trigger": "PROXIMIDAD" if dist <= record_trig_m else "PROYECCION",
                "min_dist": dist,
                "pre_buffer": list(vessel_buffers[mmsi])
            }
            alerts.append({
                "id": _alert_id(),
                "tipo": "GRABACION_INICIADA",
                "mmsi": mmsi,
                "aid_id": aid["id"],
                "aid_nombre": aid["nombre"],
                "distancia_m": round(dist, 1),
                "trigger": active_recordings[recording_key]["trigger"],
                "timestamp": datetime.utcnow().isoformat()
            })

        elif recording_key in active_recordings:
            rec = active_recordings[recording_key]
            rec["min_dist"] = min(rec["min_dist"], dist)
            if dist > record_trig_m * 2 and proj_dist > proj_radius * 2:
                alerts.append({
                    "id": _alert_id(),
                    "tipo": "GRABACION_FINALIZADA",
                    "mmsi": mmsi,
                    "aid_id": aid["id"],
                    "aid_nombre": aid["nombre"],
                    "distancia_min_m": round(rec["min_dist"], 1),
                    "timestamp": datetime.utcnow().isoformat()
                })
                del active_recordings[recording_key]

    return alerts

def evaluate_aid_movement(aid_id, lat_actual, lon_actual, lat_nominal, lon_nominal,
                          config=None, warn_m=None, alarm_m=None):
    """
    warn_m / alarm_m: per-aid override from Aid.displacement_warn_m / alarm_m.
    If None, falls back to global config values.
    """
    config  = config or {}
    warn_m  = warn_m  if warn_m  is not None else config.get("displacement_warn_m",  10.0)
    alarm_m = alarm_m if alarm_m is not None else config.get("displacement_alarm_m", 15.0)
    desplazamiento = haversine(lat_actual, lon_actual, lat_nominal, lon_nominal)
    en_movimiento = detect_continuous_movement(aid_id, lat_actual, lon_actual)

    alerts = []
    prev = aid_alert_state.get(aid_id)

    if en_movimiento or desplazamiento > alarm_m:
        if prev != 'RED':          # solo emitir al entrar en estado rojo
            aid_alert_state[aid_id] = 'RED'
            alerts.append({
                "id": _alert_id(),
                "tipo": "ALERTA_ROJA",
                "subtipo": "AYUDA_EN_MOVIMIENTO" if en_movimiento else "AYUDA_FUERA_POSICION",
                "aid_id": aid_id,
                "desplazamiento_m": round(desplazamiento, 1),
                "umbral_m": alarm_m,
                "mensaje": "Movimiento continuo detectado" if en_movimiento
                           else f"Desplazamiento {desplazamiento:.1f} m supera alarma {alarm_m} m",
                "timestamp": datetime.utcnow().isoformat()
            })
    elif desplazamiento > warn_m:
        if prev != 'YELLOW':       # solo emitir al entrar en estado amarillo
            aid_alert_state[aid_id] = 'YELLOW'
            alerts.append({
                "id": _alert_id(),
                "tipo": "ALERTA_AMARILLA",
                "subtipo": "AYUDA_FUERA_POSICION",
                "aid_id": aid_id,
                "desplazamiento_m": round(desplazamiento, 1),
                "umbral_m": warn_m,
                "timestamp": datetime.utcnow().isoformat()
            })
    else:
        aid_alert_state[aid_id] = None   # condición resuelta

    return alerts
