"""Golden file regression tests for gnssir, phase, and arc output.

Both expected (golden) and actual output are generated by
test/run_processing.sh before pytest runs. These tests just compare
the two directory trees.

After a test run, inspect results locally:

    diff -r test/data/expected/test_gnssir_output_unchanged \
           test/data/actual/test_gnssir_output_unchanged

To change the golden baseline, edit test/GOLDEN_COMMIT.
"""
from pathlib import Path

import numpy as np
import pytest

EXPECTED_DIR = Path(__file__).parent / "data" / "expected"
ACTUAL_DIR = Path(__file__).parent / "data" / "actual"

RESULTS = ("2025", "results", "mchl", "011.txt")
PHASE = ("2025", "phase", "mchl", "011.txt")
ARCS = ("2025", "arcs", "mchl", "011")


def _check_dirs_exist(test_name):
    """Skip test if output directories haven't been generated yet."""
    expected = EXPECTED_DIR / test_name
    actual = ACTUAL_DIR / test_name
    if not expected.exists():
        pytest.skip(f"Expected output not found: {expected}")
    if not actual.exists():
        pytest.skip(f"Actual output not found: {actual}")
    return actual, expected


def _compare_output(actual_path, expected_path):
    """Compare actual vs expected output files, fail with a summary if different."""
    actual = np.loadtxt(actual_path, comments="%")
    expected = np.loadtxt(expected_path, comments="%")
    if actual.shape[0] != expected.shape[0]:
        pytest.fail(f"Row count changed: expected {expected.shape[0]}, got {actual.shape[0]}")
    mask = ~np.isclose(actual, expected, rtol=1e-6).all(axis=1)
    n = mask.sum()
    if n > 0:
        pytest.fail(f"{n} of {len(expected)} rows differ")


def _compare_arcs(actual_dir, expected_dir):
    """Compare arc files between actual and expected directories."""
    actual_files = {f.name for f in actual_dir.glob("*.txt")}
    expected_files = {f.name for f in expected_dir.glob("*.txt")}

    missing = sorted(expected_files - actual_files)
    new = sorted(actual_files - expected_files)
    common = sorted(actual_files & expected_files)

    changed = []
    for name in common:
        act = np.loadtxt(actual_dir / name, comments="%")
        exp = np.loadtxt(expected_dir / name, comments="%")
        if act.shape != exp.shape or not np.allclose(act, exp, rtol=1e-6):
            col_names = ["elev", "dSNR", "sec"]
            bad_cols = []
            if act.shape == exp.shape:
                for ci, cn in enumerate(col_names):
                    if not np.allclose(act[:, ci], exp[:, ci], rtol=1e-6):
                        bad_cols.append(cn)
            else:
                bad_cols.append(f"shape {exp.shape}->{act.shape}")
            changed.append((name, bad_cols))

    n_diffs = len(missing) + len(new) + len(changed)
    if n_diffs == 0:
        return

    lines = [f"Arc output changed -- {n_diffs} arc difference{'s' if n_diffs != 1 else ''}:"]

    if missing:
        lines.append("")
        lines.append("MISSING arcs (expected but not produced):")
        for name in missing:
            lines.append(f"  {name}")

    if new:
        lines.append("")
        lines.append("NEW arcs (produced but not expected):")
        for name in new:
            lines.append(f"  {name}")

    if changed:
        lines.append("")
        lines.append("CHANGED arcs:")
        for name, bad_cols in changed:
            lines.append(f"  {name}  diverged in: {', '.join(bad_cols)}")

    pytest.fail("\n".join(lines))


def test_extract_arcs_output_unchanged():
    actual, expected = _check_dirs_exist("test_extract_arcs_output_unchanged")
    _compare_arcs(actual.joinpath(*ARCS), expected.joinpath(*ARCS))


def test_extract_arcs_from_file_output_unchanged():
    actual, expected = _check_dirs_exist("test_extract_arcs_from_file_output_unchanged")
    _compare_arcs(actual.joinpath(*ARCS), expected.joinpath(*ARCS))


def test_extract_arcs_from_station_output_unchanged():
    actual, expected = _check_dirs_exist("test_extract_arcs_from_station_output_unchanged")
    _compare_arcs(actual.joinpath(*ARCS), expected.joinpath(*ARCS))


def test_gnssir_output_unchanged():
    actual, expected = _check_dirs_exist("test_gnssir_output_unchanged")
    _compare_output(actual.joinpath(*RESULTS), expected.joinpath(*RESULTS))


def test_phase_output_unchanged():
    actual, expected = _check_dirs_exist("test_phase_output_unchanged")
    _compare_output(actual.joinpath(*PHASE), expected.joinpath(*PHASE))


def test_gnssir_arcs_unchanged():
    actual, expected = _check_dirs_exist("test_gnssir_arcs_unchanged")
    _compare_arcs(actual.joinpath(*ARCS), expected.joinpath(*ARCS))
