#[cfg(target_os = "windows")]
use std::fs::File;
use std::{
    collections::HashSet,
    path::{Path, PathBuf},
    sync::Arc,
};

use eyre::bail;
use lanspread_compat::eti::get_games;
use lanspread_db::db::{Game, GameDB};
use lanspread_peer::{PeerCommand, PeerEvent, PeerGameDB, start_peer};
use tauri::{AppHandle, Emitter as _, Manager};
use tauri_plugin_shell::{ShellExt, process::Command};
use tokio::sync::{RwLock, mpsc::UnboundedSender};

// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/

struct LanSpreadState {
    peer_ctrl: Arc<RwLock<Option<UnboundedSender<PeerCommand>>>>,
    games: Arc<RwLock<GameDB>>,
    games_in_download: Arc<RwLock<HashSet<String>>>,
    games_folder: Arc<RwLock<String>>,
    #[allow(dead_code)]
    peer_game_db: Arc<RwLock<PeerGameDB>>,
}

#[cfg(target_os = "windows")]
const FIRST_START_DONE_FILE: &str = ".softlan_first_start_done";

#[tauri::command]
async fn request_games(state: tauri::State<'_, LanSpreadState>) -> tauri::Result<()> {
    log::debug!("request_games");

    let peer_ctrl_arc = state.inner().peer_ctrl.clone();
    let peer_ctrl = peer_ctrl_arc.read().await.clone();

    if let Some(peer_ctrl) = peer_ctrl {
        if let Err(e) = peer_ctrl.send(PeerCommand::ListGames) {
            log::error!("Failed to send message to peer: {e:?}");
        }
    } else {
        log::warn!("Peer system not initialized yet");
    }

    Ok(())
}

#[tauri::command]
async fn install_game(id: String, state: tauri::State<'_, LanSpreadState>) -> tauri::Result<bool> {
    let games_in_download = state.inner().games_in_download.clone();
    let already_in_download = {
        let guard = games_in_download.read().await;
        if guard.contains(&id) {
            log::warn!("Game is already downloading: {id}");
            true
        } else {
            false
        }
    };

    if already_in_download {
        return Ok(false);
    }

    let peer_ctrl_arc = state.inner().peer_ctrl.clone();
    let peer_ctrl = peer_ctrl_arc.read().await.clone();

    let handled = if let Some(peer_ctrl) = peer_ctrl {
        if let Err(e) = peer_ctrl.send(PeerCommand::GetGame(id)) {
            log::error!("Failed to send message to peer: {e:?}");
        }
        true
    } else {
        log::warn!("Peer system not initialized yet");
        false
    };

    Ok(handled)
}

/// Backup the current game folder by renaming it to `___TO_BE_DELETE___GameNameHere`
/// # Errors
/// Returns error if backup fails
fn backup_game_folder(game_path: &Path) -> eyre::Result<PathBuf> {
    if !game_path.exists() {
        bail!("Game folder does not exist: {}", game_path.display());
    }

    let game_name = game_path
        .file_name()
        .and_then(|name| name.to_str())
        .ok_or_else(|| eyre::eyre!("Invalid game folder name"))?;

    let parent = game_path
        .parent()
        .ok_or_else(|| eyre::eyre!("Cannot get parent directory"))?;

    let backup_name = format!("___TO_BE_DELETE___{game_name}");
    let backup_path = parent.join(backup_name);

    // Remove existing backup if it exists
    if backup_path.exists() {
        std::fs::remove_dir_all(&backup_path)?;
    }

    // Rename current game folder to backup
    std::fs::rename(game_path, &backup_path)?;

    log::info!(
        "Backed up game folder: {} -> {}",
        game_path.display(),
        backup_path.display()
    );
    Ok(backup_path)
}

/// Restore game folder from backup
/// # Errors
/// Returns error if restore fails
fn restore_game_folder(game_path: &Path, backup_path: &Path) -> eyre::Result<()> {
    if !backup_path.exists() {
        bail!("Backup folder does not exist: {}", backup_path.display());
    }

    // Remove the partially installed game folder if it exists
    if game_path.exists() {
        std::fs::remove_dir_all(game_path)?;
    }

    // Restore backup
    std::fs::rename(backup_path, game_path)?;

    log::info!(
        "Restored game folder from backup: {} -> {}",
        backup_path.display(),
        game_path.display()
    );
    Ok(())
}

