use std::path::Path;

use ql_core::{
    Instance, InstanceKind, IntoIoError, IntoJsonError, IoError, LAUNCHER_DIR, Loader, err,
    file_utils::exists, info, json::FabricJSON,
};

use crate::loaders::change_instance_type;

use super::error::FabricInstallError;

async fn delete(server_dir: &Path, name: &str) -> Result<(), IoError> {
    let path = server_dir.join(name);
    if exists(&path).await {
        tokio::fs::remove_file(&path).await.path(&path)?;
    }

    Ok(())
}

async fn uninstall_server(server_name: &str) -> Result<(), FabricInstallError> {
    let server_dir = LAUNCHER_DIR.join("servers").join(server_name);

    info!("Uninstalling fabric from server: {server_name}");

    delete(&server_dir, "fabric-server-launch.jar").await?;
    delete(&server_dir, "fabric-server-launcher.properties").await?;

    let json_path = server_dir.join("fabric.json");
    if exists(&json_path).await {
        let json = tokio::fs::read_to_string(&json_path)
            .await
            .path(&json_path)?;
        let json: FabricJSON = serde_json::from_str(&json).json(json)?;
        tokio::fs::remove_file(&json_path).await.path(&json_path)?;

        let libraries_dir = server_dir.join("libraries");

        if libraries_dir.is_dir() {
            for library in &json.libraries {
                let library_path = libraries_dir.join(library.get_path());
                if exists(&library_path).await {
                    tokio::fs::remove_file(&library_path)
                        .await
                        .path(&library_path)?;
                }
            }
        }
    }

    change_instance_type(&server_dir, Loader::Vanilla, None).await?;
    info!("Finished uninstalling fabric");

    Ok(())
}

async fn uninstall_client(instance_name: &str) -> Result<(), FabricInstallError> {
    let instance_dir = LAUNCHER_DIR.join("instances").join(instance_name);

    let libraries_dir = instance_dir.join("libraries");

    let fabric_json_path = instance_dir.join("fabric.json");
    if exists(&fabric_json_path).await {
        let fabric_json = tokio::fs::read_to_string(&fabric_json_path)
            .await
            .path(&fabric_json_path)?;
        if let Ok(FabricJSON { libraries, .. }) =
            serde_json::from_str(&fabric_json).json(fabric_json)
        {
            tokio::fs::remove_file(&fabric_json_path)
                .await
                .path(fabric_json_path)?;

            for library in &libraries {
                let library_path = libraries_dir.join(library.get_path());
                if exists(&library_path).await {
                    if let Err(err) = tokio::fs::remove_file(&library_path)
                        .await
                        .path(library_path)
                    {
                        err!("While uninstalling fabric/quilt: {err}");
                    }
                }
            }
        }
    }

    let cache_dir = instance_dir.join(".minecraft/.fabric");
    if tokio::fs::try_exists(&cache_dir).await.path(&cache_dir)? {
        tokio::fs::remove_dir_all(&cache_dir)
            .await
            .path(&cache_dir)?;
    }

    change_instance_type(&instance_dir, Loader::Vanilla, None).await?;
    Ok(())
}

pub async fn uninstall(instance: Instance) -> Result<(), FabricInstallError> {
    let name = instance.get_name();
    match instance.kind {
        InstanceKind::Client => uninstall_client(name).await,
        InstanceKind::Server => uninstall_server(name).await,
    }
}
