"""
Synor ZK SDK Types for Python

Zero-Knowledge proof systems for ZK-Rollups and privacy.
"""

from dataclasses import dataclass, field
from enum import Enum
from typing import Optional, List, Dict, Any


class ProofSystem(str, Enum):
    """Proof system backends"""
    GROTH16 = "groth16"  # Smallest proofs (~192 bytes), fastest verification
    PLONK = "plonk"      # Universal trusted setup, medium proofs (~512 bytes)
    STARK = "stark"      # No trusted setup, largest proofs (~50KB)


class CircuitType(str, Enum):
    """Circuit types for different operations"""
    TRANSFER = "transfer"  # Single transfer between accounts
    BATCH = "batch"        # Batch of multiple transfers
    DEPOSIT = "deposit"    # Deposit from L1 to L2
    WITHDRAW = "withdraw"  # Withdrawal from L2 to L1


class RollupState(str, Enum):
    """Rollup state"""
    ACTIVE = "active"
    PAUSED = "paused"
    FINALIZING = "finalizing"
    FINALIZED = "finalized"


class ProofStatus(str, Enum):
    """Proof status"""
    GENERATING = "generating"
    COMPLETED = "completed"
    FAILED = "failed"
    VERIFIED = "verified"


@dataclass
class Proof:
    """Zero-knowledge proof"""
    id: str
    system: ProofSystem
    circuit_type: CircuitType
    data: str  # Base64 encoded
    public_inputs: List[str]
    size: int
    generation_time_ms: int
    created_at: int

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Proof":
        return cls(
            id=data["id"],
            system=ProofSystem(data["system"]),
            circuit_type=CircuitType(data["circuit_type"]),
            data=data["data"],
            public_inputs=data.get("public_inputs", []),
            size=data.get("size", 0),
            generation_time_ms=data.get("generation_time_ms", 0),
            created_at=data.get("created_at", 0),
        )


@dataclass
class VerificationKey:
    """Verification key for a circuit"""
    circuit_id: str
    circuit_type: CircuitType
    system: ProofSystem
    data: str  # Base64 encoded
    size: int

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "VerificationKey":
        return cls(
            circuit_id=data["circuit_id"],
            circuit_type=CircuitType(data["circuit_type"]),
            system=ProofSystem(data["system"]),
            data=data["data"],
            size=data.get("size", 0),
        )


@dataclass
class ProvingKey:
    """Proving key for generating proofs"""
    circuit_id: str
    circuit_type: CircuitType
    system: ProofSystem
    data: str  # Base64 encoded
    size: int

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "ProvingKey":
        return cls(
            circuit_id=data["circuit_id"],
            circuit_type=CircuitType(data["circuit_type"]),
            system=ProofSystem(data["system"]),
            data=data["data"],
            size=data.get("size", 0),
        )


@dataclass
class CircuitConfig:
    """Circuit configuration"""
    type: CircuitType
    max_batch_size: Optional[int] = None
    tree_depth: Optional[int] = None
    verify_signatures: Optional[bool] = None
    custom_constraints: Optional[Dict[str, Any]] = None

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "CircuitConfig":
        return cls(
            type=CircuitType(data["type"]),
            max_batch_size=data.get("max_batch_size"),
            tree_depth=data.get("tree_depth"),
            verify_signatures=data.get("verify_signatures"),
            custom_constraints=data.get("custom_constraints"),
        )


@dataclass
class AccountState:
    """Account state in the rollup"""
    address: str
    balance: str
    nonce: int
    pubkey_hash: str

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "AccountState":
        return cls(
            address=data["address"],
            balance=data["balance"],
            nonce=data.get("nonce", 0),
            pubkey_hash=data.get("pubkey_hash", ""),
        )


@dataclass
class Transfer:
    """Transfer operation"""
    from_addr: str
    to: str
    amount: str
    fee: str
    nonce: int
    signature: Optional[str] = None

    def to_dict(self) -> Dict[str, Any]:
        result = {
            "from": self.from_addr,
            "to": self.to,
            "amount": self.amount,
            "fee": self.fee,
            "nonce": self.nonce,
        }
        if self.signature:
            result["signature"] = self.signature
        return result

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Transfer":
        return cls(
            from_addr=data["from"],
            to=data["to"],
            amount=data["amount"],
            fee=data["fee"],
            nonce=data.get("nonce", 0),
            signature=data.get("signature"),
        )


@dataclass
class Deposit:
    """Deposit operation (L1 -> L2)"""
    l1_tx_hash: str
    recipient: str
    amount: str
    token: Optional[str] = None

    def to_dict(self) -> Dict[str, Any]:
        result = {
            "l1_tx_hash": self.l1_tx_hash,
            "recipient": self.recipient,
            "amount": self.amount,
        }
        if self.token:
            result["token"] = self.token
        return result

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Deposit":
        return cls(
            l1_tx_hash=data["l1_tx_hash"],
            recipient=data["recipient"],
            amount=data["amount"],
            token=data.get("token"),
        )


@dataclass
class Withdrawal:
    """Withdrawal operation (L2 -> L1)"""
    sender: str
    recipient: str
    amount: str
    token: Optional[str] = None
    nonce: int = 0
    signature: Optional[str] = None

    def to_dict(self) -> Dict[str, Any]:
        result = {
            "sender": self.sender,
            "recipient": self.recipient,
            "amount": self.amount,
            "nonce": self.nonce,
        }
        if self.token:
            result["token"] = self.token
        if self.signature:
            result["signature"] = self.signature
        return result

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Withdrawal":
        return cls(
            sender=data["sender"],
            recipient=data["recipient"],
            amount=data["amount"],
            token=data.get("token"),
            nonce=data.get("nonce", 0),
            signature=data.get("signature"),
        )


