# Copyright (C) 2026 NPO NT
# SPDX-License-Identifier: AGPL-3.0-only

from __future__ import annotations

import hashlib
import json
from pathlib import Path

import pytest
from PIL import Image

from mika_deid.models import (
    AnalysisResult,
    Action,
    Box,
    CapabilityStatus,
    DocumentData,
    Finding,
    PageData,
    ResidualFinding,
    Severity,
    VerificationResult,
)
from mika_deid.workflow import ReleaseBlockedError, ResidualFindingsForReview, create_verified_copy


def _analysis(tmp_path: Path) -> AnalysisResult:
    source = tmp_path / "synthetic-source.png"
    image = Image.new("RGB", (1000, 500), "white")
    image.save(source)
    digest = hashlib.sha256(source.read_bytes()).hexdigest()
    document = DocumentData(
        source_path=source,
        source_type="image",
        source_size=source.stat().st_size,
        source_sha256=digest,
        pages=[PageData(index=0, image=image, width_points=480.0, height_points=240.0)],
    )
    capabilities = CapabilityStatus(
        ocr_available=True,
        ocr_languages=("rus", "eng"),
        barcode_available=True,
        heif_available=True,
    )
    return AnalysisResult(document=document, findings=[], capabilities=capabilities)


def _residual_verification(*, extra_issue: str | None = None) -> VerificationResult:
    issues = ["После очистки повторно обнаружены прямые идентификаторы: person_name"]
    if extra_issue:
        issues.append(extra_issue)
    residual = ResidualFinding(
        page_index=0,
        category="person_name",
        box=Box(200, 100, 400, 200),
        page_width=2000,
        page_height=1000,
        confidence=93.0,
    )
    return VerificationResult(
        passed=False,
        issues=issues,
        warnings=[],
        page_count=1,
        annotation_count=0,
        embedded_file_count=0,
        extracted_text_characters=0,
        residual_finding_count=1,
        output_sha256="synthetic",
        residual_findings=[residual],
    )


def test_residual_region_is_mapped_back_to_source_for_human_review(tmp_path: Path, monkeypatch) -> None:
    analysis = _analysis(tmp_path)
    monkeypatch.setattr(
        "mika_deid.workflow.verify_raster_pdf",
        lambda *_args, **_kwargs: _residual_verification(),
    )

    with pytest.raises(ResidualFindingsForReview) as captured:
        create_verified_copy(
            analysis,
            tmp_path / "clean.pdf",
            confirmed_pages={0},
        )

    finding = captured.value.findings[0]
    assert finding.page_index == 0
    assert finding.category == "person_name"
    assert finding.source == "verification"
    assert finding.selected
    assert finding.box == Box(88, 38, 212, 112)
    assert not (tmp_path / "clean.pdf").exists()


def test_structural_failure_is_not_downgraded_to_residual_review(tmp_path: Path, monkeypatch) -> None:
    analysis = _analysis(tmp_path)
    monkeypatch.setattr(
        "mika_deid.workflow.verify_raster_pdf",
        lambda *_args, **_kwargs: _residual_verification(extra_issue="В итоговом PDF обнаружено активное содержимое"),
    )

    with pytest.raises(ReleaseBlockedError) as captured:
        create_verified_copy(
            analysis,
            tmp_path / "clean.pdf",
            confirmed_pages={0},
        )

    assert not isinstance(captured.value, ResidualFindingsForReview)
    assert "активное содержимое" in str(captured.value)


def test_residual_public_payload_contains_no_detected_text() -> None:
    verification = _residual_verification()
    payload = verification.as_dict()
    assert payload["residual_regions"] == [
        {
            "page": 1,
            "category": "person_name",
            "normalized_box": {"x0": 0.1, "y0": 0.1, "x1": 0.2, "y1": 0.2},
            "confidence": 93.0,
        }
    ]
    assert "text" not in str(payload["residual_regions"])


def test_success_report_counts_human_confirmed_verification_redactions(tmp_path: Path, monkeypatch) -> None:
    analysis = _analysis(tmp_path)
    analysis.findings.append(
        Finding(
            finding_id="verification-test",
            page_index=0,
            category="person_name",
            box=Box(10, 10, 100, 50),
            action=Action.REDACT,
            severity=Severity.CRITICAL,
            confidence=95.0,
            source="verification",
            reason="synthetic test",
        )
    )
    passed = VerificationResult(
        passed=True,
        issues=[],
        warnings=[],
        page_count=1,
        annotation_count=0,
        embedded_file_count=0,
        extracted_text_characters=0,
        residual_finding_count=0,
        output_sha256="synthetic",
    )
    monkeypatch.setattr("mika_deid.workflow.verify_raster_pdf", lambda *_args, **_kwargs: passed)

    output, report_path, _verification = create_verified_copy(
        analysis,
        tmp_path / "clean.pdf",
        confirmed_pages={0},
    )
    report = json.loads(report_path.read_text(encoding="utf-8"))

    assert output.exists()
    assert report["verification_added_redaction_count"] == 1
    assert report["verification_added_category_counts"] == {"person_name": 1}
