use std::{
    path::PathBuf,
    sync::{Arc, LazyLock, RwLock},
};

use clap::{Parser, Subcommand};
use owo_colors::{OwoColorize, Style};
use ql_core::{InstanceKind, LAUNCHER_VERSION_NAME, REDACT_SENSITIVE_INFO, WEBSITE, err};

use crate::{
    cli::helpers::render_row,
    menu_renderer::{DISCORD, GITHUB},
};

mod account;
mod command;
mod helpers;

#[derive(Parser)]
#[cfg_attr(target_os = "windows", command(name = ".\\quantum_launcher.exe"))]
#[cfg_attr(not(target_os = "windows"), command(name = "./quantum_launcher"))]
#[command(version = LAUNCHER_VERSION_NAME)]
#[command(long_about = long_about())]
#[command(author = "Mrmayman")]
struct Cli {
    #[clap(subcommand)]
    command: Option<QSubCommand>,
    /// Some systems mistakenly pass this. It's unused though.
    #[arg(long, hide = true)]
    no_sandbox: Option<bool>,
    #[arg(long)]
    no_redact_info: bool,
    #[arg(long)]
    #[arg(help = "Enable experimental server manager (create, delete and host local servers)")]
    enable_server_manager: bool,
    #[arg(long)]
    #[arg(help = "Enable experimental MultiMC import feature (in create instance screen)")]
    enable_mmc_import: bool,
    #[arg(short, long)]
    #[arg(help = "Operate on servers, not instances")]
    #[arg(hide = true)]
    server: bool,
    #[arg(long)]
    dir: Option<PathBuf>,
}

#[derive(Subcommand)]
enum QSubCommand {
    #[command(about = "Creates a new Minecraft instance")]
    Create {
        #[arg(help = "Version of Minecraft to download")]
        version: String,
        instance_name: String,
        #[arg(short, long)]
        #[arg(help = "Skips downloading game assets (sound/music) to speed up downloads")]
        skip_assets: bool,
    },
    #[command(about = "Launches an instance")]
    Launch {
        instance_name: Arc<str>,
        #[arg(help = "Username to play with")]
        username: String,

        // Used by shortcuts, do not break
        #[arg(short, long, short_alias = 'a')]
        #[arg(help = "Whether to use a logged in account of the given username (if any)")]
        use_account: bool,
        // Used by shortcuts
        #[arg(long)]
        show_progress: bool,
        // Used by shortcuts
        #[arg(long)]
        #[arg(help = "microsoft/elyby/littleskin")]
        account_type: Option<String>,
    },
    #[command(aliases = ["list", "list-instances"], short_flag = 'l')]
    #[command(about = "Lists installed instances")]
    ListInstalled { properties: Option<Vec<String>> },
    #[command(about = "Deletes the specified instance")]
    Delete {
        instance_name: String,
        #[arg(short, long)]
        #[arg(help = "Forces deletion without confirmation. DANGEROUS")]
        force: bool,
    },
    #[clap(subcommand)]
    #[clap(alias = "loaders")]
    Loader(QLoader),
    #[command(about = "Lists downloadable versions", short_flag = 'a')]
    ListAvailableVersions,
}

#[derive(Subcommand)]
#[command(
    about = "Manages mod loaders",
    long_about = r"Install, uninstall and look up mod loaders.

Supported loaders: Fabric, Forge, Quilt, NeoForge, Paper, OptiFine
(case-insensitive)"
)]
enum QLoader {
    #[command(about = "Installs the specified loader")]
    #[command(long_about = r"Installs the specified loader

Supported loaders: Fabric, Forge, Quilt, NeoForge, Paper, OptiFine
(case-insensitive)")]
    Install {
        loader: String,
        instance: String,
        more: Option<String>,
        #[arg(long)]
        version: Option<String>,
    },
    Uninstall {
        instance: String,
    },
    #[command(about = "Info about the currently-installed loader")]
    Info {
        instance: String,
    },
}

pub static EXPERIMENTAL_SERVERS: LazyLock<RwLock<bool>> = LazyLock::new(|| RwLock::new(false));
pub static EXPERIMENTAL_MMC_IMPORT: LazyLock<RwLock<bool>> = LazyLock::new(|| RwLock::new(false));