/// Remove backup folder after successful update
/// # Errors
/// Returns error if cleanup fails
fn cleanup_backup_folder(backup_path: &Path) -> eyre::Result<()> {
    if backup_path.exists() {
        std::fs::remove_dir_all(backup_path)?;
        log::info!("Cleaned up backup folder: {}", backup_path.display());
    }
    Ok(())
}

async fn cleanup_failed_download(app_handle: &AppHandle, id: &str) {
    app_handle
        .state::<LanSpreadState>()
        .inner()
        .games_in_download
        .write()
        .await
        .remove(id);

    let games_folder = app_handle
        .state::<LanSpreadState>()
        .inner()
        .games_folder
        .read()
        .await
        .clone();

    if games_folder.is_empty() {
        return;
    }

    let backup_name = format!("___TO_BE_DELETE___{id}");
    let backup_path = PathBuf::from(&games_folder).join(&backup_name);
    let game_path = PathBuf::from(&games_folder).join(id);

    if game_path.exists() {
        if let Err(e) = std::fs::remove_dir_all(&game_path) {
            log::error!("Failed to delete half-downloaded game folder: {e}");
        } else {
            log::info!(
                "Deleted half-downloaded game folder: {}",
                game_path.display()
            );
        }
    }

    if let Err(e) = restore_game_folder(&game_path, &backup_path) {
        log::error!("Failed to restore backup after download failure: {e}");
    }
}

#[tauri::command]
async fn update_game(id: String, state: tauri::State<'_, LanSpreadState>) -> tauri::Result<bool> {
    let games_in_download = state.inner().games_in_download.clone();
    let already_in_download = {
        let guard = games_in_download.read().await;
        if guard.contains(&id) {
            log::warn!("Game is already downloading/updating: {id}");
            true
        } else {
            false
        }
    };

    if already_in_download {
        return Ok(false);
    }

    // Get the games folder
    let games_folder_lock = state.inner().games_folder.clone();
    let games_folder = {
        let guard = games_folder_lock.read().await;
        guard.clone()
    };

    let games_folder = PathBuf::from(games_folder);
    let game_path = games_folder.join(&id);

    // Backup current game folder
    let backup_path = match backup_game_folder(&game_path) {
        Ok(path) => path,
        Err(e) => {
            log::error!("Failed to backup game folder for {id}: {e}");
            return Ok(false);
        }
    };

    log::info!("Starting update for game: {id}");

    // Start the download process
    let peer_ctrl_arc = state.inner().peer_ctrl.clone();
    let peer_ctrl = peer_ctrl_arc.read().await.clone();

    if let Some(peer_ctrl) = peer_ctrl {
        if let Err(e) = peer_ctrl.send(PeerCommand::GetGame(id.clone())) {
            log::error!("Failed to send message to peer: {e:?}");

            // Try to restore backup if download fails to start
            if let Err(restore_err) = restore_game_folder(&game_path, &backup_path) {
                log::error!("Failed to restore backup after download failure: {restore_err}");
            }

            return Ok(false);
        }
        Ok(true)
    } else {
        log::warn!("Peer system not initialized yet");
        Ok(false)
    }
}

#[tauri::command]
async fn get_peer_count(state: tauri::State<'_, LanSpreadState>) -> tauri::Result<usize> {
    let peer_ctrl_arc = state.inner().peer_ctrl.clone();
    let peer_ctrl = peer_ctrl_arc.read().await.clone();

    if let Some(peer_ctrl) = peer_ctrl {
        // Send a request to get peer count
        if let Err(e) = peer_ctrl.send(PeerCommand::GetPeerCount) {
            log::error!("Failed to send GetPeerCount message to peer: {e:?}");
        }
    }

    // For now, we'll return 0 and rely on the event-based updates
    // The UI will get the actual count through peer-count-updated events
    Ok(0)
}

#[tauri::command]
async fn get_game_thumbnail(
    game_id: String,
    app_handle: tauri::AppHandle,
) -> tauri::Result<String> {
    use base64::Engine;

    let resource_path = app_handle.path().resolve(
        format!("assets/{game_id}.jpg"),
        tauri::path::BaseDirectory::Resource,
    )?;

    let image_data = std::fs::read(&resource_path)?;
    let base64_data = base64::engine::general_purpose::STANDARD.encode(&image_data);
    Ok(format!("data:image/jpeg;base64,{base64_data}"))
}

