use crate::https;
use crate::java;
use crate::system_dirs::{
    get_data_directory, get_java_executable, get_local_data_directory, get_prism_executable,
};
use crate::util;
use reqwest::IntoUrl;
use serde::de::value::Error;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
use std::fs;
use std::fs::File;
use std::io::{Read, Seek, Write};
use std::process::Command;
use std::time::Duration;
use std::{env, thread};
use std::{io::Cursor, path::PathBuf};

#[derive(Serialize, Deserialize, Clone)]
pub struct ModpackEntry {
    pub name: String,
    pub id: String,
    pub last_updated: String,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct VersionEntry {
    pub Version: String,
    pub File: String,
    pub Date: String,
}

async fn get_modpack_name(id: String) -> String {
    let modpacks = get_modpacks().await;
    let mut instance_name = String::new();
    for pack in modpacks {
        if pack.id == id {
            instance_name = pack.name;
        }
    }
    return instance_name;
}

async fn check_modpack_needs_update(id: String) -> bool {
    let mut instance_name = get_modpack_name(id.clone()).await;
    if !get_local_data_directory()
        .join("prism")
        .join("instances")
        .join(&mut instance_name)
        .exists()
    {
        return true;
    }

    let versions = get_versions(id).await;
    if !versions.is_ok() {
        return false;
    }
    let versions = versions.unwrap();
    let latest = versions[versions.len() - 1].Version.clone();

    let mut file = File::open(
        get_local_data_directory()
            .join("prism")
            .join("instances")
            .join(instance_name)
            .join(".minecraft")
            .join("version.txt"),
    )
    .unwrap();
    let mut current = String::new();
    file.read_to_string(&mut current);

    if latest != current {
        return true;
    }

    return false;
}

#[tauri::command]
pub async fn launch_modpack(window: tauri::AppHandle, id: String) {
    if check_modpack_needs_update(id.clone()).await {
        install_modpack(window, id.clone()).await;
    }
    // Launch
    let mut child = Command::new(
        get_local_data_directory()
            .join("prism")
            .join(get_prism_executable()),
    )
    .arg("-l")
    .arg(get_modpack_name(id).await)
    .spawn()
    .unwrap();
}

async fn install_modpack(window: tauri::AppHandle, id: String) {
    let versions = get_versions(id.clone()).await.unwrap();
    let path = env::temp_dir().join(format!("{}.mrpack", get_modpack_name(id.clone()).await));
    let mut file = File::create(path.clone()).unwrap();
    let ftp_path = PathBuf::new()
        .join(id.clone())
        .join(versions[versions.len() - 1].File.clone().as_str());
    println!("Downloading file {}", ftp_path.to_str().unwrap());
    //ftp::ftp_retr(Some(window.clone()), ftp_path, &mut file, |window, data, size| util::download_callback(name.clone(), window, data, size)).unwrap();
    https::download(
        Some(window.clone()),
        format!(
            "https://gitea.piwalker.net/fclauncher/{}/{}",
            id.clone(),
            versions[versions.len() - 1].File.clone()
        ),
        &mut file,
        get_modpack_name(id.clone()).await,
    )
    .await;
    let mut child = Command::new(
        get_local_data_directory()
            .join("prism")
            .join(get_prism_executable()),
    )
    .arg("-I")
    .arg(path)
    .spawn()
    .unwrap();
    loop {
        let version_path = get_local_data_directory()
            .join("prism")
            .join("instances")
            .join(get_modpack_name(id.clone()).await)
            .join(".minecraft")
            .join("version.txt");
        if version_path.clone().exists() {
            let mut ver_file = File::open(version_path).unwrap();
            let mut buf = String::new();
            ver_file.read_to_string(&mut buf).unwrap();
            if buf == versions[versions.len() - 1].Version.clone().as_str() {
                break;
            }
        }
        thread::sleep(Duration::from_secs(3));
    }
    thread::sleep(Duration::from_secs(1));
    child.kill();
    let info_path = get_local_data_directory()
        .join("prism")
        .join("instances")
        .join(get_modpack_name(id.clone()).await)
        .join("mmc-pack.json");
    let mut info_file = File::open(info_path.clone()).unwrap();
    let info_json: Value = serde_json::from_reader(info_file).unwrap();
    let mut mc_version = "0.0";
    for component in info_json["components"].as_array().unwrap() {
        if component["uid"] == "net.minecraft" {
            mc_version = component["version"].as_str().unwrap();
        }
    }
    let java = get_java_version(mc_version);
    java::install_java(java, window.clone()).await;

    let option_path = get_local_data_directory()
        .join("prism")
        .join("instances")
        .join(get_modpack_name(id.clone()).await)
        .join("instance.cfg");
    let mut option_file = File::open(option_path.clone()).unwrap();
    let mut buf = String::new();
    option_file.read_to_string(&mut buf);
    let mut option_file = File::create(option_path).unwrap();
    let mut set = false;
    for line in buf.lines() {
        if line.starts_with("JavaPath=") {
            option_file.write_all(
                format!(
                    "JavaPath={}/java-{}-{}/bin/{}\n",
                    get_local_data_directory()
                        .join("java")
                        .into_os_string()
                        .to_str()
                        .unwrap()
                        .replace("\\", "/"),
                    java,
                    if env::consts::OS == "windows" {
                        "win"
                    } else {
                        "lin"
                    },
                    get_java_executable()
                )
                .as_bytes(),
            );
            set = true;
        } else {
            option_file.write_all(format!("{}\n", line).as_bytes());
        }
    }
    if !set {
        option_file.write_all(
            format!(
                "JavaPath={}/java-{}-{}/bin/{}\n",
                get_local_data_directory()
                    .join("java")
                    .into_os_string()
                    .to_str()
                    .unwrap()
                    .replace("\\", "/"),
                java,
                if env::consts::OS == "windows" {
                    "win"
                } else {
                    "lin"
                },
                get_java_executable()
            )
            .as_bytes(),
        );
        option_file.write_all("OverrideJavaLocation=true\n".as_bytes());
        option_file.write_all("OverrideJava=true\n".as_bytes());
    }
}

#[tauri::command]
pub async fn get_modpacks() -> Vec<ModpackEntry> {
    //unsafe{
    let mut modpacks: Vec<ModpackEntry> = Vec::new();
    //if modpacks.is_empty() {
    let mut buf = Cursor::new(vec![]);
    //ftp::ftp_retr(None, PathBuf::new().join("modpacks.json"), &mut buf, |_, _, _| return);
    https::download(
        None,
        format!("https://gitea.piwalker.net/fclauncher/modpacks.json"),
        &mut buf,
        format!("modpacks.json"),
    )
    .await;
    buf.rewind();
    let res = serde_json::from_reader(buf);
    if !res.is_ok() {
        println!("Result not ok!");
        let paths =
            fs::read_dir(get_local_data_directory().join("prism").join("instances")).unwrap();
        for path in paths {
            let path = path.unwrap();
            if fs::metadata(path.path()).unwrap().is_file() {
                continue;
            }
            let name = path.file_name().into_string().unwrap();
            if name.starts_with(".") {
                continue;
            }

            modpacks.push(ModpackEntry {
                name: name.clone(),
                id: name,
                last_updated: format!(""),
            })
        }
        return modpacks.clone();
    }
    let modpacks: Vec<ModpackEntry> = res.unwrap();
    //println!("{}", v[0].name);
    //for pack in v.as_array().unwrap() {
    //modpacks.push(ModpackEntry{name: pack["name"].as_str().unwrap().to_string(), id: pack["id"].as_str().unwrap().to_string(), last_updated: pack["last-updated"].as_str().unwrap().to_string()});
    //}
    //}
    return modpacks.clone();
    //}
}

#[tauri::command]
pub async fn get_versions(id: String) -> Result<Vec<VersionEntry>, String> {
    let mut versions: Vec<VersionEntry> = Vec::new();
    let mut buf = Cursor::new(vec![]);
    //ftp::ftp_retr(None, PathBuf::new().join(id).join("versions.json"), &mut buf, |_, _, _| return);
    https::download(
        None,
        format!(
            "https://gitea.piwalker.net/fclauncher/{}/versions.json",
            id.clone()
        ),
        &mut buf,
        format!("{}/versions.json", id.clone()),
    )
    .await;
    buf.rewind();
    let versions: Vec<VersionEntry> =
        serde_json::from_reader(buf).or(Err(format!("Unable to parse json")))?;
    //for version in v.as_array().unwrap() {
    //versions.push(VersionEntry{version: version["Version"].as_str().unwrap().to_string(), file: version["File"].as_str().unwrap().to_string(), date: version["Date"].as_str().unwrap().to_string()});
    //}
    return Ok(versions.clone());
}

fn get_java_version(mc_version: &str) -> u8 {
    let components: Vec<&str> = mc_version.split(".").collect();
    let mut java = 8;
    if components[1] == "17" {
        java = 17
    } else if components[1] == "18" || components[1] == "19" {
        java = 17
    } else if components[1].parse::<i32>().unwrap() > 19 {
        if components[1] == "20" && components[1].parse::<i32>().unwrap() < 5 {
            java = 17
        } else {
            java = 21
        }
    }
    return java;
}

#[tauri::command]
pub async fn get_latest_version(id: String) -> Result<String, String> {
    let versions = get_versions(id).await.unwrap();
    if (versions.len() == 0) {
        return Ok(format!(""));
    }
    Ok(versions[versions.len() - 1].Version.clone())
}

//pub fn create_json(modpacks: Vec<ModpackEntry>) -> Result<serde_json::Value, String> {

//}
