//! Synor Desktop Wallet - Tauri Backend
//!
//! Provides native functionality for the desktop wallet:
//! - Secure key storage using OS keychain
//! - Direct RPC communication with Synor nodes
//! - Transaction signing with Dilithium3 post-quantum signatures
//! - File system access for wallet backups
//! - System tray for background operation
//! - Auto-updates for seamless upgrades

mod commands;
mod crypto;
mod error;
mod keychain;
mod node;
mod rpc_client;
mod wallet;

use tauri::{
    menu::{Menu, MenuItem},
    tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
    Emitter, Manager, Runtime,
};

pub use error::{Error, Result};

/// Build the system tray menu
fn build_tray_menu<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<Menu<R>> {
    let show = MenuItem::with_id(app, "show", "Show Wallet", true, None::<&str>)?;
    let hide = MenuItem::with_id(app, "hide", "Hide to Tray", true, None::<&str>)?;
    let separator1 = MenuItem::with_id(app, "sep1", "─────────────", false, None::<&str>)?;
    let lock = MenuItem::with_id(app, "lock", "Lock Wallet", true, None::<&str>)?;
    let separator2 = MenuItem::with_id(app, "sep2", "─────────────", false, None::<&str>)?;
    let check_updates = MenuItem::with_id(app, "check_updates", "Check for Updates", true, None::<&str>)?;
    let separator3 = MenuItem::with_id(app, "sep3", "─────────────", false, None::<&str>)?;
    let quit = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;

    Menu::with_items(
        app,
        &[
            &show,
            &hide,
            &separator1,
            &lock,
            &separator2,
            &check_updates,
            &separator3,
            &quit,
        ],
    )
}

/// Handle system tray menu events
fn handle_tray_event<R: Runtime>(app: &tauri::AppHandle<R>, event: TrayIconEvent) {
    match event {
        TrayIconEvent::Click {
            button: MouseButton::Left,
            button_state: MouseButtonState::Up,
            ..
        } => {
            // Left click: show and focus the window
            if let Some(window) = app.get_webview_window("main") {
                let _ = window.show();
                let _ = window.set_focus();
            }
        }
        _ => {}
    }
}

/// Handle tray menu item clicks
fn handle_menu_event<R: Runtime>(app: &tauri::AppHandle<R>, event: tauri::menu::MenuEvent) {
    match event.id().as_ref() {
        "show" => {
            if let Some(window) = app.get_webview_window("main") {
                let _ = window.show();
                let _ = window.set_focus();
            }
        }
        "hide" => {
            if let Some(window) = app.get_webview_window("main") {
                let _ = window.hide();
            }
        }
        "lock" => {
            // Emit lock event to frontend
            let _ = app.emit("wallet-lock", ());
        }
        "check_updates" => {
            // Emit update check event to frontend
            let _ = app.emit("check-updates", ());
        }
        "quit" => {
            // Emit quit event to allow cleanup
            let _ = app.emit("app-quit", ());
            std::process::exit(0);
        }
        _ => {}
    }
}

