//! Synor Wallet SDK types.

use serde::{Deserialize, Serialize};

/// Network type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Network {
    Mainnet,
    Testnet,
}

impl Default for Network {
    fn default() -> Self {
        Self::Mainnet
    }
}

/// Wallet type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum WalletType {
    Standard,
    Multisig,
    Stealth,
    Hardware,
}

impl Default for WalletType {
    fn default() -> Self {
        Self::Standard
    }
}

/// Transaction priority.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Priority {
    Low,
    Medium,
    High,
    Urgent,
}

impl Default for Priority {
    fn default() -> Self {
        Self::Medium
    }
}

/// Wallet SDK configuration.
#[derive(Debug, Clone)]
pub struct WalletConfig {
    /// API key for authentication.
    pub api_key: String,
    /// API endpoint.
    pub endpoint: String,
    /// Network type.
    pub network: Network,
    /// Request timeout in seconds.
    pub timeout_secs: u64,
    /// Enable debug logging.
    pub debug: bool,
    /// BIP44 derivation path.
    pub derivation_path: Option<String>,
}

impl WalletConfig {
    /// Create a new configuration with an API key.
    pub fn new(api_key: impl Into<String>) -> Self {
        Self {
            api_key: api_key.into(),
            endpoint: "https://wallet.synor.cc/api/v1".to_string(),
            network: Network::default(),
            timeout_secs: 30,
            debug: false,
            derivation_path: None,
        }
    }

    /// Set the API endpoint.
    pub fn endpoint(mut self, endpoint: impl Into<String>) -> Self {
        self.endpoint = endpoint.into();
        self
    }

    /// Set the network.
    pub fn network(mut self, network: Network) -> Self {
        self.network = network;
        self
    }

    /// Set the timeout in seconds.
    pub fn timeout(mut self, secs: u64) -> Self {
        self.timeout_secs = secs;
        self
    }

    /// Enable debug mode.
    pub fn debug(mut self, debug: bool) -> Self {
        self.debug = debug;
        self
    }

    /// Set the derivation path.
    pub fn derivation_path(mut self, path: impl Into<String>) -> Self {
        self.derivation_path = Some(path.into());
        self
    }
}

/// Wallet instance.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Wallet {
    /// Unique wallet ID.
    pub id: String,
    /// Primary address.
    pub address: String,
    /// Compressed public key (hex).
    pub public_key: String,
    /// Wallet type.
    #[serde(rename = "type")]
    pub wallet_type: WalletType,
    /// Creation timestamp.
    pub created_at: i64,
}

/// Wallet creation result.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateWalletResult {
    /// Created wallet.
    pub wallet: Wallet,
    /// BIP39 mnemonic phrase (24 words).
    pub mnemonic: String,
}

/// Stealth address for private payments.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct StealthAddress {
    /// One-time address.
    pub address: String,
    /// View public key.
    pub view_key: String,
    /// Spend public key.
    pub spend_key: String,
    /// Ephemeral public key.
    pub ephemeral_key: Option<String>,
}

/// Transaction input.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionInput {
    /// Previous transaction hash.
    pub txid: String,
    /// Output index.
    pub vout: u32,
    /// Amount.
    pub amount: String,
    /// Script signature.
    pub script_sig: Option<String>,
}

/// Transaction output.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionOutput {
    /// Recipient address.
    pub address: String,
    /// Amount.
    pub amount: String,
    /// Script pubkey.
    pub script_pub_key: Option<String>,
}

/// Unsigned transaction.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Transaction {
    /// Version.
    pub version: u32,
    /// Inputs.
    pub inputs: Vec<TransactionInput>,
    /// Outputs.
    pub outputs: Vec<TransactionOutput>,
    /// Lock time.
    #[serde(default)]
    pub lock_time: u32,
    /// Fee.
    pub fee: Option<String>,
}

/// Signed transaction.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SignedTransaction {
    /// Raw hex-encoded transaction.
    pub raw: String,
    /// Transaction hash.
    pub txid: String,
    /// Transaction size in bytes.
    pub size: u32,
    /// Transaction weight.
    pub weight: Option<u32>,
}

