//! Tauri commands for the desktop wallet
//!
//! All commands are async and return Result<T, Error> which Tauri
//! serializes as { ok: T } or { error: string } to the frontend.

use serde::{Deserialize, Serialize};
use tauri::{AppHandle, Manager, State};

use crate::wallet::{WalletState, WalletAddress, NetworkConnection};
use crate::{Error, Result};

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

/// Response from wallet creation
#[derive(Debug, Serialize)]
pub struct CreateWalletResponse {
    pub mnemonic: String,
    pub address: String,
}

/// Create a new wallet with a random mnemonic
#[tauri::command]
pub async fn create_wallet(
    app: AppHandle,
    state: State<'_, WalletState>,
    password: String,
) -> Result<CreateWalletResponse> {
    // Validate password strength
    if password.len() < 8 {
        return Err(Error::Crypto("Password must be at least 8 characters".to_string()));
    }

    // Set up data directory
    let app_data_dir = app.path().app_data_dir()
        .map_err(|e| Error::Internal(format!("Failed to get app data dir: {}", e)))?;
    state.set_data_dir(app_data_dir).await?;

    // Check if wallet already exists
    if state.wallet_exists().await {
        return Err(Error::Internal("Wallet already exists. Import or unlock instead.".to_string()));
    }

    // Create wallet with encryption (testnet by default for safety)
    let (mnemonic, address) = state.create(&password, true).await?;

    Ok(CreateWalletResponse { mnemonic, address })
}

/// Import request
#[derive(Debug, Deserialize)]
pub struct ImportWalletRequest {
    pub mnemonic: String,
    pub password: String,
}

/// Import a wallet from mnemonic phrase
#[tauri::command]
pub async fn import_wallet(
    app: AppHandle,
    state: State<'_, WalletState>,
    request: ImportWalletRequest,
) -> Result<String> {
    // Validate password strength
    if request.password.len() < 8 {
        return Err(Error::Crypto("Password must be at least 8 characters".to_string()));
    }

    // Set up data directory
    let app_data_dir = app.path().app_data_dir()
        .map_err(|e| Error::Internal(format!("Failed to get app data dir: {}", e)))?;
    state.set_data_dir(app_data_dir).await?;

    // Import wallet with encryption (testnet by default for safety)
    let address = state.import(&request.mnemonic, &request.password, true).await?;

    Ok(address)
}

/// Unlock an existing wallet
#[tauri::command]
pub async fn unlock_wallet(
    app: AppHandle,
    state: State<'_, WalletState>,
    password: String,
) -> Result<bool> {
    // Set up data directory
    let app_data_dir = app.path().app_data_dir()
        .map_err(|e| Error::Internal(format!("Failed to get app data dir: {}", e)))?;
    state.set_data_dir(app_data_dir).await?;

    // Load wallet metadata from file
    state.load_metadata().await?;

    // Decrypt and unlock
    state.unlock(&password).await?;

    Ok(true)
}

/// Lock the wallet (clear sensitive data from memory)
#[tauri::command]
pub async fn lock_wallet(state: State<'_, WalletState>) -> Result<()> {
    state.lock().await;
    Ok(())
}

/// Wallet info returned to frontend
#[derive(Debug, Serialize)]
pub struct WalletInfo {
    pub locked: bool,
    pub address_count: usize,
    pub network: Option<String>,
}

/// Get wallet information
#[tauri::command]
pub async fn get_wallet_info(state: State<'_, WalletState>) -> Result<WalletInfo> {
    let locked = !state.is_unlocked().await;
    let addresses = state.addresses.read().await;
    let connection = state.connection.read().await;

    Ok(WalletInfo {
        locked,
        address_count: addresses.len(),
        network: connection.as_ref().map(|c| c.network.clone()),
    })
}

/// Export the mnemonic phrase (requires unlock)
#[tauri::command]
pub async fn export_mnemonic(
    state: State<'_, WalletState>,
    password: String,
) -> Result<String> {
    if !state.is_unlocked().await {
        return Err(Error::WalletLocked);
    }
    // TODO: Re-verify password and return mnemonic
    Err(Error::Internal("Not implemented".to_string()))
}

// ============================================================================
// Address & Balance Commands
// ============================================================================

/// Get all addresses in the wallet
#[tauri::command]
pub async fn get_addresses(state: State<'_, WalletState>) -> Result<Vec<WalletAddress>> {
    let addresses = state.addresses.read().await;
    Ok(addresses.clone())
}

