use std::{
    fs,
    path::{Path, PathBuf},
};

use serde::Deserialize;

use crate::attr::Color;

#[derive(Deserialize)]
pub struct FontConfig {
    pub regular: PathBuf,
    pub italic: PathBuf,
    pub bold: PathBuf,
    pub bold_italic: PathBuf,

    pub size: usize,
}

#[derive(Deserialize)]
pub struct ColorsGroupConfig {
    pub red: Color,
    pub green: Color,
    pub blue: Color,
    pub yellow: Color,
    pub magenta: Color,
    pub cyan: Color,
    pub white: Color,
    pub black: Color,
}

#[derive(Deserialize)]
pub struct ColorsConfig {
    pub bold: ColorsGroupConfig,
    pub dim: ColorsGroupConfig,
}

#[derive(Default, Deserialize)]
pub struct Config {
    pub fonts: FontConfig,
    pub colors: ColorsConfig,
}

#[cfg(any(rust_analyzer, target_os = "yggdrasil"))]
impl Default for FontConfig {
    fn default() -> Self {
        Self {
            regular: PathBuf::from("/etc/fonts/fixed-regular.ttf"),
            italic: PathBuf::from("/etc/fonts/fixed-italic.ttf"),
            bold: PathBuf::from("/etc/fonts/fixed-bold.ttf"),
            bold_italic: PathBuf::from("/etc/fonts/fixed-bold-italic.ttf"),
            size: 16,
        }
    }
}

#[cfg(any(rust_analyzer, unix))]
impl Default for FontConfig {
    fn default() -> Self {
        // TODO query fontconfig for monospace
        Self {
            regular: PathBuf::from("/usr/share/fonts/TTF/DejaVuSansMono.ttf"),
            italic: PathBuf::from("/usr/share/fonts/TTF/DejaVuSansMono-Oblique.ttf"),
            bold: PathBuf::from("/usr/share/fonts/TTF/DejaVuSansMono-Bold.ttf"),
            bold_italic: PathBuf::from("/usr/share/fonts/TTF/DejaVuSansMono-BoldOblique.ttf"),
            size: 16,
        }
    }
}

impl Default for ColorsConfig {
    fn default() -> Self {
        Self {
            dim: ColorsGroupConfig {
                black: Color::from_u32(0x000000),
                white: Color::from_u32(0xCCCCCC),
                red: Color::from_u32(0xCC0000),
                green: Color::from_u32(0x00CC00),
                blue: Color::from_u32(0x0000CC),
                yellow: Color::from_u32(0xCCCC00),
                magenta: Color::from_u32(0xCC00CC),
                cyan: Color::from_u32(0x00CCCC),
            },
            bold: ColorsGroupConfig {
                black: Color::from_u32(0x666666),
                white: Color::from_u32(0xFFFFFF),
                red: Color::from_u32(0xFF0000),
                green: Color::from_u32(0x00FF00),
                blue: Color::from_u32(0x0000FF),
                yellow: Color::from_u32(0xFFFF00),
                magenta: Color::from_u32(0xFF00FF),
                cyan: Color::from_u32(0x00FFFF),
            },
        }
    }
}

impl ColorsGroupConfig {
    pub fn lookup_escape(&self, esc: u32) -> Option<Color> {
        match esc {
            0 => Some(self.black),
            1 => Some(self.red),
            2 => Some(self.green),
            3 => Some(self.yellow),
            4 => Some(self.blue),
            5 => Some(self.magenta),
            6 => Some(self.cyan),
            7 => Some(self.white),
            _ => None,
        }
    }
}

impl Config {
    pub fn load_or_default<P: AsRef<Path>>(path: P) -> Self {
        Self::load(path).unwrap_or_default()
    }

    fn load<P: AsRef<Path>>(path: P) -> Option<Self> {
        let path = path.as_ref();
        let data = fs::read_to_string(path)
            .inspect_err(|error| log::warn!("{path:?}: {error}"))
            .ok()?;
        let this = toml::from_str(&data)
            .inspect_err(|error| log::warn!("{path:?}: {error}"))
            .ok()?;
        Some(this)
    }
}