/// Signed message.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SignedMessage {
    /// Signature (hex).
    pub signature: String,
    /// Public key used for signing.
    pub public_key: String,
    /// Address that signed.
    pub address: String,
}

/// Unspent Transaction Output.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Utxo {
    /// Transaction hash.
    pub txid: String,
    /// Output index.
    pub vout: u32,
    /// Amount.
    pub amount: String,
    /// Address.
    pub address: String,
    /// Number of confirmations.
    pub confirmations: u32,
    /// Script pubkey.
    pub script_pub_key: Option<String>,
}

/// Balance information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Balance {
    /// Confirmed balance.
    pub confirmed: String,
    /// Unconfirmed balance.
    pub unconfirmed: String,
    /// Total balance.
    pub total: String,
}

/// Token balance.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenBalance {
    /// Token contract address.
    pub token: String,
    /// Token symbol.
    pub symbol: String,
    /// Token decimals.
    pub decimals: u8,
    /// Balance.
    pub balance: String,
}

/// Full balance response.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BalanceResponse {
    /// Native token balance.
    pub native: Balance,
    /// Token balances.
    #[serde(default)]
    pub tokens: Vec<TokenBalance>,
}

/// Fee estimation result.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FeeEstimate {
    /// Priority level.
    pub priority: Priority,
    /// Fee rate.
    pub fee_rate: String,
    /// Estimated blocks until confirmation.
    pub estimated_blocks: u32,
}

/// Options for importing a wallet.
#[derive(Debug, Clone, Default)]
pub struct ImportWalletOptions {
    /// BIP39 mnemonic phrase.
    pub mnemonic: String,
    /// Optional passphrase.
    pub passphrase: Option<String>,
    /// Wallet type.
    pub wallet_type: WalletType,
}

impl ImportWalletOptions {
    /// Create new import options with a mnemonic.
    pub fn new(mnemonic: impl Into<String>) -> Self {
        Self {
            mnemonic: mnemonic.into(),
            passphrase: None,
            wallet_type: WalletType::default(),
        }
    }

    /// Set the passphrase.
    pub fn passphrase(mut self, passphrase: impl Into<String>) -> Self {
        self.passphrase = Some(passphrase.into());
        self
    }

    /// Set the wallet type.
    pub fn wallet_type(mut self, wallet_type: WalletType) -> Self {
        self.wallet_type = wallet_type;
        self
    }
}

/// Options for building a transaction.
#[derive(Debug, Clone, Default)]
pub struct BuildTransactionOptions {
    /// Recipient address.
    pub to: String,
    /// Amount to send.
    pub amount: String,
    /// Fee rate (satoshis per byte).
    pub fee_rate: Option<f64>,
    /// Specific UTXOs to use.
    pub utxos: Option<Vec<Utxo>>,
    /// Change address.
    pub change_address: Option<String>,
}

impl BuildTransactionOptions {
    /// Create new build options.
    pub fn new(to: impl Into<String>, amount: impl Into<String>) -> Self {
        Self {
            to: to.into(),
            amount: amount.into(),
            fee_rate: None,
            utxos: None,
            change_address: None,
        }
    }

    /// Set the fee rate.
    pub fn fee_rate(mut self, fee_rate: f64) -> Self {
        self.fee_rate = Some(fee_rate);
        self
    }

    /// Set specific UTXOs to use.
    pub fn utxos(mut self, utxos: Vec<Utxo>) -> Self {
        self.utxos = Some(utxos);
        self
    }

    /// Set the change address.
    pub fn change_address(mut self, address: impl Into<String>) -> Self {
        self.change_address = Some(address.into());
        self
    }
}

/// Options for querying UTXOs.
#[derive(Debug, Clone, Default)]
pub struct GetUtxosOptions {
    /// Minimum confirmations.
    pub min_confirmations: Option<u32>,
    /// Minimum amount.
    pub min_amount: Option<String>,
}

impl GetUtxosOptions {
    /// Set minimum confirmations.
    pub fn min_confirmations(mut self, confirmations: u32) -> Self {
        self.min_confirmations = Some(confirmations);
        self
    }

    /// Set minimum amount.
    pub fn min_amount(mut self, amount: impl Into<String>) -> Self {
        self.min_amount = Some(amount.into());
        self
    }
}
