"""Session key manager — time-limited, scoped signing keys for AI agents."""

from dataclasses import dataclass

from web3 import Web3

from .constants import EXPLORER_URLS, SESSION_KEY_MANAGER_ABI


@dataclass
class SessionKeyInfo:
    """Information about an active session key."""

    session_id: int
    owner: str
    session_address: str
    max_value_wei: int
    total_limit_wei: int
    total_spent_wei: int
    remaining_wei: int
    expires_at: int
    created_at: int
    revoked: bool
    allow_any_contract: bool


class SessionKeyManager:
    """Create and manage time-limited session keys.

    Session keys give an agent (or delegated agent) just enough authority
    for a specific task, and nothing more. They expire automatically.

    Example:
        >>> session_id = client.sessions.create_session(
        ...     session_address="0xTempKey...",
        ...     max_value="0.01",
        ...     total_limit="0.1",
        ...     duration_hours=24,
        ...     allowed_contracts=["0xUniswap..."],
        ... )
    """

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

    def create_session(
        self,
        from_address: str,
        session_address: str,
        max_value: str = "0.01",
        total_limit: str = "0.1",
        duration_hours: int = 24,
        allowed_contracts: list[str] | None = None,
    ) -> dict:
        """Create a new session key with scoped permissions.

        Args:
            from_address: The agent creating the session (must be ERC-8004 registered).
            session_address: The address to authorize as session signer.
            max_value: Max ETH per transaction for this session.
            total_limit: Max total ETH the session can spend in its lifetime.
            duration_hours: How many hours the session key is valid.
            allowed_contracts: List of contract addresses this session can call.
                              Empty list = any contract allowed.

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

        manager = self._w3.eth.contract(
            address=Web3.to_checksum_address(self._manager_address),
            abi=SESSION_KEY_MANAGER_ABI,
        )

        contracts = [Web3.to_checksum_address(c) for c in (allowed_contracts or [])]

        tx = manager.functions.createSession(
            Web3.to_checksum_address(session_address),
            Web3.to_wei(max_value, "ether"),
            Web3.to_wei(total_limit, "ether"),
            duration_hours * 3600,
            contracts,
        ).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": "Create session key"}

    def get_session(self, session_id: int) -> SessionKeyInfo:
        """Get full details of a session key.

        Args:
            session_id: The session ID to look up.

        Returns:
            SessionKeyInfo with all session details.
        """
        if not self._manager_address:
            raise ValueError("SessionKeyManager address not configured.")

        manager = self._w3.eth.contract(
            address=Web3.to_checksum_address(self._manager_address),
            abi=SESSION_KEY_MANAGER_ABI,
        )

        data = manager.functions.getSession(session_id).call()
        remaining = manager.functions.sessionRemaining(session_id).call()

        return SessionKeyInfo(
            session_id=session_id,
            owner=data[0],
            session_address=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],
            revoked=data[7],
            allow_any_contract=data[8],
        )

    def is_valid(self, session_id: int) -> bool:
        """Check if a session key is currently valid.

        Args:
            session_id: The session ID to check.

        Returns:
            True if the session is active (not expired, not revoked, has remaining limit).
        """
        if not self._manager_address:
            raise ValueError("SessionKeyManager address not configured.")

        manager = self._w3.eth.contract(
            address=Web3.to_checksum_address(self._manager_address),
            abi=SESSION_KEY_MANAGER_ABI,
        )

        return manager.functions.isSessionValid(session_id).call()

    def revoke_session(self, from_address: str, session_id: int) -> dict:
        """Revoke a specific session key.

        Args:
            from_address: The session owner's address.
            session_id: The session ID to revoke.

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

        manager = self._w3.eth.contract(
            address=Web3.to_checksum_address(self._manager_address),
            abi=SESSION_KEY_MANAGER_ABI,
        )

        tx = manager.functions.revokeSession(session_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 session #{session_id}"}

    def revoke_all(self, from_address: str) -> dict:
        """Emergency: revoke ALL session keys for an agent.

        Args:
            from_address: The agent's address.

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

        manager = self._w3.eth.contract(
            address=Web3.to_checksum_address(self._manager_address),
            abi=SESSION_KEY_MANAGER_ABI,
        )

        tx = manager.functions.revokeAllSessions().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 session keys"}
