#!/usr/bin/env python3
"""Audit issue 63's native AISv3 capability and reconciliation boundary."""

from __future__ import annotations

from pathlib import Path
import re

import generate_api_shims


ROOT = Path(__file__).resolve().parents[1]
SOURCE = ROOT / "crates" / "libremetaverse" / "src" / "inventory_ais.rs"
HTTP = ROOT / "crates" / "libremetaverse" / "src" / "caps_http.rs"
INVENTORY = ROOT / "crates" / "libremetaverse" / "src" / "inventory.rs"
NATIVE_TESTS = ROOT / "crates" / "libremetaverse" / "src" / "inventory_ais_internal_semantics.rs"
COMPAT_TESTS = ROOT / "tests" / "compat" / "tests" / "inventory_ais_semantics.rs"
DOC = ROOT / "docs" / "inventory.md"
MAPPING = ROOT / "api" / "RUST-MAPPING.tsv"
STUB_RE = re.compile(r"\b(?:not_implemented|unimplemented_api)\b|\b(?:todo|unimplemented)!\s*\(")


def require_markers(path: Path, markers: tuple[str, ...]) -> None:
    text = path.read_text()
    missing = [marker for marker in markers if marker not in text]
    if missing:
        raise SystemExit(f"{path.name}: audit evidence missing: " + ", ".join(missing))


def main() -> None:
    native_types = generate_api_shims.NATIVE_TYPES | generate_api_shims.NATIVE_DECLARATIONS
    for type_id in ("T:LibreMetaverse.AISResponseMeta", "T:LibreMetaverse.InventoryAISClient"):
        if type_id not in native_types:
            raise SystemExit(f"issue 63 native type missing: {type_id}")

    mapped = generate_api_shims.NATIVE_MEMBER_BODIES
    required = set()
    for row in MAPPING.read_text().splitlines()[1:]:
        doc_id = row.split("\t", 1)[0]
        if doc_id.startswith(("M:LibreMetaverse.InventoryAISClient.", "P:LibreMetaverse.InventoryAISClient.")):
            required.add(doc_id)
    missing = sorted(required - mapped.keys())
    if missing:
        raise SystemExit("issue 63 native members missing: " + ", ".join(missing))
    for member in (
        "P:LibreMetaverse.GridClient.AisClient",
        "P:LibreMetaverse.GridClient.AisClient#set",
    ):
        if member not in mapped:
            raise SystemExit(f"issue 63 GridClient member missing: {member}")
    if STUB_RE.search(SOURCE.read_text()):
        raise SystemExit("issue 63 owned Rust stubs remain in inventory_ais.rs")

    require_markers(
        SOURCE,
        (
            '"COPY"',
            '"Destination"',
            '"application/llsd+xml"',
            "MAX_FOLDER_DEPTH_REQUEST",
            "MAX_AIS_OBJECTS",
            "Error::Cancelled",
            "parse_meta_map",
            "native_reconcile_ais",
        ),
    )
    require_markers(HTTP, ("send_custom", "send_with_headers"))
    require_markers(INVENTORY, ("native_apply_ais_batch", "detached candidate"))
    require_markers(
        NATIVE_TESTS,
        (
            "records_exact_ais_verbs_paths_headers_and_llsd_payloads",
            "malformed_and_cancelled_responses_leave_store_unchanged",
            "partially_valid_response_is_rejected_before_any_reconciliation",
            "response_metadata_reconciles_removals_and_folder_versions_together",
        ),
    )
    require_markers(
        COMPAT_TESTS,
        (
            "parse_links_object_asset_results_in_attachment_type_and_parses_fields",
            "parse_embedded_combines_items_and_links",
            "move_category_links_returns_completed_future",
        ),
    )
    require_markers(
        DOC,
        (
            "Inventory API v3",
            "InventoryAPIv3",
            "LibraryAPIv3",
            "Destination",
            "atomically",
            "UDP fallback",
        ),
    )
    print(
        "issue 63 audit: AIS endpoints, LLSD parsing, COPY headers, bounded atomic "
        "reconciliation, documentation, and focused tests are present"
    )


if __name__ == "__main__":
    main()
