"""W18J overlap-reference planner — minimal tests.

2 tests:
1. deterministic overlap planner picks reference_derived vs
   fp_seeded_anchor by exact-ID overlap, anchor seeded as catalog.
2. anchor cannot appear in target set + methodology grep.
"""
from __future__ import annotations

import re
import sys
from pathlib import Path


_REPO_ROOT = Path(__file__).resolve().parents[3]
_SCRIPTS_DIR = _REPO_ROOT / "backend" / "scripts"
if str(_SCRIPTS_DIR) not in sys.path:
    sys.path.insert(0, str(_SCRIPTS_DIR))


def _synth_w18d_bg(units, base_markers, transient_markers=None,
                   fp_id="fp_l05_01",
                   base_fp_ref_path="scripts_output/fake/png/fp.png"):
    return {
        "fp_id": fp_id,
        "base_fp_ref_path": base_fp_ref_path,
        "target_unit_refs": [
            {"unit_id": u, "marker_number": i + 100, "label": u}
            for i, u in enumerate(units)
        ],
        "base_marker_refs": [
            {"marker_number": m, "label": f"m{m}"} for m in base_markers
        ],
        "transient_overlay_to_describe": [
            {"marker_number": t, "label": f"t{t}", "unit_id": "u"}
            for t in (transient_markers or [])
        ],
        "transient_overlay_marker_numbers": list(transient_markers or []),
        "clean_background_expected": not bool(transient_markers),
        "assembled_background_prompt_preview": (
            "source prose ----- region appendix -----"
        ),
    }


def test_w18j_overlap_planner_dispatches_mode_by_exact_id():
    """Catalog seeded with anchor BG. New targets dispatched as
    reference_derived when overlap is strong, fp_seeded_anchor when
    weak. Tie-breaking by score desc, then bg_id asc."""
    from experiment_background_image_overlap_reference_slice import (
        _compute_reference_decision, W18J_MODE_REFERENCE_DERIVED,
        W18J_MODE_FP_SEEDED_ANCHOR, W18J_MAX_REFS_PER_BG,
    )

    base_fp = Path("/non/existent/fp.png")  # value only, not opened here
    anchor_w18d = _synth_w18d_bg(
        units=["UnitA", "UnitL"], base_markers=[1, 11, 13],
    )
    catalog = [{
        "bg_id": "ANCHOR", "png_path": "/tmp/anchor.png",
        "unit_set": sorted({"UnitA", "UnitL"}),
        "base_marker_set": sorted({1, 11, 13}),
        "transient_marker_set": [],
    }]

    # (a) Strong overlap: share UnitL + base marker 11.
    strong_target = _synth_w18d_bg(units=["UnitL", "UnitX"],
                                   base_markers=[11, 99])
    dec_strong = _compute_reference_decision(
        bg_id="BG_STRONG", target_w18d_bg=strong_target,
        catalog=catalog, base_fp_png_path=base_fp,
    )
    assert dec_strong["mode"] == W18J_MODE_REFERENCE_DERIVED
    assert dec_strong["fp_included"] is False
    assert [s["ref_bg_id"] for s in dec_strong["selected_refs"]] == ["ANCHOR"]
    cand0 = dec_strong["candidate_scores"][0]
    assert cand0["shared_units"] == ["UnitL"]
    assert cand0["shared_base_markers"] == [11]
    assert cand0["is_strong_overlap"] is True

    # (b) Weak overlap: share only one unit, no shared base marker → fp_seed.
    weak_target = _synth_w18d_bg(units=["UnitL", "UnitY"],
                                 base_markers=[42, 43])
    dec_weak = _compute_reference_decision(
        bg_id="BG_WEAK", target_w18d_bg=weak_target,
        catalog=catalog, base_fp_png_path=base_fp,
    )
    assert dec_weak["mode"] == W18J_MODE_FP_SEEDED_ANCHOR
    assert dec_weak["fp_included"] is True
    assert dec_weak["selected_refs"] == []
    # Reference paths must point at the FP.
    assert dec_weak["reference_paths"] == [str(base_fp)]

    # (c) Two strong candidates → cap at W18J_MAX_REFS_PER_BG.
    catalog2 = catalog + [{
        "bg_id": "BEDROOM_ANCHOR", "png_path": "/tmp/bed.png",
        "unit_set": ["UnitB", "UnitL"],
        "base_marker_set": [3, 8],
        "transient_marker_set": [],
    }, {
        "bg_id": "EXTRA", "png_path": "/tmp/extra.png",
        "unit_set": ["UnitL"],
        "base_marker_set": [99],
        "transient_marker_set": [],
    }]
    split_target = _synth_w18d_bg(
        units=["UnitA", "UnitB", "UnitL"], base_markers=[1, 3, 11],
    )
    dec_split = _compute_reference_decision(
        bg_id="BG_SPLIT", target_w18d_bg=split_target,
        catalog=catalog2, base_fp_png_path=base_fp,
    )
    assert dec_split["mode"] == W18J_MODE_REFERENCE_DERIVED
    assert dec_split["fp_included"] is False
    assert (
        len(dec_split["selected_refs"]) <= W18J_MAX_REFS_PER_BG
    )
    selected_ids = [s["ref_bg_id"] for s in dec_split["selected_refs"]]
    # ANCHOR (UnitA, UnitL share + 1, 11 share, score 2+0.5*2=3.0) and
    # BEDROOM_ANCHOR (UnitB, UnitL share + 3 share, score 2+0.5*1=2.5)
    # must both appear; EXTRA (UnitL share + 11 share, score 1+0.5*1=1.5)
    # may be dropped because of the W18J_MAX_REFS_PER_BG cap.
    assert "ANCHOR" in selected_ids
    assert "BEDROOM_ANCHOR" in selected_ids


def test_w18j_target_excludes_anchor_and_methodology_grep():
    """L05B02 (anchor) must be rejected from target list; script source
    must not embed scenario-specific tokens."""
    from experiment_background_image_overlap_reference_slice import (
        _resolve_target_bg_ids, W18J_ANCHOR_BG_ID, W18J_ALLOWED_BG_IDS,
    )

    seen, invalid = _resolve_target_bg_ids(
        f"{W18J_ANCHOR_BG_ID},L05B05,L05B04,L05B99,L05B03"
    )
    assert W18J_ANCHOR_BG_ID in invalid
    assert "L05B99" in invalid
    # Order preserved for the legit subset.
    assert seen == ["L05B05", "L05B04", "L05B03"]
    for bg in seen:
        assert bg in W18J_ALLOWED_BG_IDS

    script_path = (
        _SCRIPTS_DIR
        / "experiment_background_image_overlap_reference_slice.py"
    )
    assert script_path.exists()
    forbidden_tokens = [
        "b" + "edroom", "ki" + "tchen", "blood" + "stain", "cur" + "tain",
        "coo" + "ktop", "tele" + "vision", "cri" + "me", "vi" + "lla",
        "roo" + "ftop", "foot" + "print", "pol" + "ice", "de" + "ck",
        "wheel" + "house", "ba" + "throom", "su" + "ri-young",
    ]
    pat = re.compile(r"(?i)\b(" + "|".join(forbidden_tokens) + r")\b")
    m = pat.search(script_path.read_text())
    assert m is None, (
        f"{script_path.name}: scenario-specific token leaked → "
        f"{m.group(0) if m else ''}"
    )
