"""Delegation manager — encrypted key delegation between verified AI agents.

This is the "XMTP for keys" layer. Agents can delegate scoped signing
authority to other verified agents, gated by identity, bonds, and reputation.
"""

from dataclasses import dataclass

from web3 import Web3

from .constants import DELEGATION_REGISTRY_ABI, EXPLORER_URLS


@dataclass
class DelegationInfo:
    """Information about an active delegation."""

    delegation_id: int
    delegator: str
    delegate: str
    max_value_wei: int
    total_limit_wei: int
    total_spent_wei: int
    remaining_wei: int
    expires_at: int
    created_at: int
    min_bond_required: int
    min_reputation: int
    revoked: bool
    accepted: bool
    encrypted_key_hash: bytes


class DelegationManager:
    """Create, accept, and manage delegations between agents.

    Delegations let Agent A grant scoped signing authority to Agent B,
    gated by on-chain trust requirements. If Agent B's bond is slashed
    or reputation drops below the threshold, the delegation auto-revokes.

    Example:
        >>> delegation_id = client.delegations.create_delegation(
        ...     from_address="0xAgentA...",
        ...     delegate="0xAgentB...",
        ...     max_value="0.1",
        ...     total_limit="1.0",
        ...     duration_hours=48,
        ...     min_bond="0.01",
        ...     min_reputation=50,
        ... )
    """

    def __init__(self, w3: Web3, network_id: str, registry_address: str | None = None):
        self._w3 = w3
        self._network_id = network_id
        self._registry_address = registry_address
        self._explorer = EXPLORER_URLS.get(network_id, "")

    def create_delegation(
        self,
        from_address: str,
        delegate: str,
        max_value: str = "0.1",
        total_limit: str = "1.0",
        duration_hours: int = 48,
        min_bond: str = "0",
        min_reputation: int = 0,
        allowed_contracts: list[str] | None = None,
        allowed_functions: list[str] | None = None,
        encrypted_key_hash: bytes = b"\x00" * 32,
    ) -> dict:
        """Create a new delegation to another verified agent.

        Args:
            from_address: The delegator's address (must be ERC-8004 registered).
            delegate: The agent receiving authority (must be ERC-8004 registered).
            max_value: Max ETH per transaction for the delegate.
            total_limit: Max total ETH the delegate can spend.
            duration_hours: How many hours the delegation is valid.
            min_bond: Minimum accountability bond the delegate must maintain.
            min_reputation: Minimum reputation score (0-100) the delegate must have.
            allowed_contracts: Contracts the delegate can interact with.
            allowed_functions: Function selectors (hex) the delegate can call.
            encrypted_key_hash: Hash of the off-chain encrypted session key.

        Returns:
            dict with unsigned_tx for signing.
        """
        if not self._registry_address:
            raise ValueError("DelegationRegistry address not configured for this network.")

        registry = self._w3.eth.contract(
            address=Web3.to_checksum_address(self._registry_address),
            abi=DELEGATION_REGISTRY_ABI,
        )

        contracts = [Web3.to_checksum_address(c) for c in (allowed_contracts or [])]
        functions = [bytes.fromhex(f.replace("0x", "")) for f in (allowed_functions or [])]

        tx = registry.functions.createDelegation(
            Web3.to_checksum_address(delegate),
            Web3.to_wei(max_value, "ether"),
            Web3.to_wei(total_limit, "ether"),
            duration_hours * 3600,
            Web3.to_wei(min_bond, "ether"),
            min_reputation,
            contracts,
            functions,
            encrypted_key_hash,
        ).build_transaction({
            "from": Web3.to_checksum_address(from_address),
            "nonce": self._w3.eth.get_transaction_count(
                Web3.to_checksum_address(from_address)
            ),
        })

        return {"unsigned_tx": tx, "description": f"Create delegation → {delegate}"}

    def accept_delegation(self, from_address: str, delegation_id: int, bond_id: int = 0) -> dict:
        """Accept a delegation (delegate must meet trust requirements).

        The contract verifies on-chain that the delegate's bond meets the
        minimum stake requirement and that the bond belongs to the delegate.

        Args:
            from_address: The delegate's address.
            delegation_id: The delegation ID to accept.
            bond_id: The delegate's accountability bond ID (verified on-chain).

        Returns:
            dict with unsigned_tx for signing.
        """
        if not self._registry_address:
            raise ValueError("DelegationRegistry address not configured.")

        registry = self._w3.eth.contract(
            address=Web3.to_checksum_address(self._registry_address),
            abi=DELEGATION_REGISTRY_ABI,
        )

        tx = registry.functions.acceptDelegation(delegation_id, bond_id).build_transaction({
            "from": Web3.to_checksum_address(from_address),
            "nonce": self._w3.eth.get_transaction_count(
                Web3.to_checksum_address(from_address)
            ),
        })

        return {"unsigned_tx": tx, "description": f"Accept delegation #{delegation_id}"}

    def get_delegation(self, delegation_id: int) -> DelegationInfo:
        """Get full details of a delegation.

        Args:
            delegation_id: The delegation ID to look up.

        Returns:
            DelegationInfo with all delegation details.
        """
        if not self._registry_address:
            raise ValueError("DelegationRegistry address not configured.")

        registry = self._w3.eth.contract(
            address=Web3.to_checksum_address(self._registry_address),
            abi=DELEGATION_REGISTRY_ABI,
        )

        data = registry.functions.getDelegation(delegation_id).call()
        remaining = registry.functions.delegationRemaining(delegation_id).call()

        return DelegationInfo(
            delegation_id=delegation_id,
            delegator=data[0],
            delegate=data[1],
            max_value_wei=data[2],
            total_limit_wei=data[3],
            total_spent_wei=data[4],
            remaining_wei=remaining,
            expires_at=data[5],
            created_at=data[6],
            min_bond_required=data[7],
            min_reputation=data[8],
            revoked=data[9],
            accepted=data[10],
            encrypted_key_hash=data[11],
        )

    def is_valid(self, delegation_id: int) -> bool:
        """Check if a delegation is currently valid.

        Args:
            delegation_id: The delegation ID to check.

        Returns:
            True if the delegation is active and accepted.
        """
        if not self._registry_address:
            raise ValueError("DelegationRegistry address not configured.")

        registry = self._w3.eth.contract(
            address=Web3.to_checksum_address(self._registry_address),
            abi=DELEGATION_REGISTRY_ABI,
        )

        return registry.functions.isDelegationValid(delegation_id).call()

    def revoke_delegation(self, from_address: str, delegation_id: int) -> dict:
        """Revoke a specific delegation.

        Args:
            from_address: The delegator's address.
            delegation_id: The delegation ID to revoke.

        Returns:
            dict with unsigned_tx for signing.
        """
        if not self._registry_address:
            raise ValueError("DelegationRegistry address not configured.")

        registry = self._w3.eth.contract(
            address=Web3.to_checksum_address(self._registry_address),
            abi=DELEGATION_REGISTRY_ABI,
        )

        tx = registry.functions.revokeDelegation(delegation_id).build_transaction({
            "from": Web3.to_checksum_address(from_address),
            "nonce": self._w3.eth.get_transaction_count(
                Web3.to_checksum_address(from_address)
            ),
        })

        return {"unsigned_tx": tx, "description": f"Revoke delegation #{delegation_id}"}

    def revoke_all(self, from_address: str) -> dict:
        """Emergency: revoke ALL delegations created by this agent.

        Args:
            from_address: The delegator's address.

        Returns:
            dict with unsigned_tx for signing.
        """
        if not self._registry_address:
            raise ValueError("DelegationRegistry address not configured.")

        registry = self._w3.eth.contract(
            address=Web3.to_checksum_address(self._registry_address),
            abi=DELEGATION_REGISTRY_ABI,
        )

        tx = registry.functions.revokeAllDelegations().build_transaction({
            "from": Web3.to_checksum_address(from_address),
            "nonce": self._w3.eth.get_transaction_count(
                Web3.to_checksum_address(from_address)
            ),
        })

        return {"unsigned_tx": tx, "description": "EMERGENCY: Revoke all delegations"}