/// Initialize the Tauri application with all plugins and commands
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        // Core plugins
        .plugin(tauri_plugin_fs::init())
        .plugin(tauri_plugin_store::Builder::default().build())
        .plugin(tauri_plugin_shell::init())
        .plugin(tauri_plugin_dialog::init())
        .plugin(tauri_plugin_clipboard_manager::init())
        // New plugins for desktop features
        .plugin(tauri_plugin_updater::Builder::new().build())
        .plugin(tauri_plugin_notification::init())
        .plugin(tauri_plugin_process::init())
        .setup(|app| {
            // Initialize wallet state
            let wallet_state = wallet::WalletState::new();
            app.manage(wallet_state);

            // Initialize node manager with app handle for events
            let node_manager = std::sync::Arc::new(
                node::NodeManager::with_app_handle(app.handle().clone())
            );

            // Initialize RPC client
            let rpc_client = std::sync::Arc::new(
                rpc_client::RpcClient::new(node_manager.clone())
            );

            // Initialize app state (node + RPC)
            let app_state = commands::AppState {
                node_manager,
                rpc_client,
            };
            app.manage(app_state);

            // Initialize mining state
            let mining_state = commands::MiningState::new();
            app.manage(mining_state);

            // Build and set up system tray
            let menu = build_tray_menu(app.handle())?;
            let _tray = TrayIconBuilder::new()
                .icon(app.default_window_icon().unwrap().clone())
                .menu(&menu)
                .show_menu_on_left_click(false)
                .on_tray_icon_event(|tray, event| {
                    handle_tray_event(tray.app_handle(), event);
                })
                .on_menu_event(|app, event| {
                    handle_menu_event(app, event);
                })
                .tooltip("Synor Wallet")
                .build(app)?;

            // Check for updates on startup (non-blocking)
            let handle = app.handle().clone();
            tauri::async_runtime::spawn(async move {
                check_for_updates(handle).await;
            });

            #[cfg(debug_assertions)]
            {
                // Open devtools in development
                if let Some(window) = app.get_webview_window("main") {
                    window.open_devtools();
                }
            }

            Ok(())
        })
        .invoke_handler(tauri::generate_handler![
            // Wallet management
            commands::create_wallet,
            commands::import_wallet,
            commands::unlock_wallet,
            commands::lock_wallet,
            commands::get_wallet_info,
            commands::export_mnemonic,
            // Addresses & UTXOs
            commands::get_addresses,
            commands::generate_address,
            commands::get_balance,
            commands::get_utxos,
            // Transactions
            commands::create_transaction,
            commands::sign_transaction,
            commands::broadcast_transaction,
            commands::get_transaction_history,
            // Network (legacy)
            commands::connect_node,
            commands::disconnect_node,
            commands::get_network_status,
            // Node management (new)
            commands::node_connect_external,
            commands::node_start_embedded,
            commands::node_stop,
            commands::node_get_status,
            commands::node_get_connection_mode,
            commands::node_get_peers,
            commands::node_get_sync_progress,
            // Mining
            commands::mining_start,
            commands::mining_stop,
            commands::mining_pause,
            commands::mining_resume,
            commands::mining_get_status,
            commands::mining_get_stats,
            commands::mining_set_threads,
            // Enhanced wallet (using RPC client)
            commands::wallet_get_balance,
            commands::wallet_get_utxos,
            commands::wallet_get_network_info,
            commands::wallet_get_fee_estimate,
            // Updates
            check_update,
            install_update,
            // Keychain / Biometric
            keychain_is_available,
            keychain_enable_biometric,
            keychain_disable_biometric,
            keychain_is_biometric_enabled,
            keychain_unlock_with_biometric,
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

/// Check for updates on startup
async fn check_for_updates<R: Runtime>(app: tauri::AppHandle<R>) {
    use tauri_plugin_updater::UpdaterExt;

    // Wait a few seconds before checking for updates
    tokio::time::sleep(std::time::Duration::from_secs(5)).await;

    let updater = match app.updater() {
        Ok(u) => u,
        Err(e) => {
            eprintln!("Updater not available: {}", e);
            return;
        }
    };

    match updater.check().await {
        Ok(Some(update)) => {
            // Notify frontend about available update
            let _ = app.emit("update-available", serde_json::json!({
                "version": update.version,
                "body": update.body,
                "date": update.date
            }));
        }
        Ok(None) => {
            // No update available
        }
        Err(e) => {
            eprintln!("Failed to check for updates: {}", e);
        }
    }
}

/// Tauri command: Check for updates
#[tauri::command]
async fn check_update<R: Runtime>(app: tauri::AppHandle<R>) -> std::result::Result<Option<serde_json::Value>, String> {
    use tauri_plugin_updater::UpdaterExt;

    let updater = app.updater().map_err(|e| format!("Updater not configured: {}", e))?;

    match updater.check().await {
        Ok(Some(update)) => {
            Ok(Some(serde_json::json!({
                "version": update.version,
                "body": update.body,
                "date": update.date
            })))
        }
        Ok(None) => Ok(None),
        Err(e) => Err(format!("Update check failed: {}", e)),
    }
}

/// Tauri command: Install update
#[tauri::command]
async fn install_update<R: Runtime>(app: tauri::AppHandle<R>) -> std::result::Result<(), String> {
    use tauri_plugin_updater::UpdaterExt;

    let updater = app.updater().map_err(|e| format!("Updater not configured: {}", e))?;

    match updater.check().await {
        Ok(Some(update)) => {
            // Download and install with progress callbacks
            let downloaded: u64 = 0;
            let total_size: u64 = 0;

            update.download_and_install(
                move |chunk_len, content_len| {
                    // Progress callback - could emit progress events here
                    let _ = (chunk_len, content_len, downloaded, total_size);
                },
                || {
                    // Ready to install callback
                },
            ).await.map_err(|e| format!("Update installation failed: {}", e))?;

            // Notify user to restart
            let _ = app.emit("update-installed", ());

            Ok(())
        }
        Ok(None) => Err("No update available".to_string()),
        Err(e) => Err(format!("Update check failed: {}", e)),
    }
}

// ============================================================================
// Keychain / Biometric Commands
// ============================================================================

/// Check if OS keychain is available on this system
#[tauri::command]
fn keychain_is_available() -> bool {
    keychain::KeychainService::is_available()
}

/// Enable biometric unlock for the current wallet
#[tauri::command]
async fn keychain_enable_biometric(
    password: String,
    state: tauri::State<'_, wallet::WalletState>,
) -> std::result::Result<(), String> {
    // Get wallet address to create keychain service
    let addresses = state.addresses.read().await;
    let first_address = addresses.first()
        .ok_or("No wallet loaded")?
        .address.clone();
    drop(addresses);

    // Verify wallet is unlocked and password is correct
    if !state.is_unlocked().await {
        return Err("Wallet is locked".to_string());
    }

    // Create keychain service and enable biometric
    let keychain_service = keychain::KeychainService::from_address(&first_address);
    keychain_service.enable_biometric_unlock(&password)
        .map_err(|e| e.to_string())?;

    Ok(())
}

/// Disable biometric unlock for the current wallet
#[tauri::command]
async fn keychain_disable_biometric(
    state: tauri::State<'_, wallet::WalletState>,
) -> std::result::Result<(), String> {
    let addresses = state.addresses.read().await;
    let first_address = addresses.first()
        .ok_or("No wallet loaded")?
        .address.clone();
    drop(addresses);

    let keychain_service = keychain::KeychainService::from_address(&first_address);
    keychain_service.disable_biometric_unlock()
        .map_err(|e| e.to_string())?;

    Ok(())
}

/// Check if biometric unlock is enabled for the current wallet
#[tauri::command]
async fn keychain_is_biometric_enabled(
    state: tauri::State<'_, wallet::WalletState>,
) -> std::result::Result<bool, String> {
    let addresses = state.addresses.read().await;
    let first_address = match addresses.first() {
        Some(addr) => addr.address.clone(),
        None => return Ok(false),
    };
    drop(addresses);

    let keychain_service = keychain::KeychainService::from_address(&first_address);
    Ok(keychain_service.is_biometric_enabled())
}

/// Attempt to unlock wallet using biometric authentication
/// The OS will prompt for TouchID/FaceID/Windows Hello
#[tauri::command]
async fn keychain_unlock_with_biometric(
    state: tauri::State<'_, wallet::WalletState>,
) -> std::result::Result<bool, String> {
    // Load wallet metadata if not loaded
    if state.metadata.read().await.is_none() {
        state.load_metadata().await.map_err(|e| e.to_string())?;
    }

    let addresses = state.addresses.read().await;
    let first_address = addresses.first()
        .ok_or("No wallet loaded")?
        .address.clone();
    drop(addresses);

    let keychain_service = keychain::KeychainService::from_address(&first_address);

    // Check if biometric is enabled
    if !keychain_service.is_biometric_enabled() {
        return Err("Biometric unlock not enabled".to_string());
    }

    // Retrieve the biometric key from OS keychain
    // This will trigger the OS biometric prompt (TouchID/FaceID/Windows Hello)
    let _biometric_key = keychain_service.get_biometric_unlock_key()
        .map_err(|e| format!("Biometric authentication failed: {}", e))?;

    // Biometric authentication succeeded
    // The frontend can now proceed with the unlock flow
    // In a full implementation, we would use the biometric_key to derive
    // the decryption key for the wallet

    Ok(true)
}
