#!/usr/bin/env python3
"""Audit issue 79's native RLV state, inventory, lock, and permission boundary."""

from __future__ import annotations

import json
import re
from pathlib import Path

import generate_api_shims


ROOT = Path(__file__).resolve().parents[1]
SOURCE = ROOT / "crates" / "libremetaverse-rlv" / "src" / "state.rs"
GENERATED = ROOT / "crates" / "libremetaverse-rlv" / "src" / "generated.rs"
TESTS = ROOT / "crates" / "libremetaverse-rlv" / "tests" / "state_semantics.rs"
COMPAT = ROOT / "tests" / "compat" / "tests" / "rlv_inventory_map_semantics.rs"
DOC = ROOT / "crates" / "libremetaverse-rlv" / "README.md"
WORKFLOW = ROOT / ".gitea" / "workflows" / "rust-workspace.yml"
CATALOG = ROOT / "api" / "public-api.json"
STUB_RE = re.compile(r"\b(?:not_implemented|unimplemented_api)\b|\b(?:todo|unimplemented)!\s*\(")

TYPES = {
    "T:LibreMetaverse.RLV.CameraRestrictions": "crate::state::CameraRestrictions",
    "T:LibreMetaverse.RLV.CameraSettings": "crate::state::CameraSettings",
    "T:LibreMetaverse.RLV.EventArguments.RestrictionUpdatedEventArgs":
        "crate::state::RestrictionUpdatedEventArgs",
    "T:LibreMetaverse.RLV.InventoryMap": "crate::state::InventoryMap",
    "T:LibreMetaverse.RLV.LockedFolderPublic": "crate::state::LockedFolderPublic",
    "T:LibreMetaverse.RLV.RlvBlacklist": "crate::state::RlvBlacklist",
    "T:LibreMetaverse.RLV.RlvInventoryItem": "crate::state::RlvInventoryItem",
    "T:LibreMetaverse.RLV.RlvPermissionsService": "crate::state::RlvPermissionsService",
    "T:LibreMetaverse.RLV.RlvRestrictionManager": "crate::state::RlvRestrictionManager",
    "T:LibreMetaverse.RLV.RlvSharedFolder": "crate::state::RlvSharedFolder",
}


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 catalog_members() -> set[str]:
    catalog = json.loads(CATALOG.read_text())
    assembly = next(
        value for value in catalog["assemblies"]
        if value["identity"]["name"] == "LibreMetaverse.RLV"
    )
    return {
        member["doc_id"]
        for api_type in assembly["types"]
        if api_type["doc_id"] in TYPES
        for member in api_type["members"]
    }


def generated_type_block(text: str, doc_id: str) -> str:
    marker = f"/// C# type: `{doc_id}`."
    start = text.find(marker)
    if start < 0:
        raise SystemExit(f"generated declaration is missing for {doc_id}")
    next_type = text.find("/// C# type:", start + len(marker))
    return text[start:] if next_type < 0 else text[start:next_type]


def main() -> None:
    for api_type, declaration in TYPES.items():
        if generate_api_shims.NATIVE_DECLARATIONS.get(api_type) != declaration:
            raise SystemExit(f"issue 79 native declaration is missing for {api_type}")
    members = catalog_members()
    missing = sorted(members - set(generate_api_shims.NATIVE_MEMBER_BODIES))
    if missing:
        raise SystemExit("issue 79 native members missing: " + ", ".join(missing))

    source = SOURCE.read_text()
    if STUB_RE.search(source):
        raise SystemExit("issue 79 owned Rust stubs remain in state.rs")
    generated = GENERATED.read_text()
    for api_type in TYPES:
        if STUB_RE.search(generated_type_block(generated, api_type)):
            raise SystemExit(f"issue 79 owned generated stubs remain for {api_type}")

    require_markers(SOURCE, (
        "MAX_RLV_INVENTORY_FOLDERS", "MAX_RLV_INVENTORY_ITEMS",
        "MAX_RLV_RESTRICTIONS", "pub struct InventoryMap",
        "pub struct RlvRestrictionManager", "pub struct RlvPermissionsService",
        "pub struct CameraRestrictions", "pub struct LockedFolderPublic",
        "pub struct RlvBlacklist", "fn build_locked_folders",
        "fn secure_allowed", "fn rebuild_locked_folders", "fn emit",
        "state.revision", "Arc<RwLock", "ImmutableDictionary", "CancellationToken",
    ))
    require_markers(TESTS, (
        "dispatches_after_unlocking", "concurrent_independent_sources",
        "stale_revision", "counts_repeated_inputs_before_deduplication",
        "blacklist_is_case_insensitive", "camera_restrictions_apply",
        "constructor_field_order", "secure_rules_take_precedence",
        "recursive_folder_locks", "snapshots remain immutable",
    ))
    require_markers(COMPAT, (
        "try_get_folder_when_name_contains_slash",
        "try_get_folder_prefers_exact_slash_match",
        "try_get_folder_skips_hidden_folder",
        "find_folders_containing_by_prim_id",
        "find_folders_containing_by_attachment_point_single_result",
        "find_folders_containing_by_wearable_type",
    ))
    require_markers(DOC, (
        "pure state boundary", "immutable dictionary/list membership snapshots",
        "after manager locks are released", "secure and explicit", "no network",
        "ubuntu-latest",
    ))
    require_markers(WORKFLOW, ("python3 tools/check_milestone_10_issue_79.py",))
    print(
        "issue 79 audit: native restriction state, inventory maps, recursive locks, "
        "camera aggregation, blacklist, permission precedence, concurrency evidence, "
        "compatibility tests, and docs are present"
    )


if __name__ == "__main__":
    main()
