"""Pure-logic tests for segment_gsys (no network): at-sea trim, figure-8 detection, windowing."""
import math
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

from segment_gsys import (  # noqa: E402
    at_sea_span,
    bbox_diag_m,
    is_figure8,
    segment_windows,
    track_self_crosses,
    track_turning,
)


def _fig8(R=30.0, n=240):
    """A clean figure-eight: top loop CCW + bottom loop CW, crossing at the origin."""
    pts, half = [], n // 2
    for i in range(half):                                   # top loop, CCW, starts at origin
        a = -math.pi / 2 + 2 * math.pi * i / half
        pts.append((R * math.cos(a), R + R * math.sin(a)))
    for i in range(half):                                   # bottom loop, CW, back through origin
        a = math.pi / 2 - 2 * math.pi * i / half
        pts.append((R * math.cos(a), -R + R * math.sin(a)))
    return pts


def _windward_leeward(h=60.0, w=6.0, n=240):
    """Up one side, across, down a parallel lane — a thin rectangular loop, NO self-crossing."""
    q, pts = n // 4, []
    for i in range(q):
        pts.append((0.0, h * i / q))
    for i in range(q):
        pts.append((w * i / q, h))
    for i in range(q):
        pts.append((w, h * (1 - i / q)))
    for i in range(q):
        pts.append((w * (1 - i / q), 0.0))
    return pts


def test_figure8_detected():
    xy = _fig8()
    total, flips = track_turning(xy)
    assert total >= 540.0, total
    assert flips >= 1, flips
    assert track_self_crosses(xy)
    assert is_figure8(xy)


def test_windward_leeward_rejected():
    xy = _windward_leeward()
    assert not track_self_crosses(xy)
    assert not is_figure8(xy)


def test_bbox_diag():
    assert bbox_diag_m([(0, 0), (3, 4)]) == 5.0
    assert bbox_diag_m([]) == 0.0


def test_at_sea_trim_drops_idle_ends():
    # 30 s idle (speed 0) on the dock, 60 s sailing, 30 s idle after haul-out
    pts = []
    t = 0
    for _ in range(30):
        pts.append({"t": t, "speed": 0.0})
        t += 1000
    for _ in range(60):
        pts.append({"t": t, "speed": 3.0})
        t += 1000
    for _ in range(30):
        pts.append({"t": t, "speed": 0.0})
        t += 1000
    sea = at_sea_span(pts, speed_thresh=0.8)
    assert len(sea) == 60
    assert sea[0]["t"] == 30_000
    assert sea[-1]["t"] == 89_000


def test_at_sea_keeps_longest_run_across_short_drift():
    # two sailing runs separated by a 2-min drift (< idle_gap_s=600) stay joined
    pts, t = [], 0
    for _ in range(20):
        pts.append({"t": t, "speed": 3.0})
        t += 1000
    for _ in range(120):
        pts.append({"t": t, "speed": 0.0})
        t += 1000          # 2-min drift
    for _ in range(20):
        pts.append({"t": t, "speed": 3.0})
        t += 1000
    sea = at_sea_span(pts, speed_thresh=0.8, idle_gap_s=600)
    assert sea[0]["t"] == 0 and sea[-1]["t"] == 159_000


def test_segment_windows_splits_on_gap():
    # 4 boats sail two flights 10 min apart (gap > idle_gap_s)
    def flight(t0, secs):
        return [{"t": (t0 + s) * 1000, "speed": 3.0} for s in range(secs)]
    devs = {f"d{i}": flight(0, 120) + flight(900, 120) for i in range(4)}
    wins = segment_windows(devs, min_boats=4, idle_gap_s=300)
    assert len(wins) == 2
    assert wins[0][2] == ["d0", "d1", "d2", "d3"]


def test_confirm_requires_two_figure8_boats():
    ref = (59.32, 18.107)
    # build 4 boats: 2 sailing figure-8s, 2 drifting in a tiny box → confirmed, roster = the movers
    xy8 = _fig8()
    import segment_gsys as sg
    # invert the XY→latlon so we can feed dev_points in lat/lon (confirm re-projects with `ref`)
    def to_ll(x, y):
        return (ref[0] + y / 111_320.0, ref[1] + x / (111_320.0 * math.cos(math.radians(ref[0]))))
    dev_points = {}
    for i in range(2):
        dev_points[f"sail{i}"] = [{"t": s * 1000, "lat": to_ll(*xy8[s])[0], "lon": to_ll(*xy8[s])[1],
                                   "speed": 3.0} for s in range(len(xy8))]
    for i in range(2, 4):
        dev_points[f"idle{i}"] = [{"t": s * 1000, "lat": ref[0] + 1e-5 * (s % 2),
                                   "lon": ref[1], "speed": 0.1} for s in range(len(xy8))]
    window = (0, (len(xy8) - 1) * 1000, list(dev_points))
    cand = sg.confirm_and_describe(window, dev_points, ref, top_n=4)
    assert cand is not None
    assert set(cand.devices[:2]) == {"sail0", "sail1"}
