//! ZK SDK Client

use crate::zk::types::*;
use base64::{Engine as _, engine::general_purpose::STANDARD};
use reqwest::Client;
use serde::{de::DeserializeOwned, Serialize};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

/// Synor ZK SDK Client
pub struct SynorZk {
    config: ZkConfig,
    client: Client,
    closed: Arc<AtomicBool>,
}

impl SynorZk {
    /// Create a new ZK SDK client
    pub fn new(config: ZkConfig) -> Self {
        Self {
            config,
            client: Client::new(),
            closed: Arc::new(AtomicBool::new(false)),
        }
    }

    /// Get the default proof system
    pub fn default_proof_system(&self) -> ProofSystem {
        self.config.default_proof_system
    }

    /// Health check
    pub async fn health_check(&self) -> bool {
        match self.get::<serde_json::Value>("/health").await {
            Ok(v) => v.get("status").and_then(|s| s.as_str()) == Some("healthy"),
            Err(_) => false,
        }
    }

    /// Get service info
    pub async fn get_info(&self) -> ZkResult<serde_json::Value> {
        self.get("/info").await
    }

    /// Close the client
    pub fn close(&self) {
        self.closed.store(true, Ordering::SeqCst);
    }

    /// Check if client is closed
    pub fn is_closed(&self) -> bool {
        self.closed.load(Ordering::SeqCst)
    }

    // ========================================================================
    // Proof Operations
    // ========================================================================

    /// Generate a zero-knowledge proof
    pub async fn generate_proof(&self, request: ProofRequest) -> ZkResult<Proof> {
        let system = request.system.unwrap_or(self.default_proof_system());
        self.post("/proofs/generate", &serde_json::json!({
            "circuit_type": request.circuit_type,
            "public_inputs": request.public_inputs,
            "private_inputs": request.private_inputs,
            "system": system,
        })).await
    }

    /// Verify a proof
    pub async fn verify_proof(&self, proof: &Proof) -> ZkResult<VerificationResult> {
        self.post("/proofs/verify", &serde_json::json!({
            "proof_id": proof.id,
            "data": proof.data,
            "public_inputs": proof.public_inputs,
            "system": proof.system,
            "circuit_type": proof.circuit_type,
        })).await
    }

    /// Get a proof by ID
    pub async fn get_proof(&self, proof_id: &str) -> ZkResult<Proof> {
        self.get(&format!("/proofs/{}", proof_id)).await
    }

    /// List recent proofs
    pub async fn list_proofs(&self, limit: Option<usize>, offset: Option<usize>) -> ZkResult<Vec<Proof>> {
        let mut path = "/proofs".to_string();
        if limit.is_some() || offset.is_some() {
            path = format!("/proofs?limit={}&offset={}", limit.unwrap_or(10), offset.unwrap_or(0));
        }
        #[derive(Deserialize)]
        struct Response { proofs: Vec<Proof> }
        let resp: Response = self.get(&path).await?;
        Ok(resp.proofs)
    }

    /// Serialize a proof to bytes
    pub async fn serialize_proof(&self, proof: &Proof) -> ZkResult<Vec<u8>> {
        #[derive(Deserialize)]
        struct Response { data: String }
        let resp: Response = self.post("/proofs/serialize", &serde_json::json!({
            "proof_id": proof.id,
        })).await?;
        STANDARD.decode(&resp.data).map_err(|e| ZkError {
            message: e.to_string(),
            code: None,
            status: None,
        })
    }

    /// Deserialize bytes to a proof
    pub async fn deserialize_proof(&self, data: &[u8], system: ProofSystem) -> ZkResult<Proof> {
        self.post("/proofs/deserialize", &serde_json::json!({
            "data": STANDARD.encode(data),
            "system": system,
        })).await
    }

    // ========================================================================
    // Circuit Operations
    // ========================================================================

    /// Get verification key for a circuit
    pub async fn get_verification_key(&self, circuit_type: CircuitType, system: Option<ProofSystem>) -> ZkResult<VerificationKey> {
        let sys = system.unwrap_or(self.default_proof_system());
        self.get(&format!("/circuits/{:?}/vk?system={:?}", circuit_type, sys).to_lowercase()).await
    }

