#!/usr/bin/env python3
"""Verify IMM5257 maps/contracts and a generated draft's datasets against its payload.

This checks stored XML values, not Adobe rendering, signatures or barcode validation.
Usage: python verify_imm5257_pdf.py <blank.pdf> <contract.json> <map.json> [payload.json output.pdf]
"""
from __future__ import annotations

import json
import re
import sys
import xml.etree.ElementTree as ET
from pathlib import Path

import pikepdf

from prefill_imm_xfa_pdf import fill, find_by_path


def datasets(path: str) -> ET.Element:
    with pikepdf.open(path) as pdf:
        packets = pdf.Root.AcroForm.XFA
        for index in range(0, len(packets), 2):
            if str(packets[index]) == "datasets":
                return ET.fromstring(packets[index + 1].read_bytes())
    raise ValueError("Missing datasets packet")


def verify(blank: str, contract_path: str, map_path: str, payload_path: str | None = None, output: str | None = None) -> dict:
    root = datasets(blank)
    contract = json.loads(Path(contract_path).read_text(encoding="utf-8"))
    mapping = json.loads(Path(map_path).read_text(encoding="utf-8"))
    targets = {row["xfa"] for row in mapping["fields"]}
    assert targets == set(contract["fields"]), "Map/contract targets differ"
    for target in targets:
        assert find_by_path(root, target), f"Unresolved target: {target}"
    actual_lists = {}
    for node in root.iter():
        entries = [entry for entry in node if entry.attrib.get("lic")]
        if entries:
            actual_lists[node.tag.rsplit("}", 1)[-1]] = {entry.attrib["lic"]: entry.text or "" for entry in entries}
    for name, options in contract["choice_lists"].items():
        if not name.startswith("inline:"):
            assert options == actual_lists[name], f"Stale template options: {name}"
    result = {"targets_verified": len(targets)}
    if payload_path is not None:
        assert output is not None, "Output path required with payload"
        payload = json.loads(Path(payload_path).read_text(encoding="utf-8"))
        fields = payload["fields"]
        for target in ("Passport.PassportNum.PassportNum", "natID.natIDdocs.DocNum.DocNum"):
            if target in fields:
                assert re.fullmatch(r"[A-Za-z0-9]+", fields[target]), f"Invalid document characters: {target}"
        primary = fields.get("PhoneNumbers.Phone.ActualNumber")
        alternate = fields.get("PhoneNumbers.AltPhone.ActualNumber")
        assert not (primary and primary == alternate), "Duplicate alternate telephone number"
        fill(payload_path, output, blank)
        written = datasets(output)
        for target, value in payload["fields"].items():
            matches = find_by_path(written, target)
            assert matches and all(element.text == str(value) for element in matches), f"Draft value mismatch: {target}"
        result.update(values_verified=len(payload["fields"]), output=output)
    return result


if __name__ == "__main__":
    print(json.dumps(verify(*sys.argv[1:])))