/// Generate a new receive address
#[tauri::command]
pub async fn generate_address(
    state: State<'_, WalletState>,
    label: Option<String>,
) -> Result<WalletAddress> {
    // Use wallet state's generate_address which handles crypto properly
    state.generate_address(label, false).await
}

/// Balance response
#[derive(Debug, Serialize)]
pub struct BalanceResponse {
    /// Balance in sompi (1 SYN = 100_000_000 sompi)
    pub balance: u64,
    /// Human-readable balance
    pub balance_human: String,
    /// Pending incoming
    pub pending: u64,
}

/// Get wallet balance
#[tauri::command]
pub async fn get_balance(state: State<'_, WalletState>) -> Result<BalanceResponse> {
    // TODO: Query node for UTXOs and sum balance
    Ok(BalanceResponse {
        balance: 0,
        balance_human: "0 SYN".to_string(),
        pending: 0,
    })
}

/// UTXO response
#[derive(Debug, Serialize)]
pub struct UtxoResponse {
    pub txid: String,
    pub vout: u32,
    pub amount: u64,
    pub confirmations: u64,
}

/// Get UTXOs for the wallet
#[tauri::command]
pub async fn get_utxos(state: State<'_, WalletState>) -> Result<Vec<UtxoResponse>> {
    // TODO: Query node for UTXOs
    Ok(vec![])
}

// ============================================================================
// Transaction Commands
// ============================================================================

/// Transaction creation request
#[derive(Debug, Deserialize)]
pub struct CreateTransactionRequest {
    pub to: String,
    pub amount: u64,
    pub fee: Option<u64>,
    pub use_dilithium: bool,
}

/// Unsigned transaction response
#[derive(Debug, Serialize)]
pub struct UnsignedTransaction {
    pub tx_hex: String,
    pub fee: u64,
    pub inputs: Vec<UtxoResponse>,
}

/// Create an unsigned transaction
#[tauri::command]
pub async fn create_transaction(
    state: State<'_, WalletState>,
    request: CreateTransactionRequest,
) -> Result<UnsignedTransaction> {
    if !state.is_unlocked().await {
        return Err(Error::WalletLocked);
    }

    // TODO: Select UTXOs, build transaction
    Err(Error::Internal("Not implemented".to_string()))
}

/// Signed transaction response
#[derive(Debug, Serialize)]
pub struct SignedTransaction {
    pub tx_hex: String,
    pub txid: String,
}

/// Sign a transaction
#[tauri::command]
pub async fn sign_transaction(
    state: State<'_, WalletState>,
    tx_hex: String,
) -> Result<SignedTransaction> {
    if !state.is_unlocked().await {
        return Err(Error::WalletLocked);
    }

    // TODO: Sign transaction with appropriate key
    Err(Error::Internal("Not implemented".to_string()))
}

/// Broadcast response
#[derive(Debug, Serialize)]
pub struct BroadcastResponse {
    pub txid: String,
    pub accepted: bool,
}

/// Broadcast a signed transaction
#[tauri::command]
pub async fn broadcast_transaction(
    state: State<'_, WalletState>,
    tx_hex: String,
) -> Result<BroadcastResponse> {
    let connection = state.connection.read().await;
    if connection.is_none() {
        return Err(Error::Network("Not connected to node".to_string()));
    }

    // TODO: Submit transaction via RPC
    Err(Error::Internal("Not implemented".to_string()))
}

/// Transaction history entry
#[derive(Debug, Serialize)]
pub struct TransactionHistoryEntry {
    pub txid: String,
    pub direction: String, // "sent" or "received"
    pub amount: u64,
    pub fee: Option<u64>,
    pub timestamp: i64,
    pub confirmations: u64,
    pub counterparty: Option<String>,
}

/// Get transaction history
#[tauri::command]
pub async fn get_transaction_history(
    state: State<'_, WalletState>,
    limit: Option<usize>,
) -> Result<Vec<TransactionHistoryEntry>> {
    // TODO: Query indexed transactions
    Ok(vec![])
}

// ============================================================================
// Network Commands
// ============================================================================

