use std::fmt;

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

/// Extension type enumeration
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ExtensionType {
    Provider,
    Taskserv,
    Cluster,
}

impl fmt::Display for ExtensionType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ExtensionType::Provider => write!(f, "provider"),
            ExtensionType::Taskserv => write!(f, "taskserv"),
            ExtensionType::Cluster => write!(f, "cluster"),
        }
    }
}

impl std::str::FromStr for ExtensionType {
    type Err = crate::error::RegistryError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "provider" | "providers" => Ok(ExtensionType::Provider),
            "taskserv" | "taskservs" => Ok(ExtensionType::Taskserv),
            "cluster" | "clusters" => Ok(ExtensionType::Cluster),
            _ => Err(crate::error::RegistryError::InvalidType(s.to_string())),
        }
    }
}

/// Extension source backend
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ExtensionSource {
    Gitea,
    Forgejo,
    Github,
    Oci,
}

impl fmt::Display for ExtensionSource {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ExtensionSource::Gitea => write!(f, "gitea"),
            ExtensionSource::Forgejo => write!(f, "forgejo"),
            ExtensionSource::Github => write!(f, "github"),
            ExtensionSource::Oci => write!(f, "oci"),
        }
    }
}

/// Extension metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Extension {
    pub name: String,
    #[serde(rename = "type")]
    pub extension_type: ExtensionType,
    pub version: String,
    pub description: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub author: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub repository: Option<String>,
    pub source: ExtensionSource,
    pub published_at: DateTime<Utc>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub download_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub checksum: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub size: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tags: Option<Vec<String>>,
}

impl Extension {
    /// Create unique cache key for extension
    pub fn cache_key(&self) -> String {
        format!("{}/{}/{}", self.extension_type, self.name, self.version)
    }

    /// Get extension identifier (type/name)
    pub fn identifier(&self) -> String {
        format!("{}/{}", self.extension_type, self.name)
    }
}

/// Extension version information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtensionVersion {
    pub version: String,
    pub published_at: DateTime<Utc>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub download_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub checksum: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub size: Option<u64>,
}

/// Extension list parameters
#[derive(Debug, Clone, Deserialize)]
pub struct ListParams {
    #[serde(rename = "type")]
    pub extension_type: Option<ExtensionType>,
    pub source: Option<ExtensionSource>,
    pub limit: Option<usize>,
    pub offset: Option<usize>,
}

impl Default for ListParams {
    fn default() -> Self {
        Self {
            extension_type: None,
            source: None,
            limit: Some(100),
            offset: Some(0),
        }
    }
}

/// Search parameters
#[derive(Debug, Clone, Deserialize)]
pub struct SearchParams {
    pub q: String,
    #[serde(rename = "type")]
    pub extension_type: Option<ExtensionType>,
    pub limit: Option<usize>,
}

impl Default for SearchParams {
    fn default() -> Self {
        Self {
            q: String::new(),
            extension_type: None,
            limit: Some(50),
        }
    }
}

/// Health check response
#[derive(Debug, Serialize)]
pub struct HealthResponse {
    pub status: String,
    pub version: String,
    pub uptime: u64,
    pub backends: BackendHealth,
}

/// Backend health status
#[derive(Debug, Serialize)]
pub struct BackendHealth {
    pub gitea: BackendStatus,
    pub oci: BackendStatus,
}

/// Individual backend status
#[derive(Debug, Serialize)]
pub struct BackendStatus {
    pub enabled: bool,
    pub healthy: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,
}
