#!/usr/bin/env python3
"""Audit the complete native world-services milestone boundary."""

from __future__ import annotations

import json
from pathlib import Path
import re

import generate_api_shims


ROOT = Path(__file__).resolve().parents[1]
SOURCE_ROOT = ROOT / "crates" / "libremetaverse" / "src"
OWNED_SOURCES = (
    "agent_manager.rs",
    "agent_messages.rs",
    "agent_movement.rs",
    "animation.rs",
    "animesh.rs",
    "animesh_skinning.rs",
    "appearance_baker.rs",
    "appearance_manager.rs",
    "asset_archive.rs",
    "asset_cache.rs",
    "asset_manager.rs",
    "asset_material.rs",
    "asset_models.rs",
    "avatar_manager.rs",
    "avatar_rig.rs",
    "baking_texture_provider.rs",
    "current_outfit.rs",
    "directory_manager.rs",
    "environment_manager.rs",
    "estate_tools.rs",
    "friends_manager.rs",
    "gesture.rs",
    "gltf.rs",
    "grid_manager.rs",
    "group_manager.rs",
    "initial_outfit.rs",
    "interest_list.rs",
    "inventory.rs",
    "inventory_ais.rs",
    "inventory_manager.rs",
    "marketplace.rs",
    "model_workflow.rs",
    "object_manager.rs",
    "object_material.rs",
    "object_model.rs",
    "parcel_manager.rs",
    "slurl.rs",
    "sound_manager.rs",
    "terrain_codec.rs",
    "terrain_manager.rs",
    "transfers.rs",
    "user_report.rs",
)
REQUIRED_NATIVE_TYPES = {
    "T:LibreMetaverse.AgentManager",
    "T:LibreMetaverse.AgentManager.AgentMovement",
    "T:LibreMetaverse.Animesh.AnimeshManager",
    "T:LibreMetaverse.AppearanceManager",
    "T:LibreMetaverse.AssetManager",
    "T:LibreMetaverse.AvatarManager",
    "T:LibreMetaverse.DirectoryManager",
    "T:LibreMetaverse.EnvironmentManager",
    "T:LibreMetaverse.EstateTools",
    "T:LibreMetaverse.FriendsManager",
    "T:LibreMetaverse.GridManager",
    "T:LibreMetaverse.GroupManager",
    "T:LibreMetaverse.InventoryAISClient",
    "T:LibreMetaverse.InventoryManager",
    "T:LibreMetaverse.Marketplace.MarketplaceManager",
    "T:LibreMetaverse.ObjectManager",
    "T:LibreMetaverse.ParcelManager",
    "T:LibreMetaverse.SoundManager",
    "T:LibreMetaverse.TerrainManager",
}
REQUIRED_NATIVE_MEMBERS = {
    "M:LibreMetaverse.InventoryManager.UploadThumbnailAsync(LibreMetaverse.UUID,LibreMetaverse.UUID,System.Byte[],System.Threading.CancellationToken,System.IProgress{LibreMetaverse.HttpCapsClient.ProgressReport})",
    "P:LibreMetaverse.GridClient.Marketplace",
    "P:LibreMetaverse.GridClient.Marketplace#set",
}
RELATED_UPSTREAM_SOURCES = {
    "LibreMetaverse.Tests/AnimationTrackTests.cs",
    "LibreMetaverse.Tests/AnimeshPlayerTests.cs",
    "LibreMetaverse.Tests/AnimeshSkinningTests.cs",
    "LibreMetaverse.Tests/AppearanceLiveTests.cs",
    "LibreMetaverse.Tests/AppearanceManagerTests.cs",
    "LibreMetaverse.Tests/AppearanceTests.cs",
    "LibreMetaverse.Tests/AssetMaterialOverrideTests.cs",
    "LibreMetaverse.Tests/AvatarManagerTests.cs",
    "LibreMetaverse.Tests/BakerTests.cs",
    "LibreMetaverse.Tests/BatchLinkTests.cs",
    "LibreMetaverse.Tests/CurrentOutfitFolderTests.cs",
    "LibreMetaverse.Tests/EnvironmentManagerTests.cs",
    "LibreMetaverse.Tests/EstateExperienceReplyTests.cs",
    "LibreMetaverse.Tests/EstateToolsTests.cs",
    "LibreMetaverse.Tests/ExperienceListAndRegionMessageTests.cs",
    "LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs",
    "LibreMetaverse.Tests/GLTFMaterialOverrideCapTests.cs",
    "LibreMetaverse.Tests/GLTFMaterialUploadTests.cs",
    "LibreMetaverse.Tests/GltfDocumentTests.cs",
    "LibreMetaverse.Tests/InventoryAISClientTests.cs",
    "LibreMetaverse.Tests/InventoryFetchTests.cs",
    "LibreMetaverse.Tests/InventoryManagerTests.cs",
    "LibreMetaverse.Tests/InventoryTests.cs",
    "LibreMetaverse.Tests/LibraryInventoryTests.cs",
    "LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs",
    "LibreMetaverse.Tests/MarketplaceManagerTests.cs",
    "LibreMetaverse.Tests/MisclassifiedLinkRegressionTests.cs",
    "LibreMetaverse.Tests/OarFileTerrainTests.cs",
    "LibreMetaverse.Tests/OfflineMessagesTests.cs",
    "LibreMetaverse.Tests/PrimObjectTests.cs",
    "LibreMetaverse.Tests/PrimitiveTests.cs",
    "LibreMetaverse.Tests/ProductInfoRequestMessageTests.cs",
    "LibreMetaverse.Tests/QueryExperiencesOnParcelTests.cs",
    "LibreMetaverse.Tests/RegionScheduleTests.cs",
    "LibreMetaverse.Tests/RequestTaskInventoryCapTests.cs",
    "LibreMetaverse.Tests/SendPostcardTests.cs",
    "LibreMetaverse.Tests/SimConsoleTests.cs",
    "LibreMetaverse.Tests/SlurlParserTests.cs",
    "LibreMetaverse.Tests/StartAuctionTests.cs",
    "LibreMetaverse.Tests/TerrainMaterialOverrideTests.cs",
    "LibreMetaverse.Tests/TextureEntryTests.cs",
    "LibreMetaverse.Tests/UploadThumbnailTests.cs",
}
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}: audit evidence missing: " + ", ".join(missing))