/// Connect to a Synor node
#[tauri::command]
pub async fn connect_node(
    state: State<'_, WalletState>,
    rpc_url: String,
    ws_url: Option<String>,
) -> Result<NetworkConnection> {
    // TODO: Test connection, get network info
    let network = if rpc_url.contains("testnet") || rpc_url.contains("17110") {
        "testnet"
    } else {
        "mainnet"
    };

    let connection = NetworkConnection {
        rpc_url,
        ws_url,
        connected: true,
        network: network.to_string(),
    };

    let mut conn = state.connection.write().await;
    *conn = Some(connection.clone());

    Ok(connection)
}

/// Disconnect from the current node
#[tauri::command]
pub async fn disconnect_node(state: State<'_, WalletState>) -> Result<()> {
    let mut connection = state.connection.write().await;
    *connection = None;
    Ok(())
}

/// Network status
#[derive(Debug, Serialize)]
pub struct NetworkStatus {
    pub connected: bool,
    pub network: Option<String>,
    pub block_height: Option<u64>,
    pub peer_count: Option<u32>,
    pub synced: Option<bool>,
}

/// Get network status
#[tauri::command]
pub async fn get_network_status(state: State<'_, WalletState>) -> Result<NetworkStatus> {
    let connection = state.connection.read().await;

    if let Some(conn) = connection.as_ref() {
        // TODO: Query node for actual status
        Ok(NetworkStatus {
            connected: conn.connected,
            network: Some(conn.network.clone()),
            block_height: None,
            peer_count: None,
            synced: None,
        })
    } else {
        Ok(NetworkStatus {
            connected: false,
            network: None,
            block_height: None,
            peer_count: None,
            synced: None,
        })
    }
}

// ============================================================================
// Node Management Commands
// ============================================================================

use crate::node::{ConnectionMode, NodeManager, NodeStatus, PeerInfo, SyncProgress};
use crate::rpc_client::RpcClient;
use std::path::PathBuf;
use std::sync::Arc;

/// Application state containing node manager and RPC client
pub struct AppState {
    pub node_manager: Arc<NodeManager>,
    pub rpc_client: Arc<RpcClient>,
}

/// Connect to external RPC node
#[tauri::command]
pub async fn node_connect_external(
    state: State<'_, AppState>,
    http_url: String,
    ws_url: Option<String>,
) -> Result<NodeStatus> {
    state
        .node_manager
        .connect_external(http_url, ws_url)
        .await?;

    Ok(state.node_manager.status().await)
}

/// Start embedded node (requires embedded-node feature)
#[tauri::command]
pub async fn node_start_embedded(
    state: State<'_, AppState>,
    network: String,
    data_dir: Option<String>,
    mining_enabled: bool,
    coinbase_address: Option<String>,
    mining_threads: usize,
) -> Result<NodeStatus> {
    let data_dir = data_dir.map(PathBuf::from);

    state
        .node_manager
        .start_embedded_node(
            &network,
            data_dir,
            mining_enabled,
            coinbase_address,
            mining_threads,
        )
        .await?;

    Ok(state.node_manager.status().await)
}

/// Stop the current node connection
#[tauri::command]
pub async fn node_stop(state: State<'_, AppState>) -> Result<()> {
    state.node_manager.disconnect().await
}

/// Get current node status
#[tauri::command]
pub async fn node_get_status(state: State<'_, AppState>) -> Result<NodeStatus> {
    state.node_manager.refresh_status().await
}

/// Get current connection mode
#[tauri::command]
pub async fn node_get_connection_mode(state: State<'_, AppState>) -> Result<ConnectionMode> {
    Ok(state.node_manager.connection_mode().await)
}

/// Get connected peers
#[tauri::command]
pub async fn node_get_peers(state: State<'_, AppState>) -> Result<Vec<PeerInfo>> {
    state.rpc_client.get_peers().await
}

/// Get sync progress
#[tauri::command]
pub async fn node_get_sync_progress(state: State<'_, AppState>) -> Result<SyncProgress> {
    let status = state.node_manager.status().await;

    Ok(SyncProgress {
        current_height: status.block_height,
        target_height: status.block_height, // TODO: Get from peers
        progress: status.sync_progress,
        eta_seconds: None,
        status: if status.is_syncing {
            "Syncing...".to_string()
        } else {
            "Synced".to_string()
        },
    })
}

// ============================================================================
// Mining Commands
// ============================================================================

