//! Wallet commands.
//!
//! All wallets use Hybrid keys (Ed25519 + Dilithium) for quantum-resistant security.

use anyhow::Result;
use dialoguer::{Confirm, Input, Password};

use crate::client::RpcClient;
use crate::config::CliConfig;
use crate::output::{self, OutputFormat};
use crate::wallet::Wallet;
use crate::WalletCommands;

/// Handle wallet commands.
pub async fn handle(config: &CliConfig, cmd: WalletCommands, format: OutputFormat) -> Result<()> {
    match cmd {
        WalletCommands::Create { name } => create(config, &name, format).await,
        WalletCommands::Import { name } => import(config, &name, format).await,
        WalletCommands::Export { name } => export(config, &name, format).await,
        WalletCommands::List => list(config, format).await,
        WalletCommands::Info { name } => info(config, &name, format).await,
        WalletCommands::NewAddress { name } => new_address(config, &name, format).await,
        WalletCommands::Addresses { name } => addresses(config, &name, format).await,
    }
}

/// Create a new wallet with Hybrid keys.
async fn create(config: &CliConfig, name: &str, format: OutputFormat) -> Result<()> {
    let wallet_path = config.wallet_path(name);

    if wallet_path.exists()
        && !Confirm::new()
            .with_prompt("Wallet already exists. Overwrite?")
            .default(false)
            .interact()?
    {
        return Ok(());
    }

    // Get password for wallet encryption
    let password: String = Password::new()
        .with_prompt("Enter wallet password")
        .with_confirmation("Confirm password", "Passwords don't match")
        .interact()?;

    if password.len() < 8 {
        anyhow::bail!("Password must be at least 8 characters");
    }

    // All wallets use Hybrid keys (Ed25519 + Dilithium)
    let (wallet, seed_phrase) = Wallet::create(name, &config.network, &password)?;
    wallet.save(&wallet_path)?;

    match format {
        OutputFormat::Json => {
            let result = serde_json::json!({
                "name": wallet.name,
                "address": wallet.default_address().map(|a| &a.address),
                "key_type": "Hybrid (Ed25519 + Dilithium)",
                "seed_phrase": seed_phrase,
            });
            println!("{}", serde_json::to_string_pretty(&result)?);
        }
        OutputFormat::Text => {
            output::print_success("Wallet created with Hybrid keys (Ed25519 + Dilithium)!");
            output::print_kv("Name", &wallet.name);
            output::print_kv("Network", &wallet.network);
            output::print_kv(
                "Address",
                wallet
                    .default_address()
                    .map(|a| a.address.as_str())
                    .unwrap_or("none"),
            );
            output::print_kv("Key Type", "Hybrid (Ed25519 + Dilithium)");

            println!();
            output::print_warning("IMPORTANT: Write down your seed phrase and keep it safe!");
            output::print_warning("You will need this to recover your wallet.");
            println!();
            println!("  {}", seed_phrase);
            println!();
        }
    }

    Ok(())
}

/// Import wallet from seed phrase.
async fn import(config: &CliConfig, name: &str, format: OutputFormat) -> Result<()> {
    let wallet_path = config.wallet_path(name);

    if wallet_path.exists()
        && !Confirm::new()
            .with_prompt("Wallet already exists. Overwrite?")
            .default(false)
            .interact()?
    {
        return Ok(());
    }

    let seed_phrase: String = Input::new()
        .with_prompt("Enter seed phrase (12 or 24 words)")
        .interact_text()?;

    // Get password for wallet encryption
    let password: String = Password::new()
        .with_prompt("Enter wallet password")
        .with_confirmation("Confirm password", "Passwords don't match")
        .interact()?;

    if password.len() < 8 {
        anyhow::bail!("Password must be at least 8 characters");
    }

    // All wallets use Hybrid keys (Ed25519 + Dilithium)
    let wallet = Wallet::import(name, &config.network, &seed_phrase, &password)?;
    wallet.save(&wallet_path)?;

    match format {
        OutputFormat::Json => {
            let result = serde_json::json!({
                "name": wallet.name,
                "address": wallet.default_address().map(|a| &a.address),
                "key_type": "Hybrid (Ed25519 + Dilithium)",
            });
            println!("{}", serde_json::to_string_pretty(&result)?);
        }
        OutputFormat::Text => {
            output::print_success("Wallet imported with Hybrid keys!");
            output::print_kv("Name", &wallet.name);
            output::print_kv(
                "Address",
                wallet
                    .default_address()
                    .map(|a| a.address.as_str())
                    .unwrap_or("none"),
            );
        }
    }

    Ok(())
}

