"""Unit-тесты ``_simplify_coords`` (ET-012, ADR-016).

Покрытие из 04-test-plan.yaml / 02-trz.md REQ-F-10:
  UT-SIMP-Z5-01 — прямая 100 точек на z=5 → ≤ 5 точек.
  UT-SIMP-Z5-02 — зигзаг 100 точек, амплитуда < tolerance → 2 точки.
  UT-SIMP-Z6-01 — зигзаг с амплитудой ~5 км на z=6 → > 5 точек.
  UT-SIMP-Z7-01 — регрессия: tolerance = 0.008.
  UT-SIMP-Z10-01 — регрессия: tolerance = 0.0005.
  UT-SIMP-Z12-01 — регрессия: без упрощения.
  UT-SIMP-EDGE-01 — < 3 точек возвращаются без изменений.
  UT-SIMP-EDGE-02 — DP схлопнул < 2 точек → возвращаем оригинал.

Замечание о масштабе: tolerance в градусах WGS84. На широте 55° с.ш.
1° долготы ≈ 64 км. Для зигзага амплитуда задаётся в градусах широты,
1° широты ≈ 111 км.
"""
from src.api.gps_tracks.mvt import _simplify_coords


# ─── UT-SIMP-Z5-01: прямая → ≤ 5 точек ──────────────────────────────────────

def test_ut_simp_z5_01_straight_line_collapses():
    """REQ-F-10 / UT-SIMP-Z5-01: 100 точек по прямой на z=5 → ≤ 5 точек.

    DP с большим tolerance схлопывает прямую до начала и конца.
    """
    # ~ 10 км по диагонали (шаг 0.001° × 100 = 0.1° ≈ 6.4 км по lon, 11 км по lat)
    coords = [(37.0 + i * 0.001, 55.0 + i * 0.001) for i in range(100)]
    result = _simplify_coords(coords, z=5)
    assert len(result) <= 5
    assert len(result) >= 2  # не схлопывается до 1 или 0
    # Концы сохранены
    assert result[0] == coords[0]
    assert result[-1] == coords[-1]


# ─── UT-SIMP-Z5-02: зигзаг с амплитудой < tolerance → 2 точки ───────────────

def test_ut_simp_z5_02_zigzag_below_tolerance_collapses_to_endpoints():
    """REQ-F-10 / UT-SIMP-Z5-02: зигзаг амплитудой ~0.01° (~1 км) на z=5.

    tolerance на z<=5 = 0.04° (~4 км по lon на 55° с.ш.), зигзаги
    меньше tolerance — схлопываются до концов.
    """
    coords = []
    base_lon = 37.0
    base_lat = 55.0
    for i in range(100):
        # Лонгитуда монотонно растёт, чтобы DP видел общее направление.
        # Латитуда зигзагит с амплитудой 0.01° (~1.1 км по широте).
        lon = base_lon + i * 0.002
        lat = base_lat + (0.01 if i % 2 else -0.01)
        coords.append((lon, lat))
    result = _simplify_coords(coords, z=5)
    # DP при таком tolerance оставит только начало и конец прямой.
    assert len(result) == 2
    assert result[0] == coords[0]
    assert result[-1] == coords[-1]


# ─── UT-SIMP-Z6-01: зигзаг 5 км на z=6 → видны крупные пики ─────────────────

def test_ut_simp_z6_01_large_zigzag_keeps_peaks():
    """REQ-F-10 / UT-SIMP-Z6-01: на z=6 (tolerance ~2 км) зигзаг 5 км
    оставляет крупные пики (> 5 точек).
    """
    coords = []
    base_lon = 37.0
    base_lat = 55.0
    for i in range(100):
        lon = base_lon + i * 0.005
        lat = base_lat + (0.05 if i % 2 else -0.05)
        coords.append((lon, lat))
    result = _simplify_coords(coords, z=6)
    assert len(result) > 5


# ─── UT-SIMP-Z7-01: регрессия — tolerance = 0.008 ───────────────────────────

def test_ut_simp_z7_01_regression_tolerance_unchanged():
    """REQ-F-10 / UT-SIMP-Z7-01: tolerance на z=7 = 0.008 (как до ET-012).

    Контроль: на синтетике зигзаг с амплитудой 0.01° (выше tolerance 0.008°)
    — пики сохраняются (>5 точек), но число меньше, чем на z=6 (tolerance меньше).
    """
    coords = []
    base_lon = 37.0
    base_lat = 55.0
    for i in range(100):
        lon = base_lon + i * 0.002
        lat = base_lat + (0.012 if i % 2 else -0.012)
        coords.append((lon, lat))

    result_z7 = _simplify_coords(coords, z=7)
    # Зигзаг чуть больше tolerance → точки сохраняются.
    assert len(result_z7) > 2
    # На z=10 с гораздо меньшим tolerance число сохранённых точек >= чем на z=7.
    result_z10 = _simplify_coords(coords, z=10)
    assert len(result_z10) >= len(result_z7)