#[cfg(target_os = "windows")]
fn run_as_admin(file: &str, params: &str, dir: &str) -> bool {
    use std::{ffi::OsStr, os::windows::ffi::OsStrExt};

    use windows::{Win32::UI::Shell::ShellExecuteW, core::PCWSTR};

    let file_wide: Vec<u16> = OsStr::new(file).encode_wide().chain(Some(0)).collect();
    let params_wide: Vec<u16> = OsStr::new(params).encode_wide().chain(Some(0)).collect();
    let dir_wide: Vec<u16> = OsStr::new(dir).encode_wide().chain(Some(0)).collect();
    let runas_wide: Vec<u16> = OsStr::new("runas").encode_wide().chain(Some(0)).collect();

    let result = unsafe {
        ShellExecuteW(
            None,
            PCWSTR::from_raw(runas_wide.as_ptr()),
            PCWSTR::from_raw(file_wide.as_ptr()),
            PCWSTR::from_raw(params_wide.as_ptr()),
            PCWSTR::from_raw(dir_wide.as_ptr()),
            windows::Win32::UI::WindowsAndMessaging::SW_HIDE,
        )
    };

    (result.0 as usize) > 32 // Success if greater than 32
}

#[cfg(target_os = "windows")]
async fn run_game_windows(
    id: String,
    state: tauri::State<'_, LanSpreadState>,
) -> tauri::Result<()> {
    let games_folder_lock = state.inner().games_folder.clone();
    let games_folder = {
        let guard = games_folder_lock.read().await;
        guard.clone()
    };

    let games_folder = PathBuf::from(games_folder);
    if !games_folder.exists() {
        log::error!("games_folder {} does not exist", games_folder.display());
        return Ok(());
    }

    let game_path = games_folder.join(id.clone());

    let game_setup_bin = game_path.join("game_setup.cmd");
    let game_start_bin = game_path.join("game_start.cmd");

    let first_start_done_file = game_path.join("local").join(FIRST_START_DONE_FILE);
    if !first_start_done_file.exists() && game_setup_bin.exists() {
        if !local_install_is_ready(&game_path) {
            log::warn!(
                "local install is missing or incomplete for {}; skipping game_setup",
                game_path.display()
            );
            return Ok(());
        }

        let result = run_as_admin(
            "cmd.exe",
            &format!(
                r#"/c "{} local {} de playername""#,
                game_setup_bin.display(),
                &id
            ),
            &game_path.display().to_string(),
        );

        if !result {
            log::error!("failed to run game_setup.cmd");
            return Ok(());
        }

        if let Err(e) = File::create(&first_start_done_file) {
            log::error!(
                "failed to create first-start marker {}: {e}",
                first_start_done_file.display()
            );
        }
    }

    if game_start_bin.exists() {
        let result = run_as_admin(
            "cmd.exe",
            &format!(
                r#"/c "{} local {} de playername""#,
                game_start_bin.display(),
                &id
            ),
            &game_path.display().to_string(),
        );

        if !result {
            log::error!("failed to run game_start.cmd");
        }
    }

    Ok(())
}

#[tauri::command]
async fn run_game(id: String, state: tauri::State<'_, LanSpreadState>) -> tauri::Result<()> {
    #[cfg(target_os = "windows")]
    {
        run_game_windows(id, state).await?;
    }

    #[cfg(not(target_os = "windows"))]
    {
        let _ = state;
        log::error!("run_game not implemented for this platform: id={id}");
    }

    Ok(())
}

fn eti_package_exists(game_path: &Path, game_id: &str) -> bool {
    game_path.is_dir() && game_path.join(format!("{game_id}.eti")).is_file()
}

fn local_install_is_ready(game_path: &Path) -> bool {
    let local_dir = game_path.join("local");
    if !local_dir.is_dir() {
        return false;
    }

    match std::fs::read_dir(&local_dir) {
        Ok(mut entries) => match entries.next() {
            Some(Ok(_)) => true,
            Some(Err(e)) => {
                log::warn!(
                    "Failed to inspect entry in local dir {}: {e}",
                    local_dir.display()
                );
                false
            }
            None => false,
        },
        Err(e) => {
            log::warn!(
                "Failed to enumerate local dir for game {}: {e}",
                game_path.display()
            );
            false
        }
    }
}