@dataclass
class Batch:
    """Rollup batch"""
    batch_number: int
    prev_state_root: str
    new_state_root: str
    transfers: List[Transfer]
    deposits: List[Deposit]
    withdrawals: List[Withdrawal]
    proof: Optional[Proof] = None
    timestamp: int = 0

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Batch":
        return cls(
            batch_number=data["batch_number"],
            prev_state_root=data["prev_state_root"],
            new_state_root=data["new_state_root"],
            transfers=[Transfer.from_dict(t) for t in data.get("transfers", [])],
            deposits=[Deposit.from_dict(d) for d in data.get("deposits", [])],
            withdrawals=[Withdrawal.from_dict(w) for w in data.get("withdrawals", [])],
            proof=Proof.from_dict(data["proof"]) if data.get("proof") else None,
            timestamp=data.get("timestamp", 0),
        )


@dataclass
class RollupStats:
    """Rollup statistics"""
    current_batch: int
    total_transactions: int
    total_proofs: int
    avg_proof_time_ms: int
    state_root: str
    account_count: int
    tvl: str

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "RollupStats":
        return cls(
            current_batch=data["current_batch"],
            total_transactions=data.get("total_transactions", 0),
            total_proofs=data.get("total_proofs", 0),
            avg_proof_time_ms=data.get("avg_proof_time_ms", 0),
            state_root=data["state_root"],
            account_count=data.get("account_count", 0),
            tvl=data.get("tvl", "0"),
        )


@dataclass
class ProofRequest:
    """Proof generation request"""
    circuit_type: CircuitType
    public_inputs: List[str]
    private_inputs: List[str]
    system: Optional[ProofSystem] = None

    def to_dict(self) -> Dict[str, Any]:
        result = {
            "circuit_type": self.circuit_type.value,
            "public_inputs": self.public_inputs,
            "private_inputs": self.private_inputs,
        }
        if self.system:
            result["system"] = self.system.value
        return result


@dataclass
class VerificationResult:
    """Proof verification result"""
    valid: bool
    verification_time_ms: int
    error: Optional[str] = None

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "VerificationResult":
        return cls(
            valid=data["valid"],
            verification_time_ms=data.get("verification_time_ms", 0),
            error=data.get("error"),
        )


@dataclass
class MerkleProof:
    """Merkle proof for state inclusion"""
    leaf: str
    path: List[str]
    indices: List[int]
    root: str

    def to_dict(self) -> Dict[str, Any]:
        return {
            "leaf": self.leaf,
            "path": self.path,
            "indices": self.indices,
            "root": self.root,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "MerkleProof":
        return cls(
            leaf=data["leaf"],
            path=data["path"],
            indices=data["indices"],
            root=data["root"],
        )


@dataclass
class CeremonyContribution:
    """Trusted setup ceremony contribution"""
    contributor_id: str
    hash: str
    timestamp: int
    verified: bool

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "CeremonyContribution":
        return cls(
            contributor_id=data["contributor_id"],
            hash=data["hash"],
            timestamp=data.get("timestamp", 0),
            verified=data.get("verified", False),
        )


@dataclass
class TrustedSetup:
    """Trusted setup ceremony"""
    id: str
    circuit_type: CircuitType
    system: ProofSystem
    contribution_count: int
    latest_hash: str
    complete: bool
    contributions: List[CeremonyContribution]

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "TrustedSetup":
        return cls(
            id=data["id"],
            circuit_type=CircuitType(data["circuit_type"]),
            system=ProofSystem(data["system"]),
            contribution_count=data.get("contribution_count", 0),
            latest_hash=data.get("latest_hash", ""),
            complete=data.get("complete", False),
            contributions=[
                CeremonyContribution.from_dict(c)
                for c in data.get("contributions", [])
            ],
        )


@dataclass
class ZkConfig:
    """ZK SDK configuration"""
    api_key: str
    endpoint: str = "https://zk.synor.io/v1"
    ws_endpoint: str = "wss://zk.synor.io/v1/ws"
    timeout: int = 60  # Seconds (ZK proofs take longer)
    retries: int = 3
    default_proof_system: ProofSystem = ProofSystem.GROTH16
    debug: bool = False


class ZkError(Exception):
    """ZK SDK error"""

    def __init__(
        self,
        message: str,
        code: Optional[str] = None,
        status: Optional[int] = None,
    ):
        super().__init__(message)
        self.code = code
        self.status = status


# Default configuration
DEFAULT_CONFIG = {
    "endpoint": "https://zk.synor.io/v1",
    "ws_endpoint": "wss://zk.synor.io/v1/ws",
    "timeout": 60,
    "retries": 3,
    "default_proof_system": ProofSystem.GROTH16,
    "debug": False,
}

# Constants
MAX_BATCH_SIZE = 1000
STATE_TREE_DEPTH = 32
PROOF_EXPIRY_BLOCKS = 100

# Proof system characteristics
PROOF_SYSTEM_INFO = {
    ProofSystem.GROTH16: {
        "name": "Groth16",
        "proof_size": 192,
        "verification_time_ms": 10,
        "trusted_setup": True,
        "universal": False,
    },
    ProofSystem.PLONK: {
        "name": "PLONK",
        "proof_size": 512,
        "verification_time_ms": 15,
        "trusted_setup": True,
        "universal": True,
    },
    ProofSystem.STARK: {
        "name": "STARK",
        "proof_size": 50000,
        "verification_time_ms": 30,
        "trusted_setup": False,
        "universal": True,
    },
}
