"""Unit tests for `_align_detected_to_expected` — AMEND_SPEC_01 Phase 1.

Covers the full spectrum of alignment scenarios: exact match, under-detection,
over-detection (fully and partially aligned), zero-expected (single-segment
pass), empty detected, clustered spurious cuts, tie-break determinism, and
tolerance boundary behaviour.
"""

import sys
from pathlib import Path

# Path setup so these tests can import workspace modules.
_RECOIL_ROOT = Path(__file__).resolve().parent.parent
_PIPELINE_ROOT = _RECOIL_ROOT / "pipeline"
for _p in [str(_RECOIL_ROOT), str(_PIPELINE_ROOT)]:
    if _p not in sys.path:
        sys.path.insert(0, _p)

from recoil.workspace.server import _align_detected_to_expected


def test_exact_match():
    """Detected cuts land exactly on expected boundaries."""
    result = _align_detected_to_expected(
        detected=[3.0, 8.0],
        intended_durations=[3.0, 5.0, 2.0],
        pass_duration=10.0,
    )
    assert result["aligned_cuts"] == [3.0, 8.0]
    assert result["model_added_cuts"] == []
    assert result["alignment_score"] == 1.0
    assert result["missed_boundaries"] == []


def test_under_detection_partial():
    """Fewer detected than expected — partial alignment, score < 1.0."""
    result = _align_detected_to_expected(
        detected=[3.0],
        intended_durations=[3.0, 5.0, 2.0],
        pass_duration=10.0,
    )
    assert result["aligned_cuts"] == [3.0]
    assert result["missed_boundaries"] == [8.0]
    assert result["model_added_cuts"] == []
    assert result["alignment_score"] == 0.5


def test_over_detection_aligned():
    """More detected than expected — extras are preserved as model-added."""
    result = _align_detected_to_expected(
        detected=[3.0, 5.5, 8.0],
        intended_durations=[3.0, 5.0, 2.0],
        pass_duration=10.0,
    )
    assert result["aligned_cuts"] == [3.0, 8.0]
    assert result["model_added_cuts"] == [5.5]
    assert result["alignment_score"] == 1.0
    assert result["missed_boundaries"] == []


def test_over_detection_partial_aligned():
    """Over-detected but one expected boundary is out of tolerance."""
    result = _align_detected_to_expected(
        detected=[3.0, 5.5, 9.0],
        intended_durations=[3.0, 5.0, 2.0],
        pass_duration=10.0,
    )
    # expected_ts = [3.0, 8.0]. 9.0 is 1.0 away from 8.0 (> 0.5) → missed.
    assert result["aligned_cuts"] == [3.0]
    assert result["missed_boundaries"] == [8.0]
    assert sorted(result["model_added_cuts"]) == [5.5, 9.0]
    assert result["alignment_score"] == 0.5  # below 0.8 convergence threshold


def test_zero_expected():
    """Single-segment pass (N-1 = 0 expected cuts) — score is vacuously 1.0."""
    result = _align_detected_to_expected(
        detected=[2.0, 4.0],
        intended_durations=[10.0],
        pass_duration=10.0,
    )
    assert result["aligned_cuts"] == []
    assert result["model_added_cuts"] == [2.0, 4.0]
    assert result["alignment_score"] == 1.0
    assert result["missed_boundaries"] == []


def test_empty_detected():
    """No cuts detected — every expected boundary is missed, score = 0.0."""
    result = _align_detected_to_expected(
        detected=[],
        intended_durations=[3.0, 5.0, 2.0],
        pass_duration=10.0,
    )
    assert result["aligned_cuts"] == []
    assert result["missed_boundaries"] == [3.0, 8.0]
    assert result["model_added_cuts"] == []
    assert result["alignment_score"] == 0.0


def test_cluster_inside_segment():
    """Spurious cluster inside a single planner segment — all model-added."""
    result = _align_detected_to_expected(
        detected=[1.0, 1.5, 2.0, 8.0],
        intended_durations=[3.0, 5.0, 2.0],
        pass_duration=10.0,
    )
    # expected_ts = [3.0, 8.0]. Closest to 3.0 is 2.0 (delta 1.0 > 0.5) → missed.
    # 8.0 matches exactly.
    assert result["aligned_cuts"] == [8.0]
    assert result["missed_boundaries"] == [3.0]
    assert result["model_added_cuts"] == [1.0, 1.5, 2.0]
    assert result["alignment_score"] == 0.5


def test_two_cuts_within_tolerance():
    """Two candidates within tolerance — nearer delta wins."""
    result = _align_detected_to_expected(
        detected=[2.8, 3.1],
        intended_durations=[3.0, 4.0],
        pass_duration=7.0,
    )
    # 3.1 is 0.1 from 3.0; 2.8 is 0.2 from 3.0. 3.1 wins.
    assert result["aligned_cuts"] == [3.1]
    assert result["model_added_cuts"] == [2.8]
    assert result["alignment_score"] == 1.0
    assert result["missed_boundaries"] == []


def test_deterministic_tie_break():
    """Equal-distance candidates — earlier timestamp wins."""
    result = _align_detected_to_expected(
        detected=[2.5, 3.5],
        intended_durations=[3.0, 4.0],
        pass_duration=7.0,
    )
    # Both are 0.5 from 3.0. Earlier (2.5) wins.
    assert result["aligned_cuts"] == [2.5]
    assert result["model_added_cuts"] == [3.5]
    assert result["alignment_score"] == 1.0


def test_tolerance_boundary():
    """Distances straddling the tolerance window."""
    result = _align_detected_to_expected(
        detected=[3.49, 3.51],
        intended_durations=[3.0, 4.0],
        pass_duration=7.0,
    )
    # 3.49 delta 0.49 (within 0.5); 3.51 delta 0.51 (outside).
    assert result["aligned_cuts"] == [3.49]
    assert result["model_added_cuts"] == [3.51]
    assert result["alignment_score"] == 1.0
    assert result["missed_boundaries"] == []


def test_single_segment_with_extras_preserves_all():
    """Single-segment pass with N detected cuts — all are preserved as
    model-added (spec invariant: happy-accident cuts on a single-shot pass
    are always kept as sub-segments, never dropped)."""
    result = _align_detected_to_expected(
        detected=[2.0, 4.0, 6.0],
        intended_durations=[10.0],
        pass_duration=10.0,
    )
    assert result["aligned_cuts"] == []
    assert result["model_added_cuts"] == [2.0, 4.0, 6.0]
    assert result["alignment_score"] == 1.0
    assert result["missed_boundaries"] == []
