//! Synor Wallet client implementation.

use crate::wallet::error::{Result, WalletError};
use crate::wallet::types::*;
use reqwest::Client;
use serde_json::{json, Value};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

/// Synor Wallet SDK client.
pub struct SynorWallet {
    config: WalletConfig,
    client: Client,
    closed: Arc<AtomicBool>,
}

impl SynorWallet {
    /// Create a new client with an API key.
    pub fn new(api_key: impl Into<String>) -> Self {
        Self::with_config(WalletConfig::new(api_key))
    }

    /// Create a new client with configuration.
    pub fn with_config(config: WalletConfig) -> Self {
        let client = Client::builder()
            .timeout(std::time::Duration::from_secs(config.timeout_secs))
            .build()
            .expect("Failed to create HTTP client");

        Self {
            config,
            client,
            closed: Arc::new(AtomicBool::new(false)),
        }
    }

    // ==================== Wallet Management ====================

    /// Create a new wallet.
    pub async fn create_wallet(&self, wallet_type: WalletType) -> Result<CreateWalletResult> {
        self.check_closed()?;

        let mut body = json!({
            "type": wallet_type,
            "network": self.config.network,
        });

        if let Some(ref path) = self.config.derivation_path {
            body["derivationPath"] = json!(path);
        }

        self.post("/wallets", body).await
    }

    /// Import a wallet from mnemonic phrase.
    pub async fn import_wallet(&self, options: ImportWalletOptions) -> Result<Wallet> {
        self.check_closed()?;

        let mut body = json!({
            "mnemonic": options.mnemonic,
            "type": options.wallet_type,
            "network": self.config.network,
        });

        if let Some(passphrase) = options.passphrase {
            body["passphrase"] = json!(passphrase);
        }

        if let Some(ref path) = self.config.derivation_path {
            body["derivationPath"] = json!(path);
        }

        let resp: Value = self.post("/wallets/import", body).await?;
        Ok(serde_json::from_value(resp["wallet"].clone())?)
    }

    /// Get a wallet by ID.
    pub async fn get_wallet(&self, wallet_id: &str) -> Result<Wallet> {
        self.check_closed()?;

        let resp: Value = self.get(&format!("/wallets/{}", wallet_id)).await?;
        Ok(serde_json::from_value(resp["wallet"].clone())?)
    }

    /// List all wallets for this account.
    pub async fn list_wallets(&self) -> Result<Vec<Wallet>> {
        self.check_closed()?;

        let resp: Value = self.get("/wallets").await?;
        let wallets = resp["wallets"]
            .as_array()
            .unwrap_or(&vec![])
            .iter()
            .filter_map(|w| serde_json::from_value(w.clone()).ok())
            .collect();

        Ok(wallets)
    }

    /// Get address at a specific index for a wallet.
    pub async fn get_address(&self, wallet_id: &str, index: u32) -> Result<String> {
        self.check_closed()?;

        let resp: Value = self
            .get(&format!("/wallets/{}/addresses/{}", wallet_id, index))
            .await?;

        Ok(resp["address"].as_str().unwrap_or_default().to_string())
    }

    /// Generate a stealth address for receiving private payments.
    pub async fn get_stealth_address(&self, wallet_id: &str) -> Result<StealthAddress> {
        self.check_closed()?;

        let resp: Value = self
            .post(&format!("/wallets/{}/stealth", wallet_id), json!({}))
            .await?;

        Ok(serde_json::from_value(resp["stealthAddress"].clone())?)
    }

    // ==================== Transaction Operations ====================

    /// Sign a transaction.
    pub async fn sign_transaction(
        &self,
        wallet_id: &str,
        transaction: &Transaction,
    ) -> Result<SignedTransaction> {
        self.check_closed()?;

        let body = json!({
            "transaction": transaction,
        });

        let resp: Value = self
            .post(&format!("/wallets/{}/sign", wallet_id), body)
            .await?;

        Ok(serde_json::from_value(resp["signedTransaction"].clone())?)
    }

    /// Sign a message.
    pub async fn sign_message(
        &self,
        wallet_id: &str,
        message: &str,
        format: Option<&str>,
    ) -> Result<SignedMessage> {
        self.check_closed()?;

        let body = json!({
            "message": message,
            "format": format.unwrap_or("text"),
        });

        self.post(&format!("/wallets/{}/sign-message", wallet_id), body)
            .await
    }

    /// Verify a signed message.
    pub async fn verify_message(
        &self,
        message: &str,
        signature: &str,
        address: &str,
    ) -> Result<bool> {
        self.check_closed()?;

        let body = json!({
            "message": message,
            "signature": signature,
            "address": address,
        });

        let resp: Value = self.post("/verify-message", body).await?;
        Ok(resp["valid"].as_bool().unwrap_or(false))
    }

    // ==================== Balance & UTXOs ====================

    /// Get balance for an address.
    pub async fn get_balance(&self, address: &str, include_tokens: bool) -> Result<BalanceResponse> {
        self.check_closed()?;

        let url = format!("/balances/{}?includeTokens={}", address, include_tokens);
        self.get(&url).await
    }

