"""
tests/test_abis.py
------------------
Unit tests for the Vaultfire Protocol ABI JSON files.

Verifies:
  - All 16 ABI JSON files exist
  - Each file is valid JSON containing an array
  - Each ABI has at least one entry
  - Key functions exist in their respective ABIs
  - ABI entries have required fields (type, name where applicable)
"""

import json
import os

import pytest

# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------

REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ABIS_DIR = os.path.join(REPO_ROOT, "abis")

ALL_CONTRACTS = [
    "AIAccountabilityBondsV2",
    "AIPartnershipBondsV2",
    "AntiSurveillance",
    "BeliefAttestationVerifier",
    "DilithiumAttestor",
    "ERC8004IdentityRegistry",
    "ERC8004ReputationRegistry",
    "ERC8004ValidationRegistry",
    "FlourishingMetricsOracle",
    "MissionEnforcement",
    "MultisigGovernance",
    "PrivacyGuarantees",
    "ProductionBeliefAttestationVerifier",
    "VaultfireERC8004Adapter",
    "VaultfireNameService",
    "VaultfireTeleporterBridge",
]

# Key functions that MUST exist in each contract
REQUIRED_FUNCTIONS: dict[str, list[str]] = {
    "ERC8004IdentityRegistry": [
        "registerAgent",
        "getAgent",
        "getTotalAgents",
        "owner",
    ],
    "AIPartnershipBondsV2": [
        "nextBondId",
        "createBond",
        "getBond",
        "owner",
    ],
    "AIAccountabilityBondsV2": [
        "nextBondId",
        "owner",
    ],
    "MultisigGovernance": [
        "signers",
        "threshold",
        "transactionCount",
        "proposeTransaction",
        "confirmTransaction",
        "executeTransaction",
    ],
    "FlourishingMetricsOracle": [
        "owner",
    ],
    "VaultfireNameService": [
        "isNameAvailable",
        "resolveNameToAddress",
        "registerName",
        "resolveName",
        "reverseLookup",
    ],
    "MissionEnforcement": [],
    "AntiSurveillance": [],
    "PrivacyGuarantees": [],
}

# Minimum ABI entry counts (to detect truncation)
MIN_ENTRY_COUNTS: dict[str, int] = {
    "AIPartnershipBondsV2":          50,
    "AIAccountabilityBondsV2":       50,
    "ERC8004IdentityRegistry":       30,
    "ERC8004ValidationRegistry":     20,
    "ERC8004ReputationRegistry":     20,
    "FlourishingMetricsOracle":      20,
    "MultisigGovernance":            20,
    "VaultfireTeleporterBridge":     20,
    "ProductionBeliefAttestationVerifier": 20,
}


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def load_abi(contract: str) -> list:
    path = os.path.join(ABIS_DIR, f"{contract}.json")
    with open(path) as f:
        return json.load(f)


def get_function_names(abi: list) -> set[str]:
    return {e["name"] for e in abi if e.get("type") == "function"}


def get_event_names(abi: list) -> set[str]:
    return {e["name"] for e in abi if e.get("type") == "event"}


# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------

class TestABIFilesExist:
    """Verify all 16 ABI files are present."""

    @pytest.mark.parametrize("contract", ALL_CONTRACTS)
    def test_file_exists(self, contract: str):
        path = os.path.join(ABIS_DIR, f"{contract}.json")
        assert os.path.exists(path), f"Missing ABI file: {contract}.json"

    def test_exactly_16_abi_files(self):
        json_files = [f for f in os.listdir(ABIS_DIR) if f.endswith(".json")]
        assert len(json_files) == 16, (
            f"Expected 16 ABI files, found {len(json_files)}: {sorted(json_files)}"
        )


class TestABIValidity:
    """Verify each ABI file is valid JSON and properly structured."""

    @pytest.mark.parametrize("contract", ALL_CONTRACTS)
    def test_valid_json_array(self, contract: str):
        abi = load_abi(contract)
        assert isinstance(abi, list), f"{contract}: ABI should be a JSON array"

    @pytest.mark.parametrize("contract", ALL_CONTRACTS)
    def test_non_empty(self, contract: str):
        abi = load_abi(contract)
        assert len(abi) > 0, f"{contract}: ABI array is empty"

    @pytest.mark.parametrize("contract", ALL_CONTRACTS)
    def test_entries_have_type(self, contract: str):
        abi = load_abi(contract)
        for i, entry in enumerate(abi):
            assert "type" in entry, (
                f"{contract}: entry[{i}] missing 'type' field: {entry}"
            )

    @pytest.mark.parametrize("contract", ALL_CONTRACTS)
    def test_functions_have_name(self, contract: str):
        abi = load_abi(contract)
        for i, entry in enumerate(abi):
            if entry.get("type") == "function":
                assert "name" in entry, (
                    f"{contract}: function entry[{i}] missing 'name': {entry}"
                )

    @pytest.mark.parametrize("contract", ALL_CONTRACTS)
    def test_functions_have_stateMutability(self, contract: str):
        abi = load_abi(contract)
        for i, entry in enumerate(abi):
            if entry.get("type") == "function":
                assert "stateMutability" in entry, (
                    f"{contract}: function '{entry.get('name')}' missing 'stateMutability'"
                )


