from __future__ import annotations

import copy
import json
import sys
from pathlib import Path

import pytest

REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))

import run_leo_local_ingestion_proposal_canary as canary  # noqa: E402


def _loaded() -> tuple[dict, dict, dict]:
    fixture, input_receipt = canary.load_fixture(canary.DEFAULT_FIXTURE)
    row_ids = canary.build_row_ids(input_receipt["artifact_sha256"])
    return fixture, input_receipt, row_ids


def test_fixture_is_realistic_hash_bound_and_exactly_evidenced() -> None:
    fixture, input_receipt, row_ids = _loaded()

    assert fixture["extraction"]["evidence"]["body"] in fixture["source"]["body"]
    assert len(input_receipt["artifact_sha256"]) == 64
    assert len(input_receipt["source_content_sha256"]) == 64
    assert input_receipt["artifact_sha256"] != input_receipt["source_content_sha256"]
    assert len(set(row_ids.values())) == 4


def test_fixture_validation_fails_closed_when_evidence_is_not_in_source(tmp_path: Path) -> None:
    fixture = json.loads(canary.DEFAULT_FIXTURE.read_text(encoding="utf-8"))
    fixture["extraction"]["evidence"]["body"] = "invented evidence"
    path = tmp_path / "bad-fixture.json"
    path.write_text(json.dumps(fixture), encoding="utf-8")

    with pytest.raises(canary.CanaryError, match="exact substring"):
        canary.load_fixture(path)


def test_fixture_validation_fails_closed_when_claim_body_is_not_in_source(
    tmp_path: Path,
) -> None:
    fixture = json.loads(canary.DEFAULT_FIXTURE.read_text(encoding="utf-8"))
    fixture["extraction"]["claim"]["body"] = "invented claim body"
    path = tmp_path / "bad-claim-fixture.json"
    path.write_text(json.dumps(fixture), encoding="utf-8")

    with pytest.raises(canary.CanaryError, match="claim body must be an exact substring"):
        canary.load_fixture(path)


def test_capture_sql_escapes_quotes_and_backslashes_from_fixture(
    tmp_path: Path,
) -> None:
    fixture = json.loads(canary.DEFAULT_FIXTURE.read_text(encoding="utf-8"))
    fixture["source"]["title"] = "O'Brien\\review"
    fixture["source"]["metadata"]["note"] = "quoted ' value \\ path"
    path = tmp_path / "quoted-fixture.json"
    path.write_text(json.dumps(fixture), encoding="utf-8")
    loaded, input_receipt = canary.load_fixture(path)
    row_ids = canary.build_row_ids(input_receipt["artifact_sha256"])

    sql = canary.build_capture_sql(loaded, input_receipt, row_ids)

    assert canary.ap.sql_literal(fixture["source"]["title"]) in sql
    assert canary.ap.sql_literal(
        json.dumps(fixture["source"]["metadata"], sort_keys=True)
    ) in sql


def test_parent_normalizes_to_hash_bound_pending_proposal_with_embedded_row_links() -> None:
    fixture, input_receipt, row_ids = _loaded()
    parent = canary.build_parent_packet(fixture, input_receipt, row_ids)
    child = canary.normalized_stage.prepare_normalized_child(parent)
    payload = child["payload"]["apply_payload"]

    assert child["status"] == "pending_review"
    assert child["proposal_type"] == "approve_claim"
    assert len(payload["claims"]) == 1
    assert len(payload["sources"]) == 2
    assert len(payload["evidence"]) == 2
    assert input_receipt["source_content_sha256"] in {row["hash"] for row in payload["sources"]}
    excerpts = "\n".join(row["excerpt"] for row in payload["sources"])
    assert input_receipt["artifact_sha256"] in excerpts
    assert row_ids["source_capture_id"] in excerpts
    assert row_ids["claim_extraction_id"] in excerpts
    assert row_ids["evidence_extraction_id"] in excerpts


def test_capture_and_stage_sql_never_mutate_canonical_public_tables() -> None:
    fixture, input_receipt, row_ids = _loaded()
    child = canary.normalized_stage.prepare_normalized_child(
        canary.build_parent_packet(fixture, input_receipt, row_ids)
    )
    sql = "\n".join(
        (
            canary.build_schema_sql(),
            canary.build_capture_sql(fixture, input_receipt, row_ids),
            canary.normalized_stage.build_stage_sql(child),
        )
    ).lower()

    assert "insert into public." not in sql
    assert "update public." not in sql
    assert "delete from public." not in sql
    assert "insert into kb_canary.source_captures" in sql
    assert "insert into kb_canary.claim_extractions" in sql
    assert "insert into kb_canary.evidence_extractions" in sql
    assert "insert into kb_stage.kb_proposals" in sql


def test_check_evaluation_requires_every_row_link_and_staging_boundary() -> None:
    fixture, input_receipt, row_ids = _loaded()
    child = canary.normalized_stage.prepare_normalized_child(
        canary.build_parent_packet(fixture, input_receipt, row_ids)
    )
    apply_payload = child["payload"]["apply_payload"]
    readback = {
        "source_capture": {
            "id": row_ids["source_capture_id"],
            "artifact_sha256": input_receipt["artifact_sha256"],
            "content_sha256": input_receipt["source_content_sha256"],
            "source_identity": input_receipt["source_identity"],
        },
        "claim_extraction": {
            "id": row_ids["claim_extraction_id"],
            "source_capture_id": row_ids["source_capture_id"],
            "body": fixture["extraction"]["claim"]["body"],
            "metadata": {"text": fixture["extraction"]["claim"]["text"]},
        },
        "evidence_extraction": {
            "id": row_ids["evidence_extraction_id"],
            "claim_extraction_id": row_ids["claim_extraction_id"],
            "source_capture_id": row_ids["source_capture_id"],
            "body": fixture["extraction"]["evidence"]["body"],
            "metadata": fixture["extraction"]["evidence"]["metadata"],
        },
        "staged_proposal": {
            "id": child["id"],
            "status": "pending_review",
            "source_ref": child["source_ref"],
            "payload": {"apply_payload": apply_payload},
            "reviewed_by_handle": None,
            "reviewed_at": None,
            "applied_by_handle": None,
            "applied_at": None,
        },
        "counts": {
            "source_captures": 1,
            "claim_extractions": 1,
            "evidence_extractions": 1,
            "staged_proposals": 1,
        },
    }

    checks = canary.evaluate_checks(fixture, input_receipt, row_ids, readback)
    assert all(checks.values())
    broken = copy.deepcopy(readback)
    broken["evidence_extraction"]["claim_extraction_id"] = canary.stable_uuid("0" * 64, "wrong")
    assert (
        canary.evaluate_checks(fixture, input_receipt, row_ids, broken)["evidence_links_to_claim_and_source"] is False
    )
