use std::error::Error;
use std::path::Path;
use serde::{Serialize, Deserialize};
use crate::HttpClient;
use crate::models::repository::{RepositoryPlugin, RepositorySource};
use crate::crawlers::Repository;
use urlencoding;
use async_trait::async_trait;
use std::sync::Arc;

// HangarMC API response structures
#[derive(Debug, Serialize, Deserialize)]
struct HangarProjectsResponse {
    pagination: HangarPagination,
    result: Vec<HangarProject>,
}

#[derive(Debug, Serialize, Deserialize)]
struct HangarPagination {
    limit: u32,
    offset: u32,
    count: u32,
}

#[derive(Debug, Serialize, Deserialize)]
struct HangarProject {
    id: String,
    name: String,
    namespace: HangarNamespace,
    category: String,
    description: Option<String>,
    stats: HangarStats,
    last_updated: String,
    icon_url: Option<String>,
    created_at: String,
    visibility: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct HangarNamespace {
    owner: String,
    slug: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct HangarStats {
    views: u64,
    downloads: u64,
    recent_views: u64,
    recent_downloads: u64,
    stars: u64,
    watchers: u64,
}

#[derive(Debug, Serialize, Deserialize)]
struct HangarVersionsResponse {
    pagination: HangarPagination,
    result: Vec<HangarVersion>,
}

#[derive(Debug, Serialize, Deserialize)]
struct HangarVersion {
    name: String,
    created_at: String,
    description: Option<String>,
    downloads: u64,
    file_size: u64,
    platform_versions: Vec<String>,
}

// Hangar crawler implementation
pub struct HangarCrawler {
    client: Arc<HttpClient>,
    api_base_url: String,
    web_base_url: String,
}

impl HangarCrawler {
    pub fn new() -> Self {
        HangarCrawler {
            client: Arc::new(HttpClient::new()),
            api_base_url: "https://hangar.papermc.io/api/v1".to_string(),
            web_base_url: "https://hangar.papermc.io".to_string(),
        }
    }

    async fn get_project_details_internal(&self, author: &str, slug: &str) -> Result<HangarProject, Box<dyn Error + Send + Sync>> {
        let url = format!("{}/projects/{}/{}", self.api_base_url, author, slug);
        let response_body = self.client.get(&url).await?;
        let project: HangarProject = serde_json::from_str(&response_body)?;
        Ok(project)
    }

    async fn get_project_versions_internal(&self, author: &str, slug: &str) -> Result<Vec<HangarVersion>, Box<dyn Error + Send + Sync>> {
        let url = format!("{}/projects/{}/{}/versions?limit=25&offset=0", self.api_base_url, author, slug);
        let response_body = self.client.get(&url).await?;
        let versions_result: HangarVersionsResponse = serde_json::from_str(&response_body)?;
        Ok(versions_result.result)
    }
}

#[async_trait]
impl Repository for HangarCrawler {
    fn get_repository_name(&self) -> String {
        "HangarMC".to_string()
    }

    async fn search(&self, query: &str) -> Result<Vec<RepositoryPlugin>, String> {
        let encoded_query = urlencoding::encode(query);
        let url = format!(
            "{}/projects?q={}",
            self.api_base_url,
            encoded_query
        );

        let response_body = match self.client.get(&url).await {
            Ok(body) => body,
            Err(e) => return Err(format!("Failed to search HangarMC: {}", e)),
        };

        let search_response: HangarProjectsResponse = match serde_json::from_str(&response_body) {
            Ok(res) => res,
            Err(e) => return Err(format!("Failed to parse HangarMC search results: {}", e)),
        };

        let results: Vec<RepositoryPlugin> = search_response.result.into_iter().map(|proj| {
            let page_url = format!("{}/{}/{}", self.web_base_url, proj.namespace.owner, proj.namespace.slug);
            let version = "Unknown".to_string();
            RepositoryPlugin {
                id: format!("{}/{}", proj.namespace.owner, proj.namespace.slug),
                name: proj.name,
                version,
                description: proj.description.clone(),
                authors: vec![proj.namespace.owner],
                download_url: String::new(),
                repository: RepositorySource::HangarMC,
                page_url,
                download_count: Some(proj.stats.downloads),
                last_updated: Some(proj.last_updated),
                icon_url: proj.icon_url.clone(),
                minecraft_versions: Vec::new(),
                categories: vec![proj.category.to_string()],
                rating: Some(proj.stats.stars as f32),
                file_size: None,
                file_hash: None,
                changelog: None,
            }
        }).collect();

        Ok(results)
    }

    async fn get_plugin_details(&self, plugin_id: &str) -> Result<RepositoryPlugin, String> {
        let parts: Vec<&str> = plugin_id.split('/').collect();
        if parts.len() != 2 {
            return Err(format!("Invalid Hangar plugin ID format: {}. Expected 'author/slug'.", plugin_id));
        }
        let author = parts[0];
        let slug = parts[1];

        let project = match self.get_project_details_internal(author, slug).await {
            Ok(p) => p,
            Err(e) => return Err(format!("Failed to get Hangar project details: {}", e)),
        };

        let versions = match self.get_project_versions_internal(author, slug).await {
            Ok(v) => v,
            Err(e) => return Err(format!("Failed to get Hangar project versions: {}", e)),
        };

        let latest_version_name = versions.first().map_or("Unknown".to_string(), |v| v.name.clone());

        let page_url = format!("{}/{}/{}", self.web_base_url, author, slug);

        Ok(RepositoryPlugin {
            id: plugin_id.to_string(),
            name: project.name,
            version: latest_version_name,
            description: project.description.clone(),
            authors: vec![project.namespace.owner],
            download_url: String::new(),
            repository: RepositorySource::HangarMC,
            page_url,
            download_count: Some(project.stats.downloads),
            last_updated: Some(project.last_updated),
            icon_url: project.icon_url.clone(),
            minecraft_versions: versions.first().map_or(Vec::new(), |v| v.platform_versions.clone()),
            categories: vec![project.category.to_string()],
            rating: Some(project.stats.stars as f32),
            file_size: versions.first().map(|v| v.file_size),
            file_hash: None,
            changelog: None,
        })
    }

    async fn download_plugin(&self, plugin_id: &str, version: &str, destination: &Path) -> Result<String, String> {
        let parts: Vec<&str> = plugin_id.split('/').collect();
        if parts.len() != 2 {
            return Err(format!("Invalid Hangar plugin ID format: {}. Expected 'author/slug'.", plugin_id));
        }
        let author = parts[0];
        let slug = parts[1];

        let platform_str = "PAPER";

        let download_url = format!(
            "{}/projects/{}/{}/versions/{}/{}/download",
            self.api_base_url,
            author,
            slug,
            version,
            platform_str
        );

        match self.client.download(&download_url, destination).await {
            Ok(_) => Ok(destination.to_string_lossy().to_string()),
            Err(e) => Err(format!("Failed to download plugin: {}", e))
        }
    }
}