"""W18D bg→fp region ref slice — minimal safety tests only.

3 tests:
1. happy path join shape + coverage on synthetic W18A2 + W18C inputs.
2. missing bg in W18A2 overlay OR base_marker not in W18C read causes
   invariant fail.
3. methodology grep + production/api zero guard.
"""
from __future__ import annotations

import json
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 _synthetic_w18a_fp_entry() -> dict:
    return {
        "base_fp_t2i_prompt_text": "schematic plan.",
        "included_marker_legend": [
            {"marker_number": 1, "source_candidate_number": 1,
             "base_layer_decision": "base_structural_unit",
             "label": "u1", "unit_id": "U1",
             "visual_encoding": "filled_area",
             "must_be_legible": True},
            {"marker_number": 2, "source_candidate_number": 2,
             "base_layer_decision": "base_structural_unit",
             "label": "u2", "unit_id": "U2",
             "visual_encoding": "filled_area",
             "must_be_legible": True},
        ],
        "excluded_transient_elements": [
            {"marker_number": 14, "source_candidate_number": 14,
             "base_layer_decision": "state_overlay_transient_object",
             "label": "Tx", "unit_id": "U1",
             "excluded_reason": "scene state",
             "overlay_instruction_hint": "describe in bg body"},
        ],
        "bg_state_overlay_payload_by_bg": {
            "BGa": {
                "bg_id": "BGa", "fp_id": "fp_l05_01",
                "target_unit_ids": ["U1"],
                "base_markers_to_reference": [1],
                "transient_markers_to_describe": [14],
                "prompt_appendix_hint": "anchor on living",
            },
            "BGb": {
                "bg_id": "BGb", "fp_id": "fp_l05_01",
                "target_unit_ids": ["U2"],
                "base_markers_to_reference": [2],
                "transient_markers_to_describe": [],
                "prompt_appendix_hint": "clean view",
            },
        },
        "base_fp_contract_notes": "compact zones",
        "production_prompt_delta_recommendations": "review",
    }


def _synthetic_w18c_readback() -> dict:
    return {
        "read_markers": [
            {"marker_number": 1, "visible": True, "confidence": "high",
             "observed_label_or_area": "zone1",
             "approximate_10x10_rect": [0, 0, 4, 4], "notes": ""},
            {"marker_number": 2, "visible": True, "confidence": "high",
             "observed_label_or_area": "zone2",
             "approximate_10x10_rect": [5, 0, 9, 4], "notes": ""},
        ],
        "missing_or_ambiguous_markers": [],
        "base_unit_layout": [
            {"marker_number": 1, "unit_id": "U1",
             "observed_area_label": "u1",
             "approximate_10x10_rect": [0, 0, 4, 4],
             "adjacency_notes": "left"},
            {"marker_number": 2, "unit_id": "U2",
             "observed_area_label": "u2",
             "approximate_10x10_rect": [5, 0, 9, 4],
             "adjacency_notes": "right"},
        ],
        "structural_relationship_summary": {},
        "unexpected_transient_markers": [],
        "readback_conflicts": [],
    }


def _build_region_ref_via_helper(w18a_fp, w18c_readback):
    from experiment_floor_plan_bg_to_fp_region_ref_slice import (
        _build_region_ref_for_fp,
    )
    return _build_region_ref_for_fp(
        fp_id="fp_l05_01",
        w18a_fp_entry=w18a_fp, w18c_readback=w18c_readback,
        w18b_png_relative_to_repo="scripts_output/fake/png/fp_l05_01.png",
    )


def _build_report(*, region_ref_by_bg, w18a_fp_entry, w18c_readback,
                  target_fp_ids={"fp_l05_01"},
                  production_diff_empty=True, image_import_seen=False,
                  image_api_call_count=0, vlm_api_call_count=0,
                  llm_api_call_count=0):
    from experiment_floor_plan_bg_to_fp_region_ref_slice import (
        _build_w18d_compatibility_report,
    )
    return _build_w18d_compatibility_report(
        region_ref_by_bg=region_ref_by_bg,
        w18a_fp_entry=w18a_fp_entry,
        w18c_readback=w18c_readback,
        target_fp_ids=target_fp_ids,
        production_diff_empty=production_diff_empty,
        db_write_count=0, image_import_seen=image_import_seen,
        image_api_call_count=image_api_call_count,
        vlm_api_call_count=vlm_api_call_count,
        llm_api_call_count=llm_api_call_count,
        missing_inputs=[], prev_run_id="fakeW18C",
    )


