use clap::ValueEnum;
use serde::{Deserialize, Serialize};
use std::fs;
use std::io::Write;
use std::path::Path;

/// Download mode options
#[derive(Debug, Default, Clone, ValueEnum, Deserialize, Serialize, PartialEq, Copy)]
#[serde(rename_all = "PascalCase")]
pub enum DownloadMode {
    Never,
    Always,
    #[default]
    Ask,
}

impl std::fmt::Display for DownloadMode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            DownloadMode::Never => write!(f, "never"),
            DownloadMode::Always => write!(f, "always"),
            DownloadMode::Ask => write!(f, "ask"),
        }
    }
}

/// Version strategy options
#[derive(Default, Debug, Clone, ValueEnum, Deserialize, Serialize, PartialEq, Copy)]
#[serde(rename_all = "PascalCase")]
pub enum VersionStrategy {
    Exact,
    #[default]
    Latest,
}

impl std::fmt::Display for VersionStrategy {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            VersionStrategy::Exact => write!(f, "exact"),
            VersionStrategy::Latest => write!(f, "latest"),
        }
    }
}

/// The configuration section `[korrect-shim]`
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq)]
pub struct KorrectShimConfig {
    #[serde(default)]
    pub download_mode: DownloadMode,
    #[serde(default)]
    pub version_strategy: VersionStrategy,
    #[serde(default)]
    pub manage_helm: bool,
}

impl Default for KorrectShimConfig {
    fn default() -> Self {
        Self {
            download_mode: Default::default(),
            version_strategy: Default::default(),
            manage_helm: true,
        }
    }
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Copy)]
pub struct Config {
    #[serde(default)]
    pub version: u8,
    #[serde(rename = "korrect-shim", default)]
    pub korrect_shim: KorrectShimConfig,
}

impl Default for Config {
    fn default() -> Self {
        Config {
            version: 2,
            korrect_shim: KorrectShimConfig::default(),
        }
    }
}

/// Reads and parses the configuration file at the given path.
pub fn load_config<P: AsRef<Path>>(path: P) -> anyhow::Result<Config> {
    if !path.as_ref().exists() {
        log::info!(
            "Config file not found at {:?}, using defaults.",
            path.as_ref()
        );
        return Ok(Config::default());
    }

    let content = std::fs::read_to_string(path)?;
    let config: Config = toml::from_str(&content)?;

    // Check version
    if config.version > 2 || config.version < 1 {
        // You could support multiple versions with a match if needed
        return Err(anyhow::anyhow!(
            "Unsupported config version: {}. Please setup korrect again.",
            config.version
        ));
    }
    Ok(config)
}

pub fn write_config<P: AsRef<Path>>(path: P, overrides: Config) -> anyhow::Result<()> {
    let toml_string = toml::to_string_pretty(&overrides)?;
    let mut file = fs::File::create(path)?;
    file.write_all(toml_string.as_bytes())?;
    Ok(())
}
