"""Cross-field validation, with structured review-flag output."""

from __future__ import annotations

import re

from ocr_sprint.data.master_pangkat import is_valid_pangkat
from ocr_sprint.schemas.extraction import (
    ExtractionResult,
    HeaderFields,
    ReviewFlag,
)
from ocr_sprint.schemas.personnel import PersonnelEntry

# Polri NRP = 8 digits.
_RE_NRP_8 = re.compile(r"^\d{8}$")


def validate_nrp(nrp: str | None) -> bool:
    """Return True when the value is a well-formed Polri NRP (8 digits)."""
    if nrp is None:
        return False
    return bool(_RE_NRP_8.match(nrp.strip()))


def validate_personnel_entry(entry: PersonnelEntry) -> list[ReviewFlag]:
    """Inspect a single personnel row and return any review flags it triggers."""
    flags: list[ReviewFlag] = []
    if entry.nrp and not validate_nrp(entry.nrp):
        flags.append(ReviewFlag.INVALID_NRP)
    if entry.pangkat and not is_valid_pangkat(entry.pangkat):
        flags.append(ReviewFlag.UNKNOWN_PANGKAT)
    # Identification of a personnel row requires at least pangkat OR nrp.
    # A row carrying only a name is structurally incomplete - likely a
    # mis-aligned table cell or a leaked tembusan/dasar fragment - and must
    # be flagged for human review even though pangkat/nrp validation
    # individually pass (because they're empty).
    if not entry.pangkat and not entry.nrp:
        flags.append(ReviewFlag.INCOMPLETE_PERSONNEL_ROW)
    return flags


def validate_header(header: HeaderFields) -> list[ReviewFlag]:
    """Flag missing required fields or unparseable dates in the header."""
    flags: list[ReviewFlag] = []
    if header.nomor_sprint is None:
        flags.append(ReviewFlag.MISSING_FIELD)
    if header.tanggal is None:
        flags.append(ReviewFlag.DATE_PARSE_FAILED)
    return flags


def validate_extraction(
    result: ExtractionResult,
    expected_personnel_count: int | None = None,
) -> list[ReviewFlag]:
    """Run all validators across the full extraction and dedupe the flags."""
    flags: list[ReviewFlag] = []
    flags.extend(validate_header(result.header))
    for entry in result.personel:
        flags.extend(validate_personnel_entry(entry))
    if expected_personnel_count is not None and expected_personnel_count != len(result.personel):
        flags.append(ReviewFlag.PERSONNEL_COUNT_MISMATCH)
    # dedupe while preserving order
    seen: set[ReviewFlag] = set()
    deduped: list[ReviewFlag] = []
    for flag in flags:
        if flag not in seen:
            seen.add(flag)
            deduped.append(flag)
    return deduped
