"""ET-008 — тесты модуля публичных GPS-треков (gps_tracks.js + endpoint).

Изменение ET-008 — модуль `src/web/gps_tracks.js` + FastAPI endpoint
`/api/gps-tracks`. В CI исполняется только `pytest tests/`, поэтому файл
покрывает фронтендовую часть двумя способами:

1. Статические проверки структуры gps_tracks.js — выполняются всегда.
2. Поведенческие JS unit-тесты — через встроенный тест-раннер Node
   (`node --test`). Если `node` отсутствует — часть помечается `skip`.

API-тесты endpoint живут в tests/api/test_gps_tracks_endpoint.py.

Запуск JS-тестов напрямую:
    node --test tests/web/gps_tracks.test.js
"""

from __future__ import annotations

import subprocess
from pathlib import Path
from shutil import which

import pytest

REPO_ROOT = Path(__file__).resolve().parents[2]
GPS_TRACKS_JS = REPO_ROOT / "src" / "web" / "gps_tracks.js"
JS_TEST = REPO_ROOT / "tests" / "web" / "gps_tracks.test.js"


def _read(path: Path) -> str:
    assert path.is_file(), f"не найден {path}"
    return path.read_text(encoding="utf-8")


# ──────────────────────────────────────────────────────────────────────────────
# Статические проверки gps_tracks.js
# ──────────────────────────────────────────────────────────────────────────────

def test_gps_tracks_module_exists():
    """Модуль src/web/gps_tracks.js присутствует в репозитории."""
    assert GPS_TRACKS_JS.is_file(), "не найден src/web/gps_tracks.js"


def test_gps_tracks_find_insert_position_defined():
    """_findGpsInsertPosition() объявлена в модуле."""
    js = _read(GPS_TRACKS_JS)
    assert "function _findGpsInsertPosition(" in js, (
        "_findGpsInsertPosition не объявлена в gps_tracks.js"
    )


def test_gps_tracks_find_insert_position_priority_gpx_first():
    """F-05: поиск gpx-layer-* идёт до route-* (приоритет 1 > приоритет 2)."""
    js = _read(GPS_TRACKS_JS)
    fn_start = js.index("function _findGpsInsertPosition(")
    fn_end = js.index("\n}", fn_start)
    fn_body = js[fn_start:fn_end]

    gpx_pos = fn_body.find("gpx-layer-")
    route_pos = fn_body.find("route-")
    assert gpx_pos != -1, "gpx-layer- не найден в _findGpsInsertPosition"
    assert route_pos != -1, "route- не найден в _findGpsInsertPosition"
    assert gpx_pos < route_pos, (
        "gpx-layer-* должен проверяться ДО route-* (приоритет 1 > приоритет 2)"
    )


def test_gps_tracks_find_insert_position_no_exact_route_line():
    """F-05: старый точный match 'route-line' удалён, используется startsWith."""
    js = _read(GPS_TRACKS_JS)
    fn_start = js.index("function _findGpsInsertPosition(")
    fn_end = js.index("\n}", fn_start)
    fn_body = js[fn_start:fn_end]
    assert "l.id === 'route-line'" not in fn_body, (
        "старый точный матч route-line не должен присутствовать (F-05)"
    )


def test_gps_tracks_state_object_defined():
    """window.gpsTracksLayer инициализируется в модуле."""
    js = _read(GPS_TRACKS_JS)
    assert "window.gpsTracksLayer = {" in js, (
        "gps_tracks.js не инициализирует window.gpsTracksLayer"
    )


def test_gps_tracks_source_colors_defined():
    """GPS_SOURCE_COLORS объявлен для всех основных источников."""
    js = _read(GPS_TRACKS_JS)
    for src in ("osm", "enduro_russia", "ttrails", "offmaps", "nakarte"):
        assert src in js, f"GPS_SOURCE_COLORS не содержит ключ {src}"


def test_gps_tracks_activity_colors_defined():
    """GPS_ACTIVITY_COLORS объявлен для всех 7 типов активности."""
    js = _read(GPS_TRACKS_JS)
    for act in ("enduro", "moto", "offroad", "bicycle", "hike", "ski", "other"):
        assert act in js, f"GPS_ACTIVITY_COLORS не содержит ключ {act}"


def test_gps_tracks_build_color_expression_defined():
    """_buildColorExpression() объявлена в модуле."""
    js = _read(GPS_TRACKS_JS)
    assert "function _buildColorExpression(" in js, (
        "_buildColorExpression не объявлена в gps_tracks.js"
    )


# ──────────────────────────────────────────────────────────────────────────────
# Поведенческие JS unit-тесты через Node
# ──────────────────────────────────────────────────────────────────────────────

node_required = pytest.mark.skipif(
    which("node") is None,
    reason="node не установлен — поведенческие JS unit-тесты пропущены",
)


@node_required
def test_js_unit_tests_pass():
    """F-05 + filters + colors: behavioral JS-тесты gps_tracks.js через `node --test`."""
    assert JS_TEST.is_file(), f"не найден JS-тест {JS_TEST}"
    node = which("node")
    result = subprocess.run(
        [node, "--test", str(JS_TEST)],
        capture_output=True,
        text=True,
        cwd=str(REPO_ROOT),
    )
    assert result.returncode == 0, (
        f"JS unit-тесты GPS-треков упали (код {result.returncode}):\n"
        f"{result.stdout}\n{result.stderr}"
    )