class TestMinimumEntryCounts:
    """Verify ABIs are not truncated."""

    @pytest.mark.parametrize("contract,minimum", MIN_ENTRY_COUNTS.items())
    def test_minimum_entry_count(self, contract: str, minimum: int):
        abi = load_abi(contract)
        assert len(abi) >= minimum, (
            f"{contract}: expected at least {minimum} entries, got {len(abi)}"
        )


class TestRequiredFunctions:
    """Verify key functions exist in their respective ABIs."""

    @pytest.mark.parametrize("contract,functions", [
        (c, fns) for c, fns in REQUIRED_FUNCTIONS.items() if fns
    ])
    def test_required_functions_exist(self, contract: str, functions: list[str]):
        abi = load_abi(contract)
        present = get_function_names(abi)
        for fn in functions:
            assert fn in present, (
                f"{contract}: missing required function '{fn}'. "
                f"Available: {sorted(present)}"
            )


class TestStarContracts:
    """
    Deeper verification for the AI Accountability Bonds and AI Partnership Bonds
    — the stars of the protocol.
    """

    def test_partnership_bonds_bond_lifecycle(self):
        """Verify the full bond creation and distribution lifecycle exists."""
        abi = load_abi("AIPartnershipBondsV2")
        functions = get_function_names(abi)
        lifecycle = ["createBond", "distributeBond", "getBond", "nextBondId"]
        for fn in lifecycle:
            assert fn in functions, (
                f"AIPartnershipBondsV2 missing lifecycle function '{fn}'"
            )

    def test_partnership_bonds_yield_pool(self):
        """Verify yield pool functions exist."""
        abi = load_abi("AIPartnershipBondsV2")
        functions = get_function_names(abi)
        yield_fns = ["fundYieldPool", "getYieldPoolBalance", "withdrawYieldPool"]
        for fn in yield_fns:
            assert fn in functions, (
                f"AIPartnershipBondsV2 missing yield pool function '{fn}'"
            )

    def test_accountability_bonds_challenge_system(self):
        """Verify accountability/challenge functions exist."""
        abi = load_abi("AIAccountabilityBondsV2")
        functions = get_function_names(abi)
        assert "nextBondId" in functions

    def test_accountability_bonds_has_events(self):
        """Verify accountability bond events exist."""
        abi = load_abi("AIAccountabilityBondsV2")
        events = get_event_names(abi)
        assert len(events) > 0, "AIAccountabilityBondsV2 should have events"

    def test_partnership_bonds_has_events(self):
        """Verify partnership bond events exist."""
        abi = load_abi("AIPartnershipBondsV2")
        events = get_event_names(abi)
        assert len(events) > 0, "AIPartnershipBondsV2 should have events"


class TestBeliefSystem:
    """Tests for the belief-weighted governance contracts."""

    def test_belief_attestation_verifier_exists(self):
        abi = load_abi("BeliefAttestationVerifier")
        assert isinstance(abi, list)
        assert len(abi) >= 5

    def test_production_belief_verifier_exists(self):
        abi = load_abi("ProductionBeliefAttestationVerifier")
        assert isinstance(abi, list)
        assert len(abi) >= 20

    def test_production_belief_verifier_richer(self):
        """Production verifier should be more comprehensive than the base verifier."""
        prod = load_abi("ProductionBeliefAttestationVerifier")
        base = load_abi("BeliefAttestationVerifier")
        assert len(prod) >= len(base), (
            "ProductionBeliefAttestationVerifier should have >= entries as BeliefAttestationVerifier"
        )


class TestPrivacyAndMissionContracts:
    """Tests for the core commitment contracts."""

    @pytest.mark.parametrize("contract", ["MissionEnforcement", "AntiSurveillance", "PrivacyGuarantees"])
    def test_commitment_contract_exists(self, contract: str):
        abi = load_abi(contract)
        assert isinstance(abi, list)
        assert len(abi) >= 5, f"{contract} ABI suspiciously small"

    def test_erc8004_suite_complete(self):
        """All three ERC-8004 registries must have ABIs."""
        for contract in ["ERC8004IdentityRegistry", "ERC8004ReputationRegistry", "ERC8004ValidationRegistry"]:
            abi = load_abi(contract)
            assert isinstance(abi, list)
            assert len(abi) >= 10, f"{contract} ABI too small"