fn update_game_installation_state(game: &mut Game, games_root: &Path) {
    let game_path = games_root.join(&game.id);
    if !game_path.is_dir() {
        return;
    }

    let downloaded = eti_package_exists(&game_path, &game.id);
    game.downloaded = downloaded;

    let installed = downloaded && local_install_is_ready(&game_path);
    game.installed = installed;

    // Size stays anchored to bundled game.db; skip expensive recalculation.

    if installed {
        log::debug!("Set {game} to installed");
        match lanspread_db::db::read_version_from_ini(&game_path) {
            Ok(version) => {
                game.local_version = version;
                if let Some(ref version) = game.local_version {
                    log::debug!("Read local version for game {}: {}", game.id, version);
                }
            }
            Err(e) => {
                log::warn!("Failed to read local version.ini for game {}: {e}", game.id);
                game.local_version = None;
            }
        }
    } else {
        game.local_version = None;
        if downloaded {
            log::debug!(
                "Game {} is downloaded but awaiting local install contents",
                game.id
            );
        }
    }
}

/// Left in place for potential re-enablement. Currently not invoked to avoid expensive IO.
#[allow(dead_code)]
fn calculate_directory_size_sync(dir: &Path) -> eyre::Result<u64> {
    let mut total_size = 0u64;

    for entry in walkdir::WalkDir::new(dir) {
        let entry = entry?;
        let path = entry.path();

        if path.is_file() {
            let metadata = std::fs::metadata(path)?;
            total_size += metadata.len();
        }
    }

    Ok(total_size)
}

/// Used for peer-majority calculations but currently disabled.
#[allow(dead_code)]
fn calculate_size_from_file_descriptions(
    file_descriptions: &[lanspread_db::db::GameFileDescription],
) -> u64 {
    file_descriptions
        .iter()
        .filter(|desc| !desc.is_dir)
        .map(|desc| desc.size)
        .sum()
}

/// Future hook for reintroducing peer-driven size updates.
#[allow(dead_code)]
async fn update_game_sizes_from_peers(
    games: &mut std::collections::HashMap<String, Game>,
    peer_game_db: &Arc<RwLock<PeerGameDB>>,
) {
    log::debug!("Updating game sizes from peer data where local files are not available");

    let peer_db = peer_game_db.read().await;

    for game in games.values_mut() {
        if !game.downloaded && !game.installed {
            let peer_files_for_game = peer_db.aggregated_game_files(&game.id);

            if peer_files_for_game.is_empty() {
                if let Some(peer_size) = peer_db.majority_game_size(&game.id) {
                    if peer_size > 0 {
                        game.size = peer_size;
                        log::debug!(
                            "Updated size for game {} from peer totals: {} bytes",
                            game.id,
                            peer_size
                        );
                    } else {
                        log::debug!(
                            "Peer-reported size for game {} is 0; keeping previous value",
                            game.id
                        );
                    }
                } else {
                    log::debug!("No peer size data available for game {}", game.id);
                }
            } else {
                let peer_size = calculate_size_from_file_descriptions(&peer_files_for_game);

                if peer_size > 0 {
                    game.size = peer_size;
                    log::debug!(
                        "Updated size for game {} from peer files: {} bytes ({} files)",
                        game.id,
                        peer_size,
                        peer_files_for_game.len()
                    );
                } else {
                    log::debug!(
                        "Peer files for game {} exist but calculated size is 0",
                        game.id
                    );
                }
            }
        }
    }
}

