# SPDX-License-Identifier: GPL-3.0-or-later
"""Pure AIS ingest filters for the voyage-history recorder — keep decode glitches
and non-vessel stations out of the stored history. No Qt/QGIS deps (headless
testable).

Two gates, applied at the recording tap:
  * is_ship_mmsi   — record only real ships (MID leading digit 2-7). Base stations
    (00...), SART/MOB/EPIRB (97...), craft-associated (98...), AtoN (99...) and
    SAR aircraft (111...) are stations, not voyages, so they stay out of history.
  * is_teleport    — drop a fix that implies an impossible ship speed over a short
    interval: a single-message bit error (e.g. a flipped latitude bit) reads as a
    huge instantaneous jump. Region-agnostic — no hardcoded bounding box. A genuine
    coverage gap (long dt) is NOT gated, since a vessel can legitimately reappear
    far away after being out of range.
"""
import math

SHIP_MMSI_MIN = 200000000
SHIP_MMSI_MAX = 799999999
MAX_SHIP_KN = 80.0          # faster than any vessel -> the fix is a decode glitch
MAX_GATE_DT = 120.0         # only gate near-consecutive fixes (a real gap can jump)
MIN_GATE_NM = 0.05          # ignore sub-cable GPS jitter


def is_ship_mmsi(mmsi):
    """True only for a normal ship MMSI (leading digit 2-7)."""
    try:
        n = int(mmsi)
    except (TypeError, ValueError):
        return False
    return SHIP_MMSI_MIN <= n <= SHIP_MMSI_MAX


def plausible_position(lon, lat):
    """Basic coordinate sanity: in range and not null-island."""
    if lon is None or lat is None:
        return False
    if not (-180.0 <= lon <= 180.0) or not (-90.0 <= lat <= 90.0):
        return False
    return not (lon == 0.0 and lat == 0.0)


def _nm(lon1, lat1, lon2, lat2):
    """Great-circle distance in nautical miles."""
    r = 3440.065
    p1, p2 = math.radians(lat1), math.radians(lat2)
    dp = math.radians(lat2 - lat1)
    dl = math.radians(lon2 - lon1)
    a = math.sin(dp / 2) ** 2 + math.cos(p1) * math.cos(p2) * math.sin(dl / 2) ** 2
    return 2 * r * math.asin(min(1.0, math.sqrt(a)))


def is_teleport(prev, t, lon, lat, max_kn=MAX_SHIP_KN, max_dt=MAX_GATE_DT):
    """prev=(t, lon, lat) or None. True when the step from prev to now implies an
    impossible ship speed over a short interval (a decode glitch)."""
    if not prev:
        return False
    pt, plon, plat = prev
    dt = t - pt
    if dt <= 0 or dt > max_dt:
        return False
    dist = _nm(plon, plat, lon, lat)
    if dist < MIN_GATE_NM:
        return False
    return (dist / (dt / 3600.0)) > max_kn
