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

"""Convert privacy-safe corpus findings into human-review starter annotations."""

from __future__ import annotations

import argparse
import json
import re
from pathlib import Path
from typing import Any

import pymupdf as fitz

from mika_deid.loader import DEFAULT_DPI


NEUTRAL_DOCUMENT_ID = re.compile(r"document-\d{4}", re.IGNORECASE)


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Export machine-generated starter annotations for human review"
    )
    parser.add_argument("documents_dir", type=Path)
    parser.add_argument("analysis_dir", type=Path)
    parser.add_argument("output_dir", type=Path)
    parser.add_argument(
        "--confirmed-synthetic",
        action="store_true",
        help="Required acknowledgement that the corpus contains no real patient data",
    )
    return parser.parse_args()


def _clamp(value: float) -> float:
    return round(min(1.0, max(0.0, value)), 6)


def _page_dimensions(document_path: Path) -> list[tuple[float, float]]:
    scale = DEFAULT_DPI / 72.0
    with fitz.open(document_path) as document:
        return [
            (float(page.rect.width) * scale, float(page.rect.height) * scale)
            for page in document
        ]


def build_preannotation(
    document_id: str,
    analysis_record: dict[str, Any],
    page_dimensions: list[tuple[float, float]],
) -> dict[str, Any]:
    if not NEUTRAL_DOCUMENT_ID.fullmatch(document_id):
        raise ValueError("document_id is not a neutral document-NNNN identifier")
    analysis = analysis_record.get("analysis") or {}
    if analysis_record.get("status") != "ok":
        raise ValueError("analysis record is not successful")
    if int(analysis.get("page_count", -1)) != len(page_dimensions):
        raise ValueError("analysis page count does not match the source PDF")

    regions_by_page: dict[int, list[dict[str, Any]]] = {
        page_number: [] for page_number in range(1, len(page_dimensions) + 1)
    }
    counters: dict[int, int] = {page_number: 0 for page_number in regions_by_page}
    for finding in analysis.get("findings", []):
        page_number = int(finding["page"])
        if page_number not in regions_by_page:
            raise ValueError("finding page is outside the source PDF")
        width, height = page_dimensions[page_number - 1]
        if width <= 0 or height <= 0:
            raise ValueError("source PDF has an invalid page size")
        raw = finding["box"]
        box = {
            "x0": _clamp(float(raw["x0"]) / width),
            "y0": _clamp(float(raw["y0"]) / height),
            "x1": _clamp(float(raw["x1"]) / width),
            "y1": _clamp(float(raw["y1"]) / height),
        }
        if not (box["x0"] < box["x1"] and box["y0"] < box["y1"]):
            raise ValueError("finding becomes empty after coordinate normalization")
        counters[page_number] += 1
        regions_by_page[page_number].append(
            {
                "region_id": f"auto-p{page_number:03d}-r{counters[page_number]:04d}",
                "category": finding["category"],
                "box": box,
                "required_action": finding["action"],
                "legibility": "unreviewed",
                "notes": "machine pre-annotation; synthetic corpus; human review required",
                "machine_confidence": finding.get("confidence"),
                "machine_source": finding.get("source"),
                "human_reviewed": False,
            }
        )

    return {
        "schema": "mika-deid-annotation-v1",
        "document_id": document_id,
        "annotation_status": "MACHINE_PREANNOTATION_NOT_GROUND_TRUTH",
        "provenance": {
            "generator": "MIKA De-ID Local 0.2.1",
            "corpus_classification": "CONFIRMED_FULLY_SYNTHETIC",
            "ocr_text_exported": False,
            "source_hash_exported": False,
        },
        "pages": [
            {
                "page": page_number,
                "width": 1.0,
                "height": 1.0,
                "page_reviewed": False,
                "regions": regions_by_page[page_number],
            }
            for page_number in range(1, len(page_dimensions) + 1)
        ],
    }


def main() -> int:
    args = parse_args()
    if not args.confirmed_synthetic:
        raise SystemExit("Refusing export without --confirmed-synthetic")

    documents_dir = args.documents_dir.expanduser().resolve()
    analysis_dir = args.analysis_dir.expanduser().resolve()
    output_dir = args.output_dir.expanduser().resolve()
    if output_dir.exists() and any(output_dir.iterdir()):
        raise SystemExit("Output directory must be absent or empty")
    output_dir.mkdir(mode=0o700, parents=True, exist_ok=True)

    analysis_paths = sorted(analysis_dir.glob("document-*.analysis.json"))
    if not analysis_paths:
        raise SystemExit("No document analysis records found")

    exported = 0
    total_regions = 0
    for analysis_path in analysis_paths:
        record = json.loads(analysis_path.read_text(encoding="utf-8"))
        document_id = str(record.get("document_id") or "")
        source_path = documents_dir / f"{document_id}.pdf"
        if not source_path.is_file():
            raise SystemExit(f"Missing source PDF for {document_id}")
        annotation = build_preannotation(
            document_id,
            record,
            _page_dimensions(source_path),
        )
        output_path = output_dir / f"{document_id}.json"
        output_path.write_text(
            json.dumps(annotation, ensure_ascii=False, indent=2) + "\n",
            encoding="utf-8",
        )
        output_path.chmod(0o600)
        exported += 1
        total_regions += sum(len(page["regions"]) for page in annotation["pages"])

    manifest = {
        "schema": "mika-deid-preannotation-export-v1",
        "annotation_status": "MACHINE_PREANNOTATION_NOT_GROUND_TRUTH",
        "document_count": exported,
        "region_count": total_regions,
        "human_review_required": True,
        "privacy": {
            "ocr_text_exported": False,
            "original_filenames_exported": False,
            "source_hashes_exported": False,
        },
    }
    manifest_path = output_dir / "preannotation-manifest.json"
    manifest_path.write_text(
        json.dumps(manifest, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )
    manifest_path.chmod(0o600)
    print(json.dumps(manifest, ensure_ascii=True))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