async fn refresh_games_list(app_handle: &AppHandle) {
    let state = app_handle.state::<LanSpreadState>();

    let games_folder_lock = state.games_folder.clone();
    let games_db_lock = state.games.clone();

    let games_folder = games_folder_lock.read().await.clone();

    let path = if games_folder.is_empty() {
        log::debug!("Games folder not set; emitting current game list without rescan");
        None
    } else {
        Some(PathBuf::from(&games_folder))
    };

    let mut game_db = games_db_lock.write().await;

    if game_db.games.is_empty() {
        log::debug!("Game database empty during refresh; skipping emit");
        return;
    }

    if let Some(ref path) = path {
        if path.exists() {
            game_db.set_all_uninstalled();
            for game in game_db.games.values_mut() {
                update_game_installation_state(game, path);
            }
        } else {
            log::error!(
                "game dir {} does not exist; keeping last known installation state",
                path.display()
            );
        }
    }

    let games_to_emit = game_db
        .all_games()
        .into_iter()
        .cloned()
        .collect::<Vec<Game>>();

    drop(game_db);

    if let Err(e) = app_handle.emit("games-list-updated", Some(games_to_emit)) {
        log::error!("Failed to emit games-list-updated event: {e}");
    } else {
        log::info!("Emitted games-list-updated event");
    }
}

#[tauri::command]
async fn update_game_directory(app_handle: tauri::AppHandle, path: String) -> tauri::Result<()> {
    log::info!("update_game_directory: {path}");

    let peer_ctrl_lock = app_handle
        .state::<LanSpreadState>()
        .inner()
        .peer_ctrl
        .clone();
    let games_folder_lock = app_handle
        .state::<LanSpreadState>()
        .inner()
        .games_folder
        .clone();

    let peer_ctrl = peer_ctrl_lock.read().await.clone();

    if let Some(peer_ctrl) = peer_ctrl
        && let Err(e) = peer_ctrl.send(PeerCommand::SetGameDir(path.clone()))
    {
        log::error!("Failed to send PeerCommand::SetGameDir: {e}");
    }

    {
        let mut games_folder = games_folder_lock.write().await;
        games_folder.clone_from(&path);
    }

    let path = PathBuf::from(path);
    if !path.exists() {
        log::error!("game dir {} does not exist", path.display());
        return Ok(());
    }

    refresh_games_list(&app_handle).await;

    Ok(())
}

async fn update_game_db(games: Vec<Game>, app: AppHandle) {
    for game in &games {
        log::trace!("peer event ListGames iter: {game:?}");
    }

    let state = app.state::<LanSpreadState>();

    {
        let mut game_db = state.games.write().await;

        // Reset peer counts up front. Presence/metadata stay anchored to the baked game.db.
        for game in game_db.games.values_mut() {
            game.peer_count = 0;
        }

        for peer_game in games {
            if let Some(existing) = game_db.get_mut_game_by_id(&peer_game.id) {
                existing.peer_count = peer_game.peer_count;

                if let Some(peer_version) = &peer_game.eti_game_version {
                    match &existing.eti_game_version {
                        Some(current_version) if current_version >= peer_version => {}
                        _ => {
                            existing.eti_game_version = Some(peer_version.clone());
                            log::debug!(
                                "Updated eti_game_version for {} to {} based on peer data",
                                peer_game.id,
                                peer_version
                            );
                        }
                    }
                }
            } else {
                log::debug!(
                    "Peer advertised unknown game {id}; ignoring because game.db is ground truth",
                    id = peer_game.id
                );
            }
        }
    }

    refresh_games_list(&app).await;
}

async fn update_local_games_in_db(local_games: Vec<Game>, app: AppHandle) {
    let state = app.state::<LanSpreadState>();

    // Collect local game IDs first to avoid move issues
    let local_game_ids: HashSet<String> = local_games.iter().map(|g| g.id.clone()).collect();

    {
        let mut game_db = state.games.write().await;

        // Update installation status for games that exist locally
        for local_game in &local_games {
            if let Some(existing_game) = game_db.get_mut_game_by_id(&local_game.id) {
                existing_game.downloaded = local_game.downloaded;
                existing_game.installed = local_game.installed;
                existing_game
                    .local_version
                    .clone_from(&local_game.local_version);
                log::debug!("Updated local game status for: {}", local_game.id);
            }
        }

        // For games in the main DB that are not in the local list,
        // mark them as not downloaded/installed (they were deleted)
        for game in game_db.games.values_mut() {
            if !local_game_ids.contains(&game.id) && (game.downloaded || game.installed) {
                log::info!(
                    "Game {} no longer exists locally, marking as uninstalled",
                    game.id
                );
                game.downloaded = false;
                game.installed = false;
                game.local_version = None;
            }
        }
    }

    refresh_games_list(&app).await;
}