def check_owned_sources() -> None:
    offenders = [
        name for name in OWNED_SOURCES if STUB_RE.search((SOURCE_ROOT / name).read_text())
    ]
    if offenders:
        raise SystemExit("milestone 09 owned Rust stubs remain: " + ", ".join(offenders))


def check_native_mappings() -> None:
    native = set(generate_api_shims.NATIVE_TYPES) | set(generate_api_shims.NATIVE_DECLARATIONS)
    missing_types = sorted(REQUIRED_NATIVE_TYPES - native)
    if missing_types:
        raise SystemExit("milestone 09 native types missing: " + ", ".join(missing_types))
    missing_members = sorted(
        REQUIRED_NATIVE_MEMBERS - set(generate_api_shims.NATIVE_MEMBER_BODIES)
    )
    if missing_members:
        raise SystemExit("milestone 09 native members missing: " + ", ".join(missing_members))


def check_parity_catalog() -> None:
    catalog = json.loads((ROOT / "tests" / "upstream-tests.json").read_text())
    related = [test for test in catalog["tests"] if test["source"] in RELATED_UPSTREAM_SOURCES]
    present_sources = {test["source"] for test in related}
    missing_sources = sorted(RELATED_UPSTREAM_SOURCES - present_sources)
    if missing_sources:
        raise SystemExit("milestone 09 parity sources missing: " + ", ".join(missing_sources))
    bad = [
        test["id"]
        for test in related
        if test["status"] not in {"translated", "ignored-live"}
        or test.get("semantic_review") != "reviewed"
    ]
    if bad:
        raise SystemExit("milestone 09 parity cases are not reviewed: " + ", ".join(bad))
    missing_rust = sorted(
        {
            test["rust_file"]
            for test in related
            if test["status"] == "translated"
            and not (ROOT / test["rust_file"]).is_file()
        }
    )
    if missing_rust:
        raise SystemExit("milestone 09 translated files missing: " + ", ".join(missing_rust))


def check_fake_grid_gate() -> None:
    require_markers(
        ROOT / "crates" / "libremetaverse" / "tests" / "network_manager.rs",
        (
            "offline_fake_grid_drives_the_complete_networking_lifecycle_without_task_leaks",
            "event_queue_running",
            "fake-grid inventory marker",
            "create_asset_wrapper",
            "get_player",
            "request_object",
            "current_parcel",
            "friend_list",
            "current_group_count",
            "marketplace_error",
            "search_people",
            "pending_logout_tasks",
            "dispatcher_running",
            "is_cancellation_requested",
            "seed-secret",
            "queue-secret",
            "asset-secret",
        ),
    )


def check_docs_and_program_prerequisites() -> None:
    require_markers(
        ROOT / "docs" / "world-milestone-gate.md",
        (
            "Cross-manager fake grid",
            "Cancellation and teardown",
            "Live actions remain opt-in",
            "tools/test_milestone_09.py",
            "RUN_LIVE_TESTS=1",
        ),
    )
    projects = json.loads((ROOT / "programs" / "upstream-programs.json").read_text())[
        "projects"
    ]
    if len(projects) != 9:
        raise SystemExit("milestone 09 expected all nine program prerequisite inventories")
    manifest = (ROOT / "programs" / "Cargo.toml").read_text()
    if manifest.count("[[bin]]") != 9:
        raise SystemExit("milestone 09 expected all nine Rust program targets")


def main() -> None:
    check_owned_sources()
    check_native_mappings()
    check_parity_catalog()
    check_fake_grid_gate()
    check_docs_and_program_prerequisites()
    print(
        "milestone 09 audit: owned world sources contain no stubs; native mappings, "
        "reviewed parity cases, fake-grid integration, docs, and program prerequisites are present"
    )


if __name__ == "__main__":
    main()
