//! ZK SDK Types

use serde::{Deserialize, Serialize};
use std::fmt;

/// Proof system backends
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ProofSystem {
    /// Groth16 - smallest proofs (~192 bytes), fastest verification
    Groth16,
    /// PLONK - universal trusted setup, medium proofs (~512 bytes)
    Plonk,
    /// STARK - no trusted setup, largest proofs (~50KB)
    Stark,
}

impl Default for ProofSystem {
    fn default() -> Self {
        Self::Groth16
    }
}

/// Circuit types for different operations
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CircuitType {
    /// Single transfer between accounts
    Transfer,
    /// Batch of multiple transfers
    Batch,
    /// Deposit from L1 to L2
    Deposit,
    /// Withdrawal from L2 to L1
    Withdraw,
}

/// Rollup state
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum RollupState {
    Active,
    Paused,
    Finalizing,
    Finalized,
}

/// Proof status
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ProofStatus {
    Generating,
    Completed,
    Failed,
    Verified,
}

/// Zero-knowledge proof
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Proof {
    pub id: String,
    pub system: ProofSystem,
    pub circuit_type: CircuitType,
    /// Base64 encoded proof data
    pub data: String,
    pub public_inputs: Vec<String>,
    pub size: usize,
    pub generation_time_ms: u64,
    pub created_at: u64,
}

/// Verification key for a circuit
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerificationKey {
    pub circuit_id: String,
    pub circuit_type: CircuitType,
    pub system: ProofSystem,
    /// Base64 encoded verification key
    pub data: String,
    pub size: usize,
}

/// Proving key for generating proofs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProvingKey {
    pub circuit_id: String,
    pub circuit_type: CircuitType,
    pub system: ProofSystem,
    /// Base64 encoded proving key
    pub data: String,
    pub size: usize,
}

/// Circuit configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CircuitConfig {
    #[serde(rename = "type")]
    pub circuit_type: CircuitType,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_batch_size: Option<usize>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tree_depth: Option<usize>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub verify_signatures: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub custom_constraints: Option<serde_json::Value>,
}

/// Account state in the rollup
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccountState {
    pub address: String,
    pub balance: String,
    pub nonce: u64,
    pub pubkey_hash: String,
}

/// Transfer operation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Transfer {
    pub from: String,
    pub to: String,
    pub amount: String,
    pub fee: String,
    pub nonce: u64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub signature: Option<String>,
}

/// Deposit operation (L1 -> L2)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Deposit {
    pub l1_tx_hash: String,
    pub recipient: String,
    pub amount: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub token: Option<String>,
}

/// Withdrawal operation (L2 -> L1)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Withdrawal {
    pub sender: String,
    pub recipient: String,
    pub amount: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub token: Option<String>,
    pub nonce: u64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub signature: Option<String>,
}

/// Rollup batch
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Batch {
    pub batch_number: u64,
    pub prev_state_root: String,
    pub new_state_root: String,
    pub transfers: Vec<Transfer>,
    pub deposits: Vec<Deposit>,
    pub withdrawals: Vec<Withdrawal>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub proof: Option<Proof>,
    pub timestamp: u64,
}

/// Rollup statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RollupStats {
    pub current_batch: u64,
    pub total_transactions: u64,
    pub total_proofs: u64,
    pub avg_proof_time_ms: u64,
    pub state_root: String,
    pub account_count: u64,
    pub tvl: String,
}

/// Proof generation request
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProofRequest {
    pub circuit_type: CircuitType,
    pub public_inputs: Vec<String>,
    pub private_inputs: Vec<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub system: Option<ProofSystem>,
}

/// Proof verification result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerificationResult {
    pub valid: bool,
    pub verification_time_ms: u64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,
}

/// Merkle proof for state inclusion
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MerkleProof {
    pub leaf: String,
    pub path: Vec<String>,
    pub indices: Vec<u8>,
    pub root: String,
}

/// Trusted setup ceremony contribution
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CeremonyContribution {
    pub contributor_id: String,
    pub hash: String,
    pub timestamp: u64,
    pub verified: bool,
}

/// Trusted setup ceremony
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrustedSetup {
    pub id: String,
    pub circuit_type: CircuitType,
    pub system: ProofSystem,
    pub contribution_count: usize,
    pub latest_hash: String,
    pub complete: bool,
    pub contributions: Vec<CeremonyContribution>,
}

/// ZK SDK configuration
#[derive(Debug, Clone)]
pub struct ZkConfig {
    pub api_key: String,
    pub endpoint: String,
    pub ws_endpoint: String,
    pub timeout: std::time::Duration,
    pub retries: u32,
    pub default_proof_system: ProofSystem,
    pub debug: bool,
}

impl ZkConfig {
    pub fn new(api_key: impl Into<String>) -> Self {
        Self {
            api_key: api_key.into(),
            endpoint: "https://zk.synor.io/v1".to_string(),
            ws_endpoint: "wss://zk.synor.io/v1/ws".to_string(),
            timeout: std::time::Duration::from_secs(60),
            retries: 3,
            default_proof_system: ProofSystem::Groth16,
            debug: false,
        }
    }
}

/// ZK SDK error
#[derive(Debug)]
pub struct ZkError {
    pub message: String,
    pub code: Option<String>,
    pub status: Option<u16>,
}

impl fmt::Display for ZkError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.message)
    }
}

impl std::error::Error for ZkError {}

pub type ZkResult<T> = Result<T, ZkError>;

/// Proof system characteristics
pub struct ProofSystemInfo {
    pub name: &'static str,
    pub proof_size: usize,
    pub verification_time_ms: u64,
    pub trusted_setup: bool,
    pub universal: bool,
}

impl ProofSystem {
    pub fn info(&self) -> ProofSystemInfo {
        match self {
            ProofSystem::Groth16 => ProofSystemInfo {
                name: "Groth16",
                proof_size: 192,
                verification_time_ms: 10,
                trusted_setup: true,
                universal: false,
            },
            ProofSystem::Plonk => ProofSystemInfo {
                name: "PLONK",
                proof_size: 512,
                verification_time_ms: 15,
                trusted_setup: true,
                universal: true,
            },
            ProofSystem::Stark => ProofSystemInfo {
                name: "STARK",
                proof_size: 50000,
                verification_time_ms: 30,
                trusted_setup: false,
                universal: true,
            },
        }
    }
}

/// Constants
pub const MAX_BATCH_SIZE: usize = 1000;
pub const STATE_TREE_DEPTH: usize = 32;
pub const PROOF_EXPIRY_BLOCKS: u64 = 100;