    /// Get proving key for a circuit
    pub async fn get_proving_key(&self, circuit_type: CircuitType, system: Option<ProofSystem>) -> ZkResult<ProvingKey> {
        let sys = system.unwrap_or(self.default_proof_system());
        self.get(&format!("/circuits/{:?}/pk?system={:?}", circuit_type, sys).to_lowercase()).await
    }

    /// List available circuits
    pub async fn list_circuits(&self) -> ZkResult<Vec<CircuitConfig>> {
        #[derive(Deserialize)]
        struct Response { circuits: Vec<CircuitConfig> }
        let resp: Response = self.get("/circuits").await?;
        Ok(resp.circuits)
    }

    /// Get circuit configuration
    pub async fn get_circuit_config(&self, circuit_type: CircuitType) -> ZkResult<CircuitConfig> {
        self.get(&format!("/circuits/{:?}/config", circuit_type).to_lowercase()).await
    }

    /// Compile a custom circuit
    pub async fn compile_circuit(&self, config: CircuitConfig) -> ZkResult<String> {
        #[derive(Deserialize)]
        struct Response { circuit_id: String }
        let resp: Response = self.post("/circuits/compile", &config).await?;
        Ok(resp.circuit_id)
    }

    // ========================================================================
    // Rollup Operations
    // ========================================================================

    /// Get rollup statistics
    pub async fn get_rollup_stats(&self) -> ZkResult<RollupStats> {
        self.get("/rollup/stats").await
    }

    /// Get current batch
    pub async fn get_current_batch(&self) -> ZkResult<Batch> {
        self.get("/rollup/batch/current").await
    }

    /// Get batch by number
    pub async fn get_batch(&self, batch_number: u64) -> ZkResult<Batch> {
        self.get(&format!("/rollup/batch/{}", batch_number)).await
    }

    /// Submit a transfer
    pub async fn submit_transfer(&self, transfer: Transfer) -> ZkResult<String> {
        #[derive(Deserialize)]
        struct Response { tx_id: String }
        let resp: Response = self.post("/rollup/transfer", &transfer).await?;
        Ok(resp.tx_id)
    }

    /// Submit a deposit
    pub async fn submit_deposit(&self, deposit: Deposit) -> ZkResult<String> {
        #[derive(Deserialize)]
        struct Response { tx_id: String }
        let resp: Response = self.post("/rollup/deposit", &deposit).await?;
        Ok(resp.tx_id)
    }

    /// Submit a withdrawal
    pub async fn submit_withdrawal(&self, withdrawal: Withdrawal) -> ZkResult<String> {
        #[derive(Deserialize)]
        struct Response { tx_id: String }
        let resp: Response = self.post("/rollup/withdraw", &withdrawal).await?;
        Ok(resp.tx_id)
    }

    /// Finalize current batch
    pub async fn finalize_batch(&self) -> ZkResult<Batch> {
        self.post("/rollup/batch/finalize", &()).await
    }

    /// Get pending transactions
    pub async fn get_pending_transactions(&self) -> ZkResult<Vec<Transfer>> {
        #[derive(Deserialize)]
        struct Response { transactions: Vec<Transfer> }
        let resp: Response = self.get("/rollup/pending").await?;
        Ok(resp.transactions)
    }

    // ========================================================================
    // State Operations
    // ========================================================================

    /// Get current state root
    pub async fn get_state_root(&self) -> ZkResult<String> {
        #[derive(Deserialize)]
        struct Response { root: String }
        let resp: Response = self.get("/state/root").await?;
        Ok(resp.root)
    }

    /// Get account state
    pub async fn get_account(&self, address: &str) -> ZkResult<AccountState> {
        self.get(&format!("/state/account/{}", address)).await
    }

    /// Get merkle proof for account inclusion
    pub async fn get_merkle_proof(&self, address: &str) -> ZkResult<MerkleProof> {
        self.get(&format!("/state/proof/{}", address)).await
    }

    /// Verify merkle proof
    pub async fn verify_merkle_proof(&self, proof: &MerkleProof) -> ZkResult<bool> {
        #[derive(Deserialize)]
        struct Response { valid: bool }
        let resp: Response = self.post("/state/proof/verify", proof).await?;
        Ok(resp.valid)
    }

    /// Get state at specific batch
    pub async fn get_state_at_batch(&self, batch_number: u64) -> ZkResult<serde_json::Value> {
        self.get(&format!("/state/batch/{}", batch_number)).await
    }

