use async_trait::async_trait;
use serde::Deserialize;
use super::{DownloadSource, Downloader};
use crate::{package::PackageName, prelude::*, version::structs::VersionMeta};
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
pub struct LoaderMetaItem {
    pub loader: LoaderStruct,
    pub intermediary: IntermediaryStruct,
}
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
pub struct IntermediaryStruct {
    pub maven: String,
    pub version: String,
    pub stable: bool,
}
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
pub struct LoaderStruct {
    pub maven: String,
    pub version: String,
    pub stable: bool,
}
#[async_trait]
pub trait FabricDownloadExt: Sync {
    async fn get_avaliable_loaders(&self, vanilla_version: &str) -> DynResult<Vec<LoaderMetaItem>>;
    async fn download_library(&self, name: &str) -> DynResult;
    async fn download_fabric_pre(
        &self,
        version_name: &str,
        version_id: &str,
        loader_version: &str,
    ) -> DynResult;
    async fn download_fabric_post(&self, version_name: &str) -> DynResult;
}
#[async_trait]
impl<R: Reporter> FabricDownloadExt for Downloader<R> {
    async fn get_avaliable_loaders(&self, vanilla_version: &str) -> DynResult<Vec<LoaderMetaItem>> {
        let mut result = crate::http::retry_get(match self.source {
            DownloadSource::Default => {
                format!("https://meta.fabricmc.net/v2/versions/loader/{vanilla_version}")
            }
            DownloadSource::BMCLAPI => format!(
                "https://bmclapi2.bangbang93.com/fabric-meta/v2/versions/loader/{vanilla_version}"
            ),
            DownloadSource::MCBBS => format!(
                "https://download.mcbbs.net/fabric-meta/v2/versions/loader/{vanilla_version}"
            ),
            _ => format!("https://meta.fabricmc.net/v2/versions/loader/{vanilla_version}"),
        })
        .await
        .map_err(|e| {
            anyhow::anyhow!(
                "获取为原版 {} 可用的 Fabric Loader 版本失败 {:?}",
                vanilla_version,
                e
            )
        })?;
        if result.status().is_success() {
            let result = result.body_json().await.map_err(|e| anyhow::anyhow!(e))?;
            Ok(result)
        } else {
            Ok(vec![])
        }
    }
    async fn download_library(&self, name: &str) -> DynResult {
        let package_name = name.parse::<PackageName>().unwrap();
        let full_path = package_name.to_maven_jar_path(self.minecraft_library_path.as_str());
        let r = self.reporter.sub();
        inner_future::fs::create_dir_all(
            &full_path[..full_path.rfind('/').unwrap_or(full_path.len())],
        )
        .await
        .unwrap_or_default();
        r.set_message(format!("正在下载 Fabric 支持库 {name}"));
        r.add_max_progress(1.);
        if std::path::Path::new(&full_path).is_file() {
            if self.verify_data {
                let mut file = inner_future::fs::OpenOptions::new()
                    .read(true)
                    .open(&full_path)
                    .await?;
                r.set_message(format!("正在获取数据摘要以验证完整性 {name}"));
                r.add_max_progress(1.);
                let sha1 = crate::http::retry_get_string(format!(
                    "{}.sha1",
                    package_name.to_maven_jar_path(match self.source {
                        DownloadSource::BMCLAPI => "https://bmclapi2.bangbang93.com/maven",
                        DownloadSource::MCBBS => "https://download.mcbbs.net/maven",
                        _ => "https://maven.fabricmc.net",
                    })
                ))
                .await?;
                let current_sha1 = crate::utils::get_data_sha1(&mut file).await?;
                if sha1 == current_sha1 {
                    r.add_progress(1.);
                    return Ok(());
                }
            } else {
                r.add_progress(1.);
                return Ok(());
            }
        }
        let uris = [
            package_name.to_maven_jar_path(match self.source {
                DownloadSource::BMCLAPI => "https://bmclapi2.bangbang93.com/maven",
                DownloadSource::MCBBS => "https://download.mcbbs.net/maven",
                _ => "https://maven.fabricmc.net",
            }),
            package_name.to_maven_jar_path("https://bmclapi2.bangbang93.com/maven"),
            package_name.to_maven_jar_path("https://download.mcbbs.net/maven"),
            package_name.to_maven_jar_path("https://maven.fabricmc.net"),
        ];
        crate::http::download(&uris, &full_path, 0)
            .await
            .map_err(|e| anyhow::anyhow!("下载 Fabric 依赖库失败:{:?}", e))?;
        Ok(())
    }
    async fn download_fabric_pre(
        &self,
        version_name: &str,
        version_id: &str,
        loader_version: &str,
    ) -> DynResult {
        let mut loader_meta_res = crate::http::retry_get(format!(
            "https://meta.fabricmc.net/v2/versions/loader/{version_id}/{loader_version}/profile/json"
        ))
        .await
        .map_err(|e| anyhow::anyhow!("获取 Fabric 版本元数据失败:{:?}", e))?;
        let res = loader_meta_res
            .body_bytes()
            .await
            .map_err(|e| anyhow::anyhow!(e))?;
        inner_future::fs::write(
            format!(
                "{}/{}/{}-fabric-loader.tmp.json",
                self.minecraft_version_path.as_str(),
                version_name,
                version_name
            ),
            &res,
        )
        .await?;
        let meta: VersionMeta = serde_json::from_slice(&res)?;
        let mut libraries_threads = Vec::with_capacity(meta.libraries.len());
        for lib in &meta.libraries {
            if !lib.name.is_empty() {
                libraries_threads.push(self.download_library(&lib.name));
            }
        }
        futures::future::try_join_all(libraries_threads).await?;
        Ok(())
    }
    async fn download_fabric_post(&self, version_name: &str) -> DynResult {
        let vanilla_path = format!(
            "{}/{}/{}.json",
            self.minecraft_version_path.as_str(),
            version_name,
            version_name
        );
        let vanilla_meta = crate::prelude::inner_future::fs::read(&vanilla_path).await?;
        let loader_path = format!(
            "{}/{}/{}-fabric-loader.tmp.json",
            self.minecraft_version_path.as_str(),
            version_name,
            version_name
        );
        let loader_meta = crate::prelude::inner_future::fs::read(&loader_path).await?;
        inner_future::fs::remove_file(loader_path).await?;
        let mut vanilla_meta: VersionMeta = serde_json::from_slice(&vanilla_meta)?;
        let loader_meta: VersionMeta = serde_json::from_slice(&loader_meta)?;
        vanilla_meta += loader_meta;
        inner_future::fs::write(&vanilla_path, serde_json::to_vec(&vanilla_meta)?).await?;
        Ok(())
    }
}