use clap::Parser;

use inquire::Select;

use std::{fmt, process::exit};

use libprotonup::apps::App;

mod architecture_variants;
mod cli;
mod cli_mode;
mod download;
mod file_path;
mod helper_menus;
mod manage_apps;

use cli::Opt;
use manage_apps::manage_apps_routine;

/// Guard struct that cleans up temp directory when dropped
struct TempDirCleanupGuard;

impl Drop for TempDirCleanupGuard {
    fn drop(&mut self) {
        let _ = libprotonup::utils::cleanup_fallback_temp_dir();
    }
}

#[derive(Debug, Copy, Clone)]
#[allow(clippy::upper_case_acronyms)]
enum InitialMenu {
    QuickUpdate,
    DownloadForSteam,
    DownloadForLutris,
    DownloadIntoCustomLocation,
    CheckChangelog,
    ManageExistingInstallations,
}

impl InitialMenu {
    // could be generated by macro
    const VARIANTS: &'static [InitialMenu] = &[
        Self::QuickUpdate,
        Self::DownloadForSteam,
        Self::DownloadForLutris,
        Self::DownloadIntoCustomLocation,
        Self::CheckChangelog,
        Self::ManageExistingInstallations,
    ];
}

impl fmt::Display for InitialMenu {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Self::QuickUpdate => write!(
                f,
                "Quick Update (detect apps and update compatibility tools)"
            ),
            Self::DownloadForSteam => write!(f, "Download compatibility tools for Steam"),
            Self::DownloadForLutris => write!(f, "Download compatibility tools for Lutris"),
            Self::DownloadIntoCustomLocation => {
                write!(f, "Download compatibility tools into custom location")
            }
            Self::CheckChangelog => write!(f, "Check changelog (what is new)"),
            Self::ManageExistingInstallations => write!(f, "Manage Existing Installations"),
        }
    }
}

#[tokio::main]
async fn main() {
    // Register temp directory cleanup guard
    let _cleanup_guard = TempDirCleanupGuard;

    let Opt {
        quick_download,
        force,
        tool,
        version,
        r#for: for_target,
        whats_new,
    } = Opt::parse();

    // If --whats-new is passed alone (no --tool, no --quick-download),
    // run standalone check-for-updates mode and exit
    if whats_new && !quick_download && tool.is_none() && version.is_none() && for_target.is_none() {
        if let Err(e) = download::check_whats_new().await {
            eprintln!("{e}");
            exit(1);
        }
        return;
    }

    // If any CLI argument is provided, run in CLI mode (non-interactive)
    if tool.is_some() || version.is_some() || for_target.is_some() {
        let releases = cli_mode::run_cli_mode(tool, version, for_target, force, whats_new).await;
        match releases {
            Ok(releases) => {
                for release in releases {
                    println!("Installed {}", release.tag_name);
                }
            }
            Err(e) => {
                eprintln!("{e}");
                exit(1);
            }
        }
        return;
    }

    // run quick downloads and skip InitialMenu
    let releases = if quick_download {
        download::run_quick_downloads(force, whats_new).await
    } else {
        loop {
            let answer: InitialMenu = Select::new(
                "ProtonUp Menu: Choose your action:",
                InitialMenu::VARIANTS.to_vec(),
            )
            .with_page_size(10)
            .prompt()
            .unwrap_or_else(|_| std::process::exit(0));

            // Download actions exit the loop; other actions return to menu
            match answer {
                InitialMenu::QuickUpdate => {
                    break download::run_quick_downloads(force, whats_new).await;
                }
                InitialMenu::DownloadForSteam => {
                    break download::download_to_selected_app(Some(App::Steam)).await;
                }
                InitialMenu::DownloadForLutris => {
                    break download::download_to_selected_app(Some(App::Lutris)).await;
                }
                InitialMenu::DownloadIntoCustomLocation => {
                    break download::download_to_selected_app(None).await;
                }
                InitialMenu::CheckChangelog => {
                    let _ = download::check_changelog_menu().await;
                }
                InitialMenu::ManageExistingInstallations => {
                    manage_apps_routine().await;
                }
            }
        }
    };

    match releases {
        Ok(releases) => {
            for release in releases {
                println!("Installed {}", release.tag_name);
            }
        }
        Err(e) => {
            eprintln!("{e}");
            exit(1);
        }
    }
}