/// Mining status
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MiningStatus {
    /// Is mining active
    pub is_mining: bool,
    /// Is mining paused
    pub is_paused: bool,
    /// Current hashrate (H/s)
    pub hashrate: f64,
    /// Blocks found in this session
    pub blocks_found: u64,
    /// Total shares submitted
    pub shares_submitted: u64,
    /// Number of mining threads
    pub threads: usize,
    /// Coinbase address for rewards
    pub coinbase_address: Option<String>,
}

/// Mining stats (more detailed)
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MiningStats {
    /// Current hashrate (H/s)
    pub hashrate: f64,
    /// Average hashrate (H/s)
    pub avg_hashrate: f64,
    /// Peak hashrate (H/s)
    pub peak_hashrate: f64,
    /// Blocks found
    pub blocks_found: u64,
    /// Rejected blocks
    pub blocks_rejected: u64,
    /// Estimated daily coins
    pub estimated_daily_coins: f64,
    /// Mining uptime in seconds
    pub uptime_seconds: u64,
    /// Per-thread hashrates
    pub thread_hashrates: Vec<f64>,
}

/// Global mining state
pub struct MiningState {
    pub is_mining: std::sync::atomic::AtomicBool,
    pub is_paused: std::sync::atomic::AtomicBool,
    pub threads: std::sync::atomic::AtomicUsize,
    pub coinbase_address: tokio::sync::RwLock<Option<String>>,
    pub blocks_found: std::sync::atomic::AtomicU64,
    pub hashrate: tokio::sync::RwLock<f64>,
}

impl MiningState {
    pub fn new() -> Self {
        MiningState {
            is_mining: std::sync::atomic::AtomicBool::new(false),
            is_paused: std::sync::atomic::AtomicBool::new(false),
            threads: std::sync::atomic::AtomicUsize::new(0),
            coinbase_address: tokio::sync::RwLock::new(None),
            blocks_found: std::sync::atomic::AtomicU64::new(0),
            hashrate: tokio::sync::RwLock::new(0.0),
        }
    }
}

impl Default for MiningState {
    fn default() -> Self {
        Self::new()
    }
}

/// Start mining
#[tauri::command]
pub async fn mining_start(
    app_state: State<'_, AppState>,
    mining_state: State<'_, MiningState>,
    coinbase_address: String,
    threads: usize,
) -> Result<MiningStatus> {
    use std::sync::atomic::Ordering;

    // Verify we're connected to a node
    let mode = app_state.node_manager.connection_mode().await;
    if matches!(mode, ConnectionMode::Disconnected) {
        return Err(Error::NotConnected);
    }

    // Store mining configuration
    *mining_state.coinbase_address.write().await = Some(coinbase_address.clone());
    mining_state.threads.store(threads, Ordering::SeqCst);
    mining_state.is_mining.store(true, Ordering::SeqCst);
    mining_state.is_paused.store(false, Ordering::SeqCst);

    // TODO: Actually start mining via embedded node or external RPC
    // For embedded node with mining feature:
    // if let Some(node) = app_state.node_manager.embedded_node().await {
    //     node.miner().start().await?;
    // }

    Ok(MiningStatus {
        is_mining: true,
        is_paused: false,
        hashrate: 0.0,
        blocks_found: 0,
        shares_submitted: 0,
        threads,
        coinbase_address: Some(coinbase_address),
    })
}

/// Stop mining
#[tauri::command]
pub async fn mining_stop(
    mining_state: State<'_, MiningState>,
) -> Result<()> {
    use std::sync::atomic::Ordering;

    mining_state.is_mining.store(false, Ordering::SeqCst);
    mining_state.is_paused.store(false, Ordering::SeqCst);
    *mining_state.hashrate.write().await = 0.0;

    // TODO: Actually stop mining

    Ok(())
}

/// Pause mining
#[tauri::command]
pub async fn mining_pause(
    mining_state: State<'_, MiningState>,
) -> Result<()> {
    use std::sync::atomic::Ordering;

    if !mining_state.is_mining.load(Ordering::SeqCst) {
        return Err(Error::MiningError("Mining is not active".to_string()));
    }

    mining_state.is_paused.store(true, Ordering::SeqCst);

    Ok(())
}

/// Resume mining
#[tauri::command]
pub async fn mining_resume(
    mining_state: State<'_, MiningState>,
) -> Result<()> {
    use std::sync::atomic::Ordering;

    if !mining_state.is_mining.load(Ordering::SeqCst) {
        return Err(Error::MiningError("Mining is not active".to_string()));
    }

    mining_state.is_paused.store(false, Ordering::SeqCst);

    Ok(())
}