fn long_about() -> String {
    format!(
        r"
QuantumLauncher: A simple, powerful Minecraft launcher

Website: {WEBSITE}
Github : {GITHUB}
Discord: {DISCORD}"
    )
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum PrintCmd {
    Name,
    Version,
    Loader,
}

/// Prints the "intro" to the screen
/// consisting of the **ASCII art logo**, as well as
/// **stylized text saying `QuantumLauncher`**
///
/// The actual data is `include_str!()`ed from
/// - `assets/ascii/icon.txt` for the ASCII art
/// - `assets/ascii/text.txt` for the text logo
///
/// The other files in `assets/ascii` are unused.
fn print_intro() {
    const LOGO: &str = include_str!("../../../assets/ascii/icon.txt");
    const LOGO_WIDTH: u16 = 30;

    let text = get_right_text();

    let Some((terminal_size::Width(width), _)) = terminal_size::terminal_size() else {
        return;
    };

    let draw_contents = &[
        (LOGO.to_owned(), Some(Style::new().purple().bold())),
        (text.clone(), None),
    ];

    // If we got enough space for both side-by-side
    if let Some(res) = render_row(width, draw_contents, false) {
        println!("{res}");
    } else {
        if width >= LOGO_WIDTH {
            // Screen only large enough for Logo, not text
            println!("{}", LOGO.purple().bold());
        }
        println!(
            " {} {}\n",
            "Quantum Launcher".purple().bold(),
            LAUNCHER_VERSION_NAME.purple()
        );
    }
}

fn get_right_text() -> String {
    const TEXT: &str = include_str!("../../../assets/ascii/text.txt");

    let message = format!(
        r"{TEXT}
 {}
 {}
 {}

 For a list of commands type
 {help}",
        "A simple, powerful Minecraft launcher".green().bold(),
        "This window shows debug info;".bright_black(),
        "feel free to ignore it".bright_black(),
        help = "./quantum_launcher --help".yellow()
    );

    message
}

pub fn start_cli(is_dir_err: bool, launcher_dir: &mut Option<PathBuf>) {
    let cli = Cli::parse();
    *REDACT_SENSITIVE_INFO.lock().unwrap() = !cli.no_redact_info;
    *EXPERIMENTAL_SERVERS.write().unwrap() = cli.enable_server_manager;
    *EXPERIMENTAL_MMC_IMPORT.write().unwrap() = cli.enable_mmc_import;

    if let Some(p) = &cli.dir {
        *launcher_dir = Some(p.clone());
        // Safety: Other threads will not write to this right now
        unsafe { std::env::set_var("QLDIR", p) };
    }

    let kind = if cli.server {
        InstanceKind::Server
    } else {
        InstanceKind::Client
    };

    if let Some(subcommand) = cli.command {
        if is_dir_err && cli.dir.is_none() {
            std::process::exit(1);
        }
        let runtime = tokio::runtime::Runtime::new().unwrap();

        match subcommand {
            QSubCommand::Create {
                instance_name,
                version,
                skip_assets,
            } => {
                quit(runtime.block_on(command::create_instance(
                    instance_name,
                    version,
                    skip_assets,
                    kind,
                )));
            }
            QSubCommand::Launch {
                instance_name,
                username,
                use_account,
                show_progress,
                account_type,
            } => {
                let res = runtime.block_on(command::launch_instance(
                    &instance_name,
                    username,
                    use_account,
                    kind,
                    show_progress,
                    account_type.as_deref(),
                ));
                std::process::exit(if let Err(err) = res {
                    err!("{err}");
                    if show_progress {
                        let err = err.to_string();
                        show_notification(
                            "Error launching game",
                            err.strip_prefix("while launching game:\n").unwrap_or(&err),
                        );
                    }
                    1
                } else {
                    0
                });
            }

            QSubCommand::ListAvailableVersions => {
                command::list_available_versions(kind);
                std::process::exit(0);
            }
            QSubCommand::Delete {
                instance_name,
                force,
            } => quit(command::delete_instance(&instance_name, force, kind)),
            QSubCommand::ListInstalled { properties } => {
                quit(command::list_instances(properties.as_deref(), kind));
            }
            QSubCommand::Loader(cmd) => {
                quit(runtime.block_on(command::loader(cmd, kind)));
            }
        }
    } else {
        print_intro();
    }
}

fn show_notification(title: &str, body: &str) {
    #[cfg(not(target_os = "macos"))]
    {
        _ = notify_rust::Notification::new()
            .summary(title)
            .body(body)
            .show();
    }
    #[cfg(target_os = "macos")]
    {
        _ = std::process::Command::new("osascript")
            .args([
                "-e",
                &format!("display notification {body:?} with title {title:?}"),
                "-e",
                "delay 5",
            ])
            .spawn();
    }
}

fn quit(res: Result<(), Box<dyn std::error::Error + 'static>>) {
    std::process::exit(if let Err(err) = res {
        err!("{err}");
        1
    } else {
        0
    });
}
