"""Layer 2: Citation accuracy tests — mirrors citationAccuracy.test.ts.

Feeds pre-generated Claude snapshots (ClaudeFact[] JSON files) through
map_citations and asserts acceptable match rates.  No API calls made.

To regenerate snapshots:
    python scripts/generate_fixtures.py
"""
from __future__ import annotations
import json
from dataclasses import dataclass
from pathlib import Path

import pytest

from ai_citer.ai.citation_mapper import map_citations
from ai_citer.models import ClaudeFact, ClaudeCitation

FIXTURES_DIR = Path(__file__).parent / "fixtures"

MATCH_RATE_THRESHOLD = 0.85        # ≥85% of citations must be located
HIGH_CONFIDENCE_THRESHOLD = 0.70   # ≥70% of matched must be exact/normalized


# ---------------------------------------------------------------------------
# helpers
# ---------------------------------------------------------------------------

@dataclass
class AccuracyReport:
    total: int
    matched: int
    exact: int
    normalized: int
    fuzzy: int
    unmatched: int
    match_rate: float
    high_confidence_rate: float


def analyse(raw_text: str, claude_facts: list[ClaudeFact]) -> AccuracyReport:
    facts = map_citations(raw_text, claude_facts)
    citations = [c for f in facts for c in f.citations]

    total = len(citations)
    exact = sum(1 for c in citations if c.confidence == "exact")
    normalized = sum(1 for c in citations if c.confidence == "normalized")
    fuzzy = sum(1 for c in citations if c.confidence == "fuzzy" and c.charOffset >= 0)
    unmatched = sum(1 for c in citations if c.charOffset < 0)
    matched = total - unmatched

    return AccuracyReport(
        total=total,
        matched=matched,
        exact=exact,
        normalized=normalized,
        fuzzy=fuzzy,
        unmatched=unmatched,
        match_rate=matched / total if total > 0 else 1.0,
        high_confidence_rate=(exact + normalized) / matched if matched > 0 else 1.0,
    )


def load_fixture(name: str) -> tuple[str, list[ClaudeFact]]:
    txt_path = FIXTURES_DIR / f"{name}.txt"
    json_path = FIXTURES_DIR / f"{name}-facts.json"
    text = txt_path.read_text(encoding="utf-8")
    raw_facts = json.loads(json_path.read_text(encoding="utf-8"))
    facts = [
        ClaudeFact(
            fact=f["fact"],
            citations=[ClaudeCitation(exact_quote=c["exact_quote"]) for c in f["citations"]],
        )
        for f in raw_facts
    ]
    return text, facts


# ---------------------------------------------------------------------------
# per-fixture: article
# ---------------------------------------------------------------------------

@pytest.fixture(scope="module")
def article_report() -> AccuracyReport:
    text, facts = load_fixture("article")
    return analyse(text, facts)


def test_article_match_rate(article_report: AccuracyReport):
    pct = article_report.match_rate * 100
    print(f"\narticle match rate: {pct:.0f}%")
    assert article_report.match_rate >= MATCH_RATE_THRESHOLD


def test_article_high_confidence_rate(article_report: AccuracyReport):
    pct = article_report.high_confidence_rate * 100
    print(f"\narticle high-confidence rate: {pct:.0f}%")
    assert article_report.high_confidence_rate >= HIGH_CONFIDENCE_THRESHOLD


def test_article_exact_dominates(article_report: AccuracyReport):
    assert article_report.exact > article_report.fuzzy


def test_article_has_citations(article_report: AccuracyReport):
    print(f"\narticle accuracy: {article_report}")
    assert article_report.total > 0


# ---------------------------------------------------------------------------
# per-fixture: policy
# ---------------------------------------------------------------------------

@pytest.fixture(scope="module")
def policy_report() -> AccuracyReport:
    text, facts = load_fixture("policy")
    return analyse(text, facts)


def test_policy_match_rate(policy_report: AccuracyReport):
    pct = policy_report.match_rate * 100
    print(f"\npolicy match rate: {pct:.0f}%")
    assert policy_report.match_rate >= MATCH_RATE_THRESHOLD


def test_policy_high_confidence_rate(policy_report: AccuracyReport):
    pct = policy_report.high_confidence_rate * 100
    print(f"\npolicy high-confidence rate: {pct:.0f}%")
    assert policy_report.high_confidence_rate >= HIGH_CONFIDENCE_THRESHOLD


def test_policy_exact_dominates(policy_report: AccuracyReport):
    assert policy_report.exact > policy_report.fuzzy


def test_policy_has_citations(policy_report: AccuracyReport):
    print(f"\npolicy accuracy: {policy_report}")
    assert policy_report.total > 0


# ---------------------------------------------------------------------------
# aggregate across all fixtures
# ---------------------------------------------------------------------------

def test_aggregate_match_rate():
    fixture_names = ["article", "policy"]
    total_citations = 0
    total_matched = 0

    for name in fixture_names:
        text, facts = load_fixture(name)
        report = analyse(text, facts)
        total_citations += report.total
        total_matched += report.matched

    aggregate = total_matched / total_citations
    print(f"\naggregate match rate: {aggregate * 100:.1f}% ({total_matched}/{total_citations})")
    assert aggregate >= MATCH_RATE_THRESHOLD