    // ========================================================================
    // Ceremony Operations
    // ========================================================================

    /// Get ceremony status
    pub async fn get_ceremony_status(&self, circuit_type: CircuitType, system: Option<ProofSystem>) -> ZkResult<TrustedSetup> {
        let sys = system.unwrap_or(self.default_proof_system());
        self.get(&format!("/ceremony/{:?}?system={:?}", circuit_type, sys).to_lowercase()).await
    }

    /// Contribute to ceremony
    pub async fn contribute_to_ceremony(
        &self,
        circuit_type: CircuitType,
        entropy: &[u8],
        system: Option<ProofSystem>,
    ) -> ZkResult<CeremonyContribution> {
        let sys = system.unwrap_or(self.default_proof_system());
        self.post("/ceremony/contribute", &serde_json::json!({
            "circuit_type": circuit_type,
            "entropy": STANDARD.encode(entropy),
            "system": sys,
        })).await
    }

    /// Verify a contribution
    pub async fn verify_contribution(
        &self,
        circuit_type: CircuitType,
        contribution_hash: &str,
    ) -> ZkResult<bool> {
        #[derive(Deserialize)]
        struct Response { valid: bool }
        let resp: Response = self.post("/ceremony/verify", &serde_json::json!({
            "circuit_type": circuit_type,
            "contribution_hash": contribution_hash,
        })).await?;
        Ok(resp.valid)
    }

    /// List contributions
    pub async fn list_contributions(&self, circuit_type: CircuitType) -> ZkResult<Vec<CeremonyContribution>> {
        #[derive(Deserialize)]
        struct Response { contributions: Vec<CeremonyContribution> }
        let resp: Response = self.get(&format!("/ceremony/{:?}/contributions", circuit_type).to_lowercase()).await?;
        Ok(resp.contributions)
    }

    // ========================================================================
    // HTTP Methods
    // ========================================================================

    async fn get<T: DeserializeOwned>(&self, path: &str) -> ZkResult<T> {
        self.request("GET", path, Option::<&()>::None).await
    }

    async fn post<T: DeserializeOwned, B: Serialize>(&self, path: &str, body: &B) -> ZkResult<T> {
        self.request("POST", path, Some(body)).await
    }

    async fn request<T: DeserializeOwned, B: Serialize>(
        &self,
        method: &str,
        path: &str,
        body: Option<&B>,
    ) -> ZkResult<T> {
        if self.is_closed() {
            return Err(ZkError {
                message: "Client has been closed".to_string(),
                code: Some("CLIENT_CLOSED".to_string()),
                status: None,
            });
        }

        let url = format!("{}{}", self.config.endpoint, path);
        let mut last_error = None;

        for attempt in 0..=self.config.retries {
            let mut req = match method {
                "GET" => self.client.get(&url),
                "POST" => self.client.post(&url),
                "DELETE" => self.client.delete(&url),
                _ => unreachable!(),
            };

            req = req
                .header("Content-Type", "application/json")
                .header("Authorization", format!("Bearer {}", self.config.api_key))
                .header("X-SDK-Version", "rust/0.1.0")
                .timeout(self.config.timeout);

            if let Some(b) = body {
                req = req.json(b);
            }

            match req.send().await {
                Ok(resp) => {
                    let status = resp.status();
                    if status.is_success() {
                        return resp.json().await.map_err(|e| ZkError {
                            message: e.to_string(),
                            code: None,
                            status: None,
                        });
                    } else {
                        let error: serde_json::Value = resp.json().await.unwrap_or_default();
                        return Err(ZkError {
                            message: error["message"].as_str().unwrap_or(&format!("HTTP {}", status)).to_string(),
                            code: error["code"].as_str().map(String::from),
                            status: Some(status.as_u16()),
                        });
                    }
                }
                Err(e) => {
                    last_error = Some(e.to_string());
                    if attempt < self.config.retries {
                        tokio::time::sleep(std::time::Duration::from_millis(100 * 2u64.pow(attempt))).await;
                    }
                }
            }
        }

        Err(ZkError {
            message: last_error.unwrap_or_else(|| "Request failed".to_string()),
            code: Some("NETWORK_ERROR".to_string()),
            status: None,
        })
    }
}