def test_w18d_happy_path_join_shape_and_coverage():
    """Synthetic W18A2 + W18C → W18D join produces per-bg payloads with
    target_unit_refs / base_marker_refs / transient_overlay_to_describe
    populated, and all 5 invariants pass."""
    w18a = _synthetic_w18a_fp_entry()
    w18c = _synthetic_w18c_readback()
    region_ref, diags = _build_region_ref_via_helper(w18a, w18c)
    assert set(region_ref.keys()) == {"BGa", "BGb"}
    bga = region_ref["BGa"]
    assert bga["base_fp_png_path_relative_to_repo"].endswith(
        "/png/fp_l05_01.png"
    )
    assert any(
        r["unit_id"] == "U1" and r["marker_number"] == 1
        for r in bga["target_unit_refs"]
    )
    assert bga["base_marker_refs"][0]["marker_number"] == 1
    assert bga["base_marker_refs"][0]["visible"] is True
    assert bga["base_marker_refs"][0]["confidence"] == "high"
    assert (
        bga["transient_overlay_to_describe"][0]["marker_number"] == 14
    )
    assert "#1" in bga["region_prompt_hint"]
    assert "#14" in bga["region_prompt_hint"]
    # BGb has no transient marker — region hint must say (none) for it.
    bgb = region_ref["BGb"]
    assert bgb["transient_overlay_to_describe"] == []
    assert "(none)" in bgb["region_prompt_hint"]

    rep = _build_report(
        region_ref_by_bg=region_ref,
        w18a_fp_entry=w18a, w18c_readback=w18c,
    )
    assert rep["all_pass"] is True


def test_w18d_missing_bg_or_unresolved_base_marker_fails_invariants():
    """If W18A2 overlay carries a bg the W18D output is missing → invariant
    3 fails. If a base_marker is not in W18C read_markers → invariant 4
    fails."""
    w18a = _synthetic_w18a_fp_entry()
    w18c = _synthetic_w18c_readback()
    region_ref, _ = _build_region_ref_via_helper(w18a, w18c)

    # (a) drop BGb from W18D output → cover invariant fails.
    missing_bg = json.loads(json.dumps(region_ref))
    del missing_bg["BGb"]
    rep_miss = _build_report(
        region_ref_by_bg=missing_bg,
        w18a_fp_entry=w18a, w18c_readback=w18c,
    )
    assert rep_miss["invariants"][
        "bg_region_refs_cover_w18a2_overlay_bgs"
    ]["pass"] is False

    # (b) drop marker #1 from W18C read_markers → resolve invariant fails.
    w18c_bad = json.loads(json.dumps(w18c))
    w18c_bad["read_markers"] = [
        e for e in w18c_bad["read_markers"] if e["marker_number"] != 1
    ]
    region_ref_bad, _ = _build_region_ref_via_helper(w18a, w18c_bad)
    rep_resolve = _build_report(
        region_ref_by_bg=region_ref_bad,
        w18a_fp_entry=w18a, w18c_readback=w18c_bad,
    )
    assert rep_resolve["invariants"][
        "referenced_base_markers_resolve_in_w18c"
    ]["pass"] is False

    # (c) base marker visible=False → resolve invariant fails.
    w18c_invis = json.loads(json.dumps(w18c))
    w18c_invis["read_markers"][0]["visible"] = False
    region_ref_invis, _ = _build_region_ref_via_helper(w18a, w18c_invis)
    rep_invis = _build_report(
        region_ref_by_bg=region_ref_invis,
        w18a_fp_entry=w18a, w18c_readback=w18c_invis,
    )
    assert rep_invis["invariants"][
        "referenced_base_markers_resolve_in_w18c"
    ]["pass"] is False


def test_w18d_production_api_zero_guard_and_methodology_grep():
    """All API call counts must be zero; production diff empty; image
    imports not seen. Plus script source must not embed scenario-specific
    tokens."""
    w18a = _synthetic_w18a_fp_entry()
    w18c = _synthetic_w18c_readback()
    region_ref, _ = _build_region_ref_via_helper(w18a, w18c)

    rep_ok = _build_report(
        region_ref_by_bg=region_ref,
        w18a_fp_entry=w18a, w18c_readback=w18c,
    )
    assert rep_ok["invariants"][
        "production_diff_zero_db_write_zero_llm_vlm_image_zero"
    ]["pass"] is True

    rep_llm = _build_report(
        region_ref_by_bg=region_ref,
        w18a_fp_entry=w18a, w18c_readback=w18c,
        llm_api_call_count=1,
    )
    assert rep_llm["invariants"][
        "production_diff_zero_db_write_zero_llm_vlm_image_zero"
    ]["pass"] is False

    rep_img = _build_report(
        region_ref_by_bg=region_ref,
        w18a_fp_entry=w18a, w18c_readback=w18c,
        image_api_call_count=1,
    )
    assert rep_img["invariants"][
        "production_diff_zero_db_write_zero_llm_vlm_image_zero"
    ]["pass"] is False

    rep_vlm = _build_report(
        region_ref_by_bg=region_ref,
        w18a_fp_entry=w18a, w18c_readback=w18c,
        vlm_api_call_count=1,
    )
    assert rep_vlm["invariants"][
        "production_diff_zero_db_write_zero_llm_vlm_image_zero"
    ]["pass"] is False

    rep_prod = _build_report(
        region_ref_by_bg=region_ref,
        w18a_fp_entry=w18a, w18c_readback=w18c,
        production_diff_empty=False,
    )
    assert rep_prod["invariants"][
        "production_diff_zero_db_write_zero_llm_vlm_image_zero"
    ]["pass"] is False

    script_path = (
        _SCRIPTS_DIR
        / "experiment_floor_plan_bg_to_fp_region_ref_slice.py"
    )
    assert script_path.exists(), f"script missing: {script_path}"
    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 ''}"
    )