/// Export wallet seed phrase.
async fn export(config: &CliConfig, name: &str, format: OutputFormat) -> Result<()> {
    let wallet_path = config.wallet_path(name);

    if !wallet_path.exists() {
        anyhow::bail!("Wallet '{}' not found", name);
    }

    let password: String = Password::new()
        .with_prompt("Enter wallet password")
        .interact()?;

    let wallet = Wallet::load(&wallet_path)?;

    // Verify password
    wallet.unlock(&password)?;

    // Note: We can't export the mnemonic from the derived seed
    // The user should have written down the mnemonic during creation
    match wallet.export_seed_phrase(&password) {
        Ok(seed_phrase) => match format {
            OutputFormat::Json => {
                let result = serde_json::json!({
                    "name": wallet.name,
                    "seed_phrase": seed_phrase,
                });
                println!("{}", serde_json::to_string_pretty(&result)?);
            }
            OutputFormat::Text => {
                output::print_warning("Keep this seed phrase secret and safe!");
                println!();
                println!("  {}", seed_phrase);
                println!();
            }
        },
        Err(e) => {
            output::print_warning(&format!("{}", e));
            output::print_info(
                "Please use the mnemonic phrase you wrote down during wallet creation.",
            );
        }
    }

    Ok(())
}

/// List wallets.
async fn list(config: &CliConfig, format: OutputFormat) -> Result<()> {
    let wallets = crate::wallet::list_wallets(&config.wallet_dir)?;

    match format {
        OutputFormat::Json => {
            output::print_value(&wallets, format);
        }
        OutputFormat::Text => {
            if wallets.is_empty() {
                output::print_info("No wallets found. Create one with: synor wallet create");
            } else {
                output::print_header(&format!("Wallets ({})", wallets.len()));
                for name in &wallets {
                    let is_default = name == &config.default_wallet;
                    if is_default {
                        println!("  {} (default)", name);
                    } else {
                        println!("  {}", name);
                    }
                }
            }
        }
    }

    Ok(())
}

/// Get wallet info.
async fn info(config: &CliConfig, name: &str, format: OutputFormat) -> Result<()> {
    let wallet_path = config.wallet_path(name);

    if !wallet_path.exists() {
        anyhow::bail!("Wallet '{}' not found", name);
    }

    let wallet = Wallet::load(&wallet_path)?;

    match format {
        OutputFormat::Json => {
            output::print_value(&wallet, format);
        }
        OutputFormat::Text => {
            output::print_header(&format!("Wallet: {}", wallet.name));
            output::print_kv("Network", &wallet.network);
            output::print_kv("Key Type", "Hybrid (Ed25519 + Dilithium)");
            output::print_kv("Addresses", &wallet.addresses.len().to_string());
            output::print_kv(
                "Created",
                &output::format_timestamp(wallet.created_at * 1000),
            );

            if let Some(default) = wallet.default_address() {
                output::print_kv("Default Address", &default.address);
            }
        }
    }

    Ok(())
}

