use std::{path::PathBuf, str::FromStr, sync::Arc};

use babylonia_terminal_sdk::{
    components::{
        dxvk_component::{DXVK_DEV, DXVK_REPO},
        wine_component::{WineComponent, WINE_DEV, WINE_REPO},
    },
    game_config::GameConfig,
    game_manager::{EnvironmentVariable, GameManager},
    game_state::GameState,
};

use log::{debug, info};
use tokio::io::{AsyncBufReadExt, BufReader};
use wincompatlib::prelude::*;

use crate::{reporter::DownloadReporter, utils};

pub async fn run(
    launch_options: Option<String>,
    env_vars: Vec<EnvironmentVariable>,
    show_logs: bool,
) {
    let mut wine_component: Option<WineComponent> = None;
    let mut wine: Option<Wine> = None;

    // Deleting old setup
    if None == GameConfig::get_config().await.launcher_version {
        info!("You seem to have the old setup to play the game.");
        info!("do you want to delete it to setup the new one ? (y/n) (default - yes): ");

        let input = BufReader::new(tokio::io::stdin())
            .lines()
            .next_line()
            .await
            .unwrap();

        if None == input
            || Some("".to_string()) == input
            || Some("y".to_string()) == input
            || Some("yes".to_string()) == input
        {
            info!("Deleting old setup...");
            babylonia_terminal_sdk::utils::remove_setup().await;
            info!("done!");
        } else {
            info!("Keeping old setup...");
        }
    }

    loop {
        let state_result = GameState::get_current_state().await;
        if let Err(error) = state_result {
            info!("Something goes wrong : {:?}", error);
            break;
        }
        let state = state_result.unwrap();

        if state != GameState::WineNotInstalled && wine == None {
            let wine_component = WineComponent::new(GameConfig::get_config_directory().await);
            match wine_component.init_wine() {
                Ok(p) => wine = Some(p),
                Err(err) => panic!("{}", err),
            };
        }

        match state {
            GameState::WineNotInstalled => {
                let release;
                if utils::use_latest(
                    "Do you want to install latest version of wine GE or a specific version of it?",
                ) {
                    release = 0;
                } else {
                    release = utils::choose_release_version(
                        WINE_DEV,
                        WINE_REPO,
                        "Please, select a version of wine GE to install.",
                    )
                    .await
                    .expect("Failed to fetch wine version!");
                }

                info!("Wine not installed, installing it...");
                wine_component = Some(
                    GameManager::install_wine(
                        GameConfig::get_config_directory().await,
                        release,
                        Some(DownloadReporter::create(false)),
                    )
                    .await
                    .expect("Failed to install wine"),
                );
                info!("wine installed");
            }
            GameState::DXVKNotInstalled => {
                let release;
                if utils::use_latest(
                    "Do you want to install latest version of DXVK or a specific version of it?",
                ) {
                    release = 0;
                } else {
                    release = utils::choose_release_version(
                        DXVK_DEV,
                        DXVK_REPO,
                        "Please, select a version of DXVK to install.",
                    )
                    .await
                    .expect("Failed to fetch DXVK version!");
                }

                info!("DXVK not installed, installing it...");
                debug!("{:?}", wine_component);
                GameManager::install_dxvk(
                    &wine.clone().unwrap(),
                    GameConfig::get_config_directory().await,
                    release,
                    Some(DownloadReporter::create(false)),
                )
                .await
                .expect("Failed to installed DXVK");
                info!("DXVK installed");
            }
            GameState::FontNotInstalled => {
                info!("Fonts not installed, installing it...");
                GameManager::install_font(&wine.clone().unwrap(), None::<Arc<DownloadReporter>>)
                    .await
                    .expect("Failed to install fonts");
                info!("Fonts installed");
            }
            GameState::DependecieNotInstalled => {
                info!("Dependecies not installed, installing it...");
                GameManager::install_dependencies(&wine.clone().unwrap())
                    .await
                    .expect("Failed to install dependecies");
                info!("Dependecies installed");
            }
            GameState::GameNotInstalled => {
                info!("Game not installed, installing it...");
                if GameConfig::get_game_dir().await.is_none() {
                    info!(
                        "You can choose where to put your game directory, (default '{}')",
                        GameConfig::get_config_directory().await.to_str().unwrap(),
                    );
                    info!("Please enter your wanted game directory : ");
                    let mut input = BufReader::new(tokio::io::stdin())
                        .lines()
                        .next_line()
                        .await
                        .unwrap();

                    let dir;
                    if let Some(i) = &mut input {
                        if i.is_empty() {
                            dir = GameConfig::get_config_directory().await;
                        } else {
                            dir = PathBuf::from_str(i).expect("This is not a valid directory!\n Please restart the launcher and put a valid path.");
                        }
                    } else {
                        dir = GameConfig::get_config_directory().await;
                    }

                    GameConfig::set_game_dir(Some(dir)).await.expect(
                        "Failed to save the game directory into the config file, please retry!",
                    );
                }

                GameManager::install_game(
                    GameConfig::get_game_dir().await.unwrap(),
                    DownloadReporter::create(false),
                )
                .await
                .expect("Failed to install the game");
            }
            GameState::GameNeedUpdate => {
                info!("Game need an update, updating it");
                info!("This will restart the installation process...");
                GameManager::update_game()
                    .await
                    .expect("Failed to start the installation process");
            }
            GameState::GameNotPatched => {
                info!("Patching game...");
                GameManager::patch_game(GameConfig::get_game_dir().await.unwrap())
                    .await
                    .expect("Failed to patch the game");
                info!("Game patched!");
            }
            GameState::GameInstalled => {
                break;
            }
        }
    }

    info!("Starting game...");
    debug!("{:?}", wine);
    GameManager::start_game(
        &wine.unwrap(),
        GameConfig::get_game_dir()
            .await
            .expect("Failed to start game, the game directory was not found"),
        launch_options,
        env_vars,
        show_logs,
    )
    .await;
}