    /// Get UTXOs for an address.
    pub async fn get_utxos(&self, address: &str, options: Option<GetUtxosOptions>) -> Result<Vec<Utxo>> {
        self.check_closed()?;

        let mut url = format!("/utxos/{}", address);

        if let Some(opts) = options {
            let mut params = Vec::new();

            if let Some(min_conf) = opts.min_confirmations {
                params.push(format!("minConfirmations={}", min_conf));
            }

            if let Some(min_amount) = opts.min_amount {
                params.push(format!("minAmount={}", min_amount));
            }

            if !params.is_empty() {
                url.push('?');
                url.push_str(&params.join("&"));
            }
        }

        let resp: Value = self.get(&url).await?;
        let utxos = resp["utxos"]
            .as_array()
            .unwrap_or(&vec![])
            .iter()
            .filter_map(|u| serde_json::from_value(u.clone()).ok())
            .collect();

        Ok(utxos)
    }

    // ==================== Transaction Building ====================

    /// Build a transaction without signing.
    pub async fn build_transaction(
        &self,
        wallet_id: &str,
        options: BuildTransactionOptions,
    ) -> Result<Transaction> {
        self.check_closed()?;

        let mut body = json!({
            "to": options.to,
            "amount": options.amount,
        });

        if let Some(fee_rate) = options.fee_rate {
            body["feeRate"] = json!(fee_rate);
        }

        if let Some(utxos) = options.utxos {
            body["utxos"] = json!(utxos);
        }

        if let Some(change_address) = options.change_address {
            body["changeAddress"] = json!(change_address);
        }

        let resp: Value = self
            .post(&format!("/wallets/{}/build-tx", wallet_id), body)
            .await?;

        Ok(serde_json::from_value(resp["transaction"].clone())?)
    }

    /// Build and sign a transaction in one step.
    pub async fn send_transaction(
        &self,
        wallet_id: &str,
        options: BuildTransactionOptions,
    ) -> Result<SignedTransaction> {
        let tx = self.build_transaction(wallet_id, options).await?;
        self.sign_transaction(wallet_id, &tx).await
    }

    // ==================== Fee Estimation ====================

    /// Estimate transaction fee.
    pub async fn estimate_fee(&self, priority: Priority) -> Result<FeeEstimate> {
        self.check_closed()?;

        let url = format!("/fees/estimate?priority={:?}", priority).to_lowercase();
        self.get(&url).await
    }

    /// Get all fee estimates.
    pub async fn get_all_fee_estimates(&self) -> Result<Vec<FeeEstimate>> {
        self.check_closed()?;

        let resp: Value = self.get("/fees/estimate/all").await?;
        let estimates = resp["estimates"]
            .as_array()
            .unwrap_or(&vec![])
            .iter()
            .filter_map(|e| serde_json::from_value(e.clone()).ok())
            .collect();

        Ok(estimates)
    }

    // ==================== Lifecycle ====================

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

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

    // ==================== Internal Methods ====================

    fn check_closed(&self) -> Result<()> {
        if self.is_closed() {
            Err(WalletError::ClientClosed)
        } else {
            Ok(())
        }
    }

    async fn get<T: serde::de::DeserializeOwned>(&self, path: &str) -> Result<T> {
        let url = format!("{}{}", self.config.endpoint, path);

        if self.config.debug {
            println!("[SynorWallet] GET {}", path);
        }

        let response = self
            .client
            .get(&url)
            .header("Authorization", format!("Bearer {}", self.config.api_key))
            .header("X-Network", format!("{:?}", self.config.network).to_lowercase())
            .send()
            .await?;

        if !response.status().is_success() {
            let status_code = response.status().as_u16();
            let body: Value = response.json().await.unwrap_or_default();

            return Err(WalletError::Api {
                status_code,
                message: body["message"]
                    .as_str()
                    .unwrap_or("Request failed")
                    .to_string(),
                code: body["code"].as_str().map(String::from),
            });
        }

        Ok(response.json().await?)
    }

    async fn post<T: serde::de::DeserializeOwned>(&self, path: &str, body: Value) -> Result<T> {
        let url = format!("{}{}", self.config.endpoint, path);

        if self.config.debug {
            println!("[SynorWallet] POST {}", path);
        }

        let response = self
            .client
            .post(&url)
            .header("Authorization", format!("Bearer {}", self.config.api_key))
            .header("X-Network", format!("{:?}", self.config.network).to_lowercase())
            .json(&body)
            .send()
            .await?;

        if !response.status().is_success() {
            let status_code = response.status().as_u16();
            let body: Value = response.json().await.unwrap_or_default();

            return Err(WalletError::Api {
                status_code,
                message: body["message"]
                    .as_str()
                    .unwrap_or("Request failed")
                    .to_string(),
                code: body["code"].as_str().map(String::from),
            });
        }

        Ok(response.json().await?)
    }
}