/// Generate new address.
async fn new_address(config: &CliConfig, name: &str, format: OutputFormat) -> Result<()> {
    let wallet_path = config.wallet_path(name);

    if !wallet_path.exists() {
        anyhow::bail!("Wallet '{}' not found", name);
    }

    let password: String = Password::new()
        .with_prompt("Enter wallet password")
        .interact()?;

    let mut wallet = Wallet::load(&wallet_path)?;

    // Verify password
    wallet.unlock(&password)?;

    let label: String = Input::new()
        .with_prompt("Label (optional)")
        .allow_empty(true)
        .interact_text()?;

    let label = if label.is_empty() { None } else { Some(label) };

    let addr = wallet.new_address(label, &password)?;
    let address = addr.address.clone();

    wallet.save(&wallet_path)?;

    match format {
        OutputFormat::Json => {
            let result = serde_json::json!({
                "address": address,
            });
            println!("{}", serde_json::to_string_pretty(&result)?);
        }
        OutputFormat::Text => {
            output::print_success("New address generated!");
            output::print_kv("Address", &address);
        }
    }

    Ok(())
}

/// List addresses.
async fn addresses(config: &CliConfig, name: &str, format: OutputFormat) -> Result<()> {
    let wallet_path = config.wallet_path(name);

    if !wallet_path.exists() {
        anyhow::bail!("Wallet '{}' not found", name);
    }

    let wallet = Wallet::load(&wallet_path)?;

    match format {
        OutputFormat::Json => {
            let addrs: Vec<_> = wallet.all_addresses().iter().map(|a| &a.address).collect();
            output::print_value(&addrs, format);
        }
        OutputFormat::Text => {
            output::print_header(&format!("Addresses ({})", wallet.addresses.len()));

            let headers = vec!["#", "Address", "Label", "Default"];
            let rows: Vec<Vec<String>> = wallet
                .all_addresses()
                .iter()
                .enumerate()
                .map(|(i, a)| {
                    vec![
                        i.to_string(),
                        a.address.clone(),
                        a.label.clone().unwrap_or_default(),
                        if a.is_default { "✓" } else { "" }.to_string(),
                    ]
                })
                .collect();

            output::print_table(headers, rows);
        }
    }

    Ok(())
}

/// Get balance.
pub async fn balance(
    client: &RpcClient,
    config: &CliConfig,
    address: Option<&str>,
    format: OutputFormat,
) -> Result<()> {
    let addr = if let Some(a) = address {
        a.to_string()
    } else {
        let wallet_path = config.default_wallet_path();
        if !wallet_path.exists() {
            anyhow::bail!("No wallet found. Create one or specify an address.");
        }
        let wallet = Wallet::load(&wallet_path)?;
        wallet
            .default_address()
            .map(|a| a.address.clone())
            .ok_or_else(|| anyhow::anyhow!("No address in wallet"))?
    };

    let balance = client.get_balance(&addr).await?;

    match format {
        OutputFormat::Json => {
            output::print_value(&balance, format);
        }
        OutputFormat::Text => {
            output::print_kv("Address", &addr);
            output::print_kv("Confirmed", &output::format_synor(balance.confirmed));
            output::print_kv("Unconfirmed", &output::format_synor(balance.unconfirmed));
            output::print_kv("Total", &output::format_synor(balance.total));
        }
    }

    Ok(())
}

/// Get UTXOs.
pub async fn utxos(client: &RpcClient, address: &str, format: OutputFormat) -> Result<()> {
    let utxos = client.get_utxos(address).await?;

    match format {
        OutputFormat::Json => {
            output::print_value(&utxos, format);
        }
        OutputFormat::Text => {
            if utxos.is_empty() {
                output::print_info("No UTXOs found");
                return Ok(());
            }

            output::print_header(&format!("UTXOs ({})", utxos.len()));

            let headers = vec!["Outpoint", "Amount", "Coinbase"];
            let rows: Vec<Vec<String>> = utxos
                .iter()
                .map(|u| {
                    vec![
                        format!(
                            "{}:{}",
                            output::format_hash(&u.outpoint.transaction_id),
                            u.outpoint.index
                        ),
                        output::format_synor(u.amount),
                        if u.is_coinbase { "Yes" } else { "No" }.to_string(),
                    ]
                })
                .collect();

            output::print_table(headers, rows);

            let total: u64 = utxos.iter().map(|u| u.amount).sum();
            println!("\nTotal: {}", output::format_synor(total));
        }
    }

    Ok(())
}