# ─── UT-SIMP-Z10-01: регрессия — tolerance = 0.0005 ─────────────────────────

def test_ut_simp_z10_01_regression_fine_zigzag_kept():
    """REQ-F-10 / UT-SIMP-Z10-01: tolerance на z=10 = 0.0005 (как до ET-012).

    Зигзаг с амплитудой 0.001° (~100 м) — выше tolerance, точки сохраняются.
    """
    coords = []
    base_lon = 37.0
    base_lat = 55.0
    for i in range(100):
        lon = base_lon + i * 0.0005
        lat = base_lat + (0.001 if i % 2 else -0.001)
        coords.append((lon, lat))
    result = _simplify_coords(coords, z=10)
    # На z=10 с tolerance 0.0005° зигзаг 0.001° сохраняет почти все точки.
    assert len(result) >= 50


# ─── UT-SIMP-Z12-01: регрессия — без упрощения ──────────────────────────────

def test_ut_simp_z12_01_no_simplification():
    """REQ-F-10 / UT-SIMP-Z12-01: на z=12 функция возвращает coords без изменений."""
    coords = [(37.0 + i * 0.0001, 55.0 + i * 0.0001) for i in range(100)]
    result = _simplify_coords(coords, z=12)
    # Object identity preserved (return coords, не копия)
    assert result is coords


def test_ut_simp_z12_01_high_zoom_no_simplification():
    """REQ-F-10: на z>12 (например, z=15, z=22) — также без упрощения."""
    coords = [(37.0 + i * 0.0001, 55.0 + i * 0.0001) for i in range(50)]
    assert _simplify_coords(coords, z=15) is coords
    assert _simplify_coords(coords, z=22) is coords


# ─── UT-SIMP-EDGE-01: < 3 точек ─────────────────────────────────────────────

def test_ut_simp_edge_01_two_points_returned_as_is():
    """REQ-F-10 / UT-SIMP-EDGE-01: trace из 2 точек возвращается без изменений на любом z."""
    coords = [(37.0, 55.0), (37.001, 55.001)]
    for z in (5, 6, 7, 8, 10, 12):
        result = _simplify_coords(coords, z)
        assert result == coords


# ─── UT-SIMP-EDGE-02: вырожденный трек ──────────────────────────────────────

def test_ut_simp_edge_02_degenerate_track_falls_back_to_original():
    """REQ-F-10 / UT-SIMP-EDGE-02: 100 одинаковых точек.

    Shapely.simplify на дегенеративной геометрии может вернуть < 2 точек —
    функция должна fallback'нуть на оригинал, а не отдавать пустой список.
    """
    coords = [(37.0, 55.0)] * 100
    for z in (5, 6, 7, 8, 10):
        result = _simplify_coords(coords, z)
        # Минимум — оригинал, не пустой/одноточечный
        assert len(result) >= 2


# ─── Кросс-проверка: z=5 упрощает сильнее, чем z=6, чем z=7, чем z=10 ───────

def test_simp_tier_monotonic_for_complex_trace():
    """Дополнительная проверка монотонности tolerance по зумам.

    На сложном треке (100 точек со случайной вариативностью) ожидается:
      len(simp(z=5)) <= len(simp(z=6)) <= len(simp(z=7))
                     <= len(simp(z=10)) <= len(simp(z=12)) == 100
    """
    # Детерминированный pseudo-noise через index (без random — стабильно в CI)
    coords = []
    for i in range(100):
        lon = 37.0 + i * 0.003 + ((i * 7) % 13) * 0.0003
        lat = 55.0 + i * 0.002 + ((i * 11) % 17) * 0.0004
        coords.append((lon, lat))

    n5 = len(_simplify_coords(coords, z=5))
    n6 = len(_simplify_coords(coords, z=6))
    n7 = len(_simplify_coords(coords, z=7))
    n10 = len(_simplify_coords(coords, z=10))
    n12 = len(_simplify_coords(coords, z=12))

    assert n5 <= n6 <= n7 <= n10 <= n12
    assert n12 == 100  # без упрощения
