"""
EIP-712 signing utilities for the Agent Reputation Protocol.

Uses eth_account to produce typed data signatures that the ARP API
uses to authenticate write operations.
"""

from __future__ import annotations

import time
from typing import Any

from eth_account import Account
from eth_account.messages import encode_typed_data

# EIP-712 domain separator for ARP
ARP_DOMAIN: dict[str, Any] = {
    "name": "AgentReputationProtocol",
    "version": "1",
    "chainId": 1,
    "verifyingContract": "0x0000000000000000000000000000000000000000",
}

# EIP-712 type definitions for all signable operations
ARP_TYPES: dict[str, list[dict[str, str]]] = {
    "RegisterAgent": [
        {"name": "name", "type": "string"},
        {"name": "description", "type": "string"},
        {"name": "domains", "type": "string"},
        {"name": "nonce", "type": "uint256"},
        {"name": "deadline", "type": "uint256"},
    ],
    "UpdateAgent": [
        {"name": "agentId", "type": "string"},
        {"name": "name", "type": "string"},
        {"name": "description", "type": "string"},
        {"name": "domains", "type": "string"},
        {"name": "nonce", "type": "uint256"},
        {"name": "deadline", "type": "uint256"},
    ],
    "AcceptTransaction": [
        {"name": "transactionId", "type": "string"},
        {"name": "nonce", "type": "uint256"},
        {"name": "deadline", "type": "uint256"},
    ],
    "SubmitTransaction": [
        {"name": "transactionId", "type": "string"},
        {"name": "rating", "type": "uint8"},
        {"name": "feedback", "type": "string"},
        {"name": "nonce", "type": "uint256"},
        {"name": "deadline", "type": "uint256"},
    ],
    "Deposit": [
        {"name": "agentId", "type": "string"},
        {"name": "amount", "type": "uint256"},
        {"name": "nonce", "type": "uint256"},
        {"name": "deadline", "type": "uint256"},
    ],
    "Withdraw": [
        {"name": "agentId", "type": "string"},
        {"name": "amount", "type": "uint256"},
        {"name": "nonce", "type": "uint256"},
        {"name": "deadline", "type": "uint256"},
    ],
    "Dispute": [
        {"name": "transactionId", "type": "string"},
        {"name": "reason", "type": "string"},
        {"name": "nonce", "type": "uint256"},
        {"name": "deadline", "type": "uint256"},
    ],
}


def generate_nonce() -> int:
    """Generate a nonce based on the current timestamp in milliseconds."""
    return int(time.time() * 1000)


def generate_deadline(seconds_from_now: int = 300) -> int:
    """Generate a deadline timestamp (current time + specified seconds)."""
    return int(time.time()) + seconds_from_now


def _build_eip712_payload(
    primary_type: str,
    message: dict[str, Any],
) -> dict[str, Any]:
    """Build a full EIP-712 structured data payload."""
    if primary_type not in ARP_TYPES:
        raise ValueError(f"Unknown signable type: {primary_type}")

    types = {
        "EIP712Domain": [
            {"name": "name", "type": "string"},
            {"name": "version", "type": "string"},
            {"name": "chainId", "type": "uint256"},
            {"name": "verifyingContract", "type": "address"},
        ],
        primary_type: ARP_TYPES[primary_type],
    }

    return {
        "types": types,
        "primaryType": primary_type,
        "domain": ARP_DOMAIN,
        "message": message,
    }


def sign_typed_data(
    private_key: str,
    primary_type: str,
    message: dict[str, Any],
) -> str:
    """
    Sign a typed data message using EIP-712.

    Args:
        private_key: The hex-encoded private key to sign with.
        primary_type: The primary type name (e.g., 'RegisterAgent').
        message: The data to sign, matching the fields of the primary type.

    Returns:
        The hex-encoded signature string.
    """
    payload = _build_eip712_payload(primary_type, message)
    structured = encode_typed_data(payload)
    signed = Account.sign_message(structured, private_key=private_key)
    return str(signed.signature.hex())


def get_address(private_key: str) -> str:
    """Derive the Ethereum address from a private key."""
    account = Account.from_key(private_key)
    return str(account.address)


def sign_register(
    private_key: str,
    name: str,
    description: str,
    domains: list[str],
) -> dict[str, Any]:
    """Build a signed payload for a register agent operation."""
    nonce = generate_nonce()
    deadline = generate_deadline()
    signature = sign_typed_data(private_key, "RegisterAgent", {
        "name": name,
        "description": description,
        "domains": ",".join(domains),
        "nonce": nonce,
        "deadline": deadline,
    })
    return {"signature": signature, "nonce": nonce, "deadline": deadline}


def sign_update(
    private_key: str,
    agent_id: str,
    name: str,
    description: str,
    domains: list[str],
) -> dict[str, Any]:
    """Build a signed payload for an update agent operation."""
    nonce = generate_nonce()
    deadline = generate_deadline()
    signature = sign_typed_data(private_key, "UpdateAgent", {
        "agentId": agent_id,
        "name": name,
        "description": description,
        "domains": ",".join(domains),
        "nonce": nonce,
        "deadline": deadline,
    })
    return {"signature": signature, "nonce": nonce, "deadline": deadline}


def sign_accept(
    private_key: str,
    transaction_id: str,
) -> dict[str, Any]:
    """Build a signed payload for accepting a transaction."""
    nonce = generate_nonce()
    deadline = generate_deadline()
    signature = sign_typed_data(private_key, "AcceptTransaction", {
        "transactionId": transaction_id,
        "nonce": nonce,
        "deadline": deadline,
    })
    return {"signature": signature, "nonce": nonce, "deadline": deadline}


def sign_submit(
    private_key: str,
    transaction_id: str,
    rating: int,
    feedback: str,
) -> dict[str, Any]:
    """Build a signed payload for submitting a transaction rating."""
    nonce = generate_nonce()
    deadline = generate_deadline()
    signature = sign_typed_data(private_key, "SubmitTransaction", {
        "transactionId": transaction_id,
        "rating": rating,
        "feedback": feedback,
        "nonce": nonce,
        "deadline": deadline,
    })
    return {"signature": signature, "nonce": nonce, "deadline": deadline}


def sign_deposit(
    private_key: str,
    agent_id: str,
    amount: str,
) -> dict[str, Any]:
    """Build a signed payload for a stake deposit."""
    nonce = generate_nonce()
    deadline = generate_deadline()
    signature = sign_typed_data(private_key, "Deposit", {
        "agentId": agent_id,
        "amount": int(amount),
        "nonce": nonce,
        "deadline": deadline,
    })
    return {"signature": signature, "nonce": nonce, "deadline": deadline}


def sign_withdraw(
    private_key: str,
    agent_id: str,
    amount: str,
) -> dict[str, Any]:
    """Build a signed payload for a stake withdrawal."""
    nonce = generate_nonce()
    deadline = generate_deadline()
    signature = sign_typed_data(private_key, "Withdraw", {
        "agentId": agent_id,
        "amount": int(amount),
        "nonce": nonce,
        "deadline": deadline,
    })
    return {"signature": signature, "nonce": nonce, "deadline": deadline}


def sign_dispute(
    private_key: str,
    transaction_id: str,
    reason: str,
) -> dict[str, Any]:
    """Build a signed payload for disputing a transaction."""
    nonce = generate_nonce()
    deadline = generate_deadline()
    signature = sign_typed_data(private_key, "Dispute", {
        "transactionId": transaction_id,
        "reason": reason,
        "nonce": nonce,
        "deadline": deadline,
    })
    return {"signature": signature, "nonce": nonce, "deadline": deadline}
