use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use std::sync::{Arc, OnceLock};

#[derive(Debug, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct Config {
    pub directory: DirectoryConfig,
    pub git: GitConfig,
    pub status: StatusConfig,
    pub metadata: MetadataConfig,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct DirectoryConfig {
    pub icon: String,
    pub truncation_length: usize,
    pub truncate_to_repo: bool,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct GitConfig {
    pub branch_icon: String,
    pub staged_icon: String,
    pub unstaged_icon: String,
    pub untracked_icon: String,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct StatusConfig {
    pub success_icon: String,
    pub failure_icon: String,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct MetadataConfig {
    pub time_color: String,
    pub paneship_color: String,
    pub languages: HashMap<String, LanguageStyleConfig>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(default)]
pub struct LanguageStyleConfig {
    pub icon: String,
    pub color: String,
}

impl Default for DirectoryConfig {
    fn default() -> Self {
        Self {
            icon: "\u{f07b}".to_string(),
            truncation_length: 3,
            truncate_to_repo: true,
        }
    }
}

impl Default for GitConfig {
    fn default() -> Self {
        Self {
            branch_icon: "\u{e0a0}".to_string(),
            staged_icon: "+".to_string(),
            unstaged_icon: "~".to_string(),
            untracked_icon: "?".to_string(),
        }
    }
}

impl Default for StatusConfig {
    fn default() -> Self {
        Self {
            success_icon: "\u{276f}".to_string(),
            failure_icon: "\u{276f}".to_string(),
        }
    }
}

impl Default for MetadataConfig {
    fn default() -> Self {
        Self {
            time_color: "2;37".to_string(),
            paneship_color: "1;32".to_string(),
            languages: default_language_styles(),
        }
    }
}

impl Default for LanguageStyleConfig {
    fn default() -> Self {
        Self {
            icon: "?".to_string(),
            color: "1;37".to_string(),
        }
    }
}

impl MetadataConfig {
    pub fn language_style(&self, name: &str) -> LanguageStyleConfig {
        self.languages.get(name).cloned().unwrap_or_else(|| {
            let mut defaults = default_language_styles();
            defaults.remove(name).unwrap_or_default()
        })
    }
}

fn default_language_styles() -> HashMap<String, LanguageStyleConfig> {
    let mut styles = HashMap::new();

    styles.insert(
        "rust".to_string(),
        LanguageStyleConfig {
            icon: "🦀".to_string(),
            color: "1;33".to_string(),
        },
    );
    styles.insert(
        "node".to_string(),
        LanguageStyleConfig {
            icon: "⬢".to_string(),
            color: "1;32".to_string(),
        },
    );
    styles.insert(
        "bun".to_string(),
        LanguageStyleConfig {
            icon: "🥟".to_string(),
            color: "1;38;5;208".to_string(),
        },
    );
    styles.insert(
        "python".to_string(),
        LanguageStyleConfig {
            icon: "🐍".to_string(),
            color: "1;34".to_string(),
        },
    );
    styles.insert(
        "go".to_string(),
        LanguageStyleConfig {
            icon: "🐹".to_string(),
            color: "1;36".to_string(),
        },
    );
    styles.insert(
        "deno".to_string(),
        LanguageStyleConfig {
            icon: "🦕".to_string(),
            color: "1;32".to_string(),
        },
    );
    styles.insert(
        "ruby".to_string(),
        LanguageStyleConfig {
            icon: "💎".to_string(),
            color: "1;31".to_string(),
        },
    );
    styles.insert(
        "php".to_string(),
        LanguageStyleConfig {
            icon: "🐘".to_string(),
            color: "1;35".to_string(),
        },
    );
    styles.insert(
        "java".to_string(),
        LanguageStyleConfig {
            icon: "☕".to_string(),
            color: "1;31".to_string(),
        },
    );

    styles
}

impl Config {
    pub fn load() -> Self {
        let config_path = Self::get_config_path();
        if let Ok(content) = fs::read_to_string(config_path) {
            toml::from_str(&content).unwrap_or_default()
        } else {
            Self::default()
        }
    }

    pub fn load_cached() -> Arc<Self> {
        static CONFIG_CACHE: OnceLock<Arc<Config>> = OnceLock::new();
        Arc::clone(CONFIG_CACHE.get_or_init(|| Arc::new(Self::load())))
    }

    fn get_config_path() -> PathBuf {
        let home = std::env::var("HOME")
            .or_else(|_| std::env::var("USERPROFILE"))
            .unwrap_or_else(|_| ".".to_string());
        PathBuf::from(home)
            .join(".config")
            .join("paneship")
            .join("config.toml")
    }
}