fn add_final_slash(path: &str) -> String {
    #[cfg(target_os = "windows")]
    const SLASH_CHAR: char = '\\';

    #[cfg(not(target_os = "windows"))]
    const SLASH_CHAR: char = '/';

    if path.ends_with(SLASH_CHAR) {
        path.to_string()
    } else {
        format!("{path}{SLASH_CHAR}")
    }
}

async fn do_unrar(sidecar: Command, rar_file: &Path, dest_dir: &Path) -> eyre::Result<()> {
    if let Ok(()) = std::fs::create_dir_all(dest_dir) {
        if let Ok(rar_file) = rar_file.canonicalize() {
            if let Ok(dest_dir) = dest_dir.canonicalize() {
                let dest_dir = dest_dir
                    .to_str()
                    .ok_or_else(|| eyre::eyre!("failed to get str of dest_dir"))?;

                log::info!(
                    "unrar game: {} to {}",
                    rar_file.canonicalize()?.display(),
                    dest_dir
                );

                let out = sidecar
                    .arg("x") // extract files
                    .arg(rar_file.canonicalize()?)
                    .arg("-y") // Assume Yes on all queries
                    .arg("-o") // Set overwrite mode
                    .arg(add_final_slash(dest_dir))
                    .output()
                    .await?;

                if !out.status.success() {
                    log::error!("unrar stderr: {}", String::from_utf8_lossy(&out.stderr));
                }

                return Ok(());
            }
            log::error!("dest_dir canonicalize failed: {}", dest_dir.display());
        } else {
            log::error!("rar_file canonicalize failed: {}", rar_file.display());
        }
    }

    bail!("failed to create directory: {dest_dir:?}");
}

async fn unpack_game(id: &str, sidecar: Command, games_folder: &str) {
    let game_path = PathBuf::from(games_folder).join(id);
    let eti_rar = game_path.join(format!("{id}.eti"));
    let local_path = game_path.join("local");

    if let Err(e) = do_unrar(sidecar, &eti_rar, &local_path).await {
        log::error!("{} -> {}: {e}", eti_rar.display(), local_path.display());
    }
}