/// Get mining status
#[tauri::command]
pub async fn mining_get_status(
    mining_state: State<'_, MiningState>,
) -> Result<MiningStatus> {
    use std::sync::atomic::Ordering;

    Ok(MiningStatus {
        is_mining: mining_state.is_mining.load(Ordering::SeqCst),
        is_paused: mining_state.is_paused.load(Ordering::SeqCst),
        hashrate: *mining_state.hashrate.read().await,
        blocks_found: mining_state.blocks_found.load(Ordering::SeqCst),
        shares_submitted: 0,
        threads: mining_state.threads.load(Ordering::SeqCst),
        coinbase_address: mining_state.coinbase_address.read().await.clone(),
    })
}

/// Get detailed mining stats
#[tauri::command]
pub async fn mining_get_stats(
    mining_state: State<'_, MiningState>,
) -> Result<MiningStats> {
    use std::sync::atomic::Ordering;

    let hashrate = *mining_state.hashrate.read().await;
    let threads = mining_state.threads.load(Ordering::SeqCst);

    Ok(MiningStats {
        hashrate,
        avg_hashrate: hashrate,
        peak_hashrate: hashrate,
        blocks_found: mining_state.blocks_found.load(Ordering::SeqCst),
        blocks_rejected: 0,
        estimated_daily_coins: 0.0, // TODO: Calculate based on network difficulty
        uptime_seconds: 0,
        thread_hashrates: vec![hashrate / threads.max(1) as f64; threads],
    })
}

/// Set mining threads
#[tauri::command]
pub async fn mining_set_threads(
    mining_state: State<'_, MiningState>,
    threads: usize,
) -> Result<()> {
    use std::sync::atomic::Ordering;

    if threads == 0 {
        return Err(Error::MiningError("Threads must be greater than 0".to_string()));
    }

    mining_state.threads.store(threads, Ordering::SeqCst);

    // TODO: Actually adjust mining threads

    Ok(())
}

// ============================================================================
// Enhanced Wallet Commands (using RPC client)
// ============================================================================

/// Get balance using RPC client
#[tauri::command]
pub async fn wallet_get_balance(
    wallet_state: State<'_, WalletState>,
    app_state: State<'_, AppState>,
) -> Result<BalanceResponse> {
    let addresses = wallet_state.addresses.read().await;
    if addresses.is_empty() {
        return Ok(BalanceResponse {
            balance: 0,
            balance_human: "0 SYN".to_string(),
            pending: 0,
        });
    }

    let mut total_balance: u64 = 0;

    for addr in addresses.iter() {
        match app_state.rpc_client.get_balance(&addr.address).await {
            Ok(balance) => {
                total_balance += balance.balance;
            }
            Err(e) => {
                tracing::warn!("Failed to get balance for {}: {}", addr.address, e);
            }
        }
    }

    // Convert sompi to SYN (1 SYN = 100_000_000 sompi)
    let syn = total_balance as f64 / 100_000_000.0;
    let balance_human = format!("{:.8} SYN", syn);

    Ok(BalanceResponse {
        balance: total_balance,
        balance_human,
        pending: 0, // TODO: Track pending transactions
    })
}

/// Get UTXOs using RPC client
#[tauri::command]
pub async fn wallet_get_utxos(
    wallet_state: State<'_, WalletState>,
    app_state: State<'_, AppState>,
) -> Result<Vec<crate::rpc_client::Utxo>> {
    let addresses = wallet_state.addresses.read().await;
    let mut all_utxos = Vec::new();

    for addr in addresses.iter() {
        match app_state.rpc_client.get_utxos(&addr.address).await {
            Ok(utxos) => {
                all_utxos.extend(utxos);
            }
            Err(e) => {
                tracing::warn!("Failed to get UTXOs for {}: {}", addr.address, e);
            }
        }
    }

    Ok(all_utxos)
}

/// Get network info using RPC client
#[tauri::command]
pub async fn wallet_get_network_info(
    app_state: State<'_, AppState>,
) -> Result<crate::rpc_client::NetworkInfo> {
    app_state.rpc_client.get_network_info().await
}

/// Get fee estimate using RPC client
#[tauri::command]
pub async fn wallet_get_fee_estimate(
    app_state: State<'_, AppState>,
) -> Result<crate::rpc_client::FeeEstimate> {
    app_state.rpc_client.get_fee_estimate().await
}
