"""Unit tests for the per-sheet DrawingIntegrityAgent and scope builder."""

import backend.agents.integrity_agent as integ
from backend.agents.base import AgentScope, AgentUsage
from backend.agents.integrity_agent import (
    DrawingIntegrityAgent, build_integrity_scopes,
)
from backend import config


def _sheet(page, sheet_number, n_assertions):
    return {
        "sheet_number": sheet_number,
        "page_number": page,
        "sheet_title": f"Sheet {sheet_number}",
        "discipline": "Architectural",
        "assertions": [
            {"object_type": "note", "source_text": f"note {i}"}
            for i in range(n_assertions)
        ],
    }


def test_build_scopes_skips_sparse_sheets(monkeypatch):
    monkeypatch.setattr(config, "INTEGRITY_MIN_ASSERTIONS", 3)
    sheets = [
        _sheet(1, "A101", 5),   # kept
        _sheet(2, "A102", 2),   # skipped (too sparse)
        _sheet(3, "A103", 3),   # kept (== floor)
    ]
    page_to_b64 = {1: "IMG1", 2: "IMG2", 3: "IMG3"}
    page_to_text = {1: "text one", 2: "text two", 3: "text three"}
    scopes = build_integrity_scopes(sheets, page_to_b64, page_to_text)
    ids = sorted(s.scope_id for s in scopes)
    assert ids == ["integrity:1", "integrity:3"]
    # Each scope carries its own page image + text layer.
    by_id = {s.scope_id: s for s in scopes}
    assert by_id["integrity:1"].payload["image_b64"] == "IMG1"
    assert by_id["integrity:1"].payload["text_layer"] == "text one"


def test_agent_parses_and_anchors_sheet(monkeypatch):
    """Findings with blank sheets get anchored to the scope's sheet number."""
    captured = {}

    def fake_call_json(**kwargs):
        captured.update(kwargs)
        return {"issues": [
            {"issue_id": "DI-1", "severity": "high", "confidence": "high",
             "category": "dangling_reference", "sheets": [],
             "description": "Detail callout 5/A101 has no detail 5 on this sheet",
             "evidence": [{"sheet": "A101", "source_text": "5/A101"}]},
        ]}

    monkeypatch.setattr(integ, "call_json", fake_call_json)
    sheet = _sheet(1, "A101", 5)
    scope = AgentScope("integrity:1", {
        "sheet": sheet, "page_number": 1,
        "image_b64": "IMG1", "text_layer": "the deterministic text layer",
    })
    result = DrawingIntegrityAgent(AgentUsage()).run(scope)
    assert result.error == ""
    assert len(result.artifacts) == 1
    finding = result.artifacts[0]
    assert finding["source_stage"] == "drawing_integrity"
    assert finding["sheets"] == ["A101"]          # anchored
    assert finding["agent"] == "drawing_integrity"
    assert finding["scope_id"] == "integrity:1"
    # The image + text layer reached the model.
    assert captured["images_b64"] == ["IMG1"]
    assert "the deterministic text layer" in captured["user_text"]


def test_agent_empty_issues_is_clean(monkeypatch):
    monkeypatch.setattr(integ, "call_json", lambda **k: {"issues": []})
    scope = AgentScope("integrity:1", {
        "sheet": _sheet(1, "A101", 5), "page_number": 1,
        "image_b64": "IMG1", "text_layer": "t",
    })
    result = DrawingIntegrityAgent(AgentUsage()).run(scope)
    assert result.error == ""
    assert result.artifacts == []


def test_agent_survives_call_failure(monkeypatch):
    def boom(**kwargs):
        raise RuntimeError("model exploded")
    monkeypatch.setattr(integ, "call_json", boom)
    scope = AgentScope("integrity:1", {
        "sheet": _sheet(1, "A101", 5), "page_number": 1,
        "image_b64": "IMG1", "text_layer": "t",
    })
    result = DrawingIntegrityAgent(AgentUsage()).run(scope)
    assert "model exploded" in result.error
    assert result.artifacts == []