#[allow(clippy::too_many_lines)]
#[allow(clippy::missing_panics_doc)]
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    let tauri_logger_builder = tauri_plugin_log::Builder::new()
        .clear_targets()
        .target(tauri_plugin_log::Target::new(
            tauri_plugin_log::TargetKind::Stdout,
        ))
        .level(log::LevelFilter::Info)
        .level_for("mdns_sd::service_daemon", log::LevelFilter::Off);

    // channel to receive events from the peer
    let (tx_peer_event, mut rx_peer_event) = tokio::sync::mpsc::unbounded_channel::<PeerEvent>();

    let peer_game_db = Arc::new(RwLock::new(PeerGameDB::new()));

    let lanspread_state = LanSpreadState {
        peer_ctrl: Arc::new(RwLock::new(None)),
        games: Arc::new(RwLock::new(GameDB::empty())),
        games_in_download: Arc::new(RwLock::new(HashSet::new())),
        games_folder: Arc::new(RwLock::new(String::new())),
        peer_game_db: peer_game_db.clone(),
    };

    tauri::Builder::default()
        .plugin(tauri_plugin_store::Builder::new().build())
        .plugin(tauri_plugin_dialog::init())
        .plugin(tauri_logger_builder.build())
        .plugin(tauri_plugin_shell::init())
        .invoke_handler(tauri::generate_handler![
            request_games,
            install_game,
            run_game,
            update_game_directory,
            update_game,
            get_peer_count,
            get_game_thumbnail
        ])
        .manage(lanspread_state)
        .setup({
            let tx_peer_event_clone = tx_peer_event.clone();
            let peer_game_db_clone = peer_game_db.clone();
            move |app| {
            // Initialize peer system ONLY when games directory is set (games directory is mandatory)
            // But the UI is responsive immediately - no blocking server discovery
            let app_handle_clone = app.handle().clone();
            let tx_peer_event_for_spawn = tx_peer_event_clone.clone();
            let peer_game_db_for_spawn = peer_game_db_clone.clone();
            tauri::async_runtime::spawn(async move {
                // Wait for games directory to be set by user (this is mandatory)
                loop {
                    let games_folder = {
                        let state = app_handle_clone.state::<LanSpreadState>();
                        state.games_folder.read().await.clone()
                    };

                    if !games_folder.is_empty() {
                        // Load ETI games from the game.db resource - this MUST succeed
                        let game_db_path =
                            match app_handle_clone
                                .path()
                                .resolve("game.db", tauri::path::BaseDirectory::Resource)
                            {
                                Ok(path) => path,
                                Err(e) => {
                                    log::error!("Failed to resolve game.db resource: {e}");
                                    panic!("game.db resource is mandatory - cannot continue");
                                }
                            };

                        let eti_games = match get_games(&game_db_path).await {
                            Ok(games) => {
                                log::info!("Loaded {} ETI games from game.db", games.len());
                                games
                            }
                            Err(e) => {
                                log::error!("Failed to load ETI games: {e}");
                                panic!("game.db resource is mandatory - cannot continue");
                            }
                        };

                        // Convert ETI games to GameDB and store in state
                        let games: Vec<Game> = eti_games.into_iter().map(Into::into).collect();
                        {
                            let state = app_handle_clone.state::<LanSpreadState>();
                            *state.games.write().await = GameDB::from(games);
                        }

                        refresh_games_list(&app_handle_clone).await;

                        // Only start peer system when we have a valid games directory
                        match start_peer(
                            games_folder,
                            tx_peer_event_for_spawn.clone(),
                            peer_game_db_for_spawn.clone(),
                        ) {
                            Ok(peer_ctrl) => {
                                let state = app_handle_clone.state::<LanSpreadState>();
                                *state.peer_ctrl.write().await = Some(peer_ctrl);
                                log::info!("Peer system initialized successfully with games directory");

                                // Wait a moment for local game database to be loaded before starting discovery
                                tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await;

                                // Start peer discovery and request games from other peers
                                if let Err(e) = request_games(state).await {
                                    log::error!("Failed to request games after peer init: {e}");
                                }
                            }
                            Err(e) => {
                                log::error!("Failed to initialize peer system: {e}");
                            }
                        }
                        break;
                    }

                    // Check every 100ms for games directory (non-blocking)
                    tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
                }
            });

            let app_handle = app.handle().clone();
            tauri::async_runtime::spawn(async move {
                while let Some(event) = rx_peer_event.recv().await {
                    match event {
                        PeerEvent::ListGames(games) => {
                            log::info!("PeerEvent::ListGames received");
                            update_game_db(games, app_handle.clone()).await;
                        }
                        PeerEvent::LocalGamesUpdated(local_games) => {
                            log::info!("PeerEvent::LocalGamesUpdated received");
                            update_local_games_in_db(local_games, app_handle.clone()).await;
                        }
                        PeerEvent::GotGameFiles { id, file_descriptions } => {
                            log::info!("PeerEvent::GotGameFiles received");

                                if let Err(e) = app_handle.emit(
                                    "game-download-pre",
                                    Some(id.clone()),
                                ) {
                                    log::error!("PeerEvent::GotGameFiles: Failed to emit game-download-pre event: {e}");
                                }

                                let state = app_handle.state::<LanSpreadState>();
                                let peer_ctrl = state.peer_ctrl.read().await.clone();
                                if let Some(peer_ctrl) = peer_ctrl
                                    && let Err(e) = peer_ctrl.send(PeerCommand::DownloadGameFiles{
                                        id,
                                        file_descriptions,
                                    }) {
                                        log::error!("Failed to send PeerCommand::DownloadGameFiles: {e}");
                                    }

                        }
                        PeerEvent::NoPeersHaveGame { id } => {
                            log::warn!("PeerEvent::NoPeersHaveGame received for {id}");

                            if let Err(e) = app_handle.emit("game-no-peers", Some(id.clone())) {
                                log::error!("PeerEvent::NoPeersHaveGame: Failed to emit game-no-peers event: {e}");
                            }

                            app_handle
                                .state::<LanSpreadState>()
                                .inner()
                                .games_in_download
                                .write()
                                .await
                                .remove(&id);
                        }
                        PeerEvent::DownloadGameFilesBegin { id } => {
                            log::info!("PeerEvent::DownloadGameFilesBegin received");

                            app_handle
                                .state::<LanSpreadState>()
                                .inner()
                                .games_in_download
                                .write()
                                .await
                                .insert(id.clone());

                            if let Err(e) = app_handle.emit("game-download-begin", Some(id)) {
                                log::error!("PeerEvent::DownloadGameFilesBegin: Failed to emit game-download-begin event: {e}");
                            }
                        }
                        PeerEvent::DownloadGameFilesFinished { id } => {
                            log::info!("PeerEvent::DownloadGameFilesFinished received");
                            if let Err(e) = app_handle.emit("game-download-finished", Some(id.clone())) {
                                log::error!("PeerEvent::DownloadGameFilesFinished: Failed to emit game-download-finished event: {e}");
                            }

                            app_handle
                                .state::<LanSpreadState>()
                                .inner()
                                .games_in_download
                                .write()
                                .await
                                .remove(&id.clone());

                            let games_folder = app_handle
                                .state::<LanSpreadState>()
                                .inner()
                                .games_folder
                                .read()
                                .await
                                .clone();

                            if let Ok(sidecar) = app_handle.shell().sidecar("unrar") {

                                let app_handle = app_handle.clone();

                                // Spawn a separate task to handle unpacking and backup cleanup
                                tauri::async_runtime::spawn(async move {
                                    unpack_game(&id, sidecar, &games_folder).await;

                                    if !games_folder.is_empty() {
                                        let backup_name = format!("___TO_BE_DELETE___{id}");
                                        let backup_path = PathBuf::from(&games_folder).join(backup_name);

                                        if let Err(e) = cleanup_backup_folder(&backup_path) {
                                            log::error!("Failed to cleanup backup folder after successful update: {e}");
                                        }
                                    }

                                    log::info!("PeerEvent::UnpackGameFinished received");
                                    if let Err(e) = app_handle.emit("game-unpack-finished", Some(id.clone())) {
                                        log::error!("PeerEvent::UnpackGameFinished: Failed to emit game-unpack-finished event: {e}");
                                    }
                                });
                            }
                        }
                        PeerEvent::DownloadGameFilesFailed { id } => {
                            log::warn!("PeerEvent::DownloadGameFilesFailed received");

                            if let Err(e) = app_handle.emit("game-download-failed", Some(id.clone())) {
                                log::error!("Failed to emit game-download-failed event: {e}");
                            }

                            cleanup_failed_download(&app_handle, &id).await;
                        }
                        PeerEvent::DownloadGameFilesAllPeersGone { id } => {
                            log::warn!(
                                "PeerEvent::DownloadGameFilesAllPeersGone received for {id}"
                            );

                            if let Err(e) = app_handle.emit(
                                "game-download-peers-gone",
                                Some(id.clone()),
                            ) {
                                log::error!(
                                    "Failed to emit game-download-peers-gone event: {e}"
                                );
                            }

                            cleanup_failed_download(&app_handle, &id).await;
                        }
                        PeerEvent::PeerConnected(addr) => {
                            log::info!("Peer connected: {addr}");
                            if let Err(e) = app_handle.emit("peer-connected", Some(addr.to_string())) {
                                log::error!("Failed to emit peer-connected event: {e}");
                            }
                        }
                        PeerEvent::PeerDisconnected(addr) => {
                            log::info!("Peer disconnected: {addr}");
                            if let Err(e) = app_handle.emit("peer-disconnected", Some(addr.to_string())) {
                                log::error!("Failed to emit peer-disconnected event: {e}");
                            }
                        }
                        PeerEvent::PeerDiscovered(addr) => {
                            log::info!("Peer discovered: {addr}");
                            if let Err(e) = app_handle.emit("peer-discovered", Some(addr.to_string())) {
                                log::error!("Failed to emit peer-discovered event: {e}");
                            }
                        }
                        PeerEvent::PeerLost(addr) => {
                            log::info!("Peer lost: {addr}");
                            if let Err(e) = app_handle.emit("peer-lost", Some(addr.to_string())) {
                                log::error!("Failed to emit peer-lost event: {e}");
                            }
                        }
                        PeerEvent::PeerCountUpdated(count) => {
                            log::info!("Peer count updated: {count}");
                            if let Err(e) = app_handle.emit("peer-count-updated", Some(count)) {
                                log::error!("Failed to emit peer-count-updated event: {e}");
                            }
                        }
                    }
                }
            });

            Ok(())
            }
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
