use std::{fmt, str::FromStr};

use serde::{
    de::{Unexpected, Visitor},
    Deserialize,
};

use crate::config::Config;

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(C)]
pub struct Color {
    pub r: u8,
    pub g: u8,
    pub b: u8,
}

#[derive(Clone, Copy, Debug)]
pub struct CellAttributes {
    pub fg: Color,
    pub bg: Color,
    pub bold: bool,
    pub inverse: bool,
    pub italic: bool,
    pub underlined: bool,
    pub strikethrough: bool,
}

impl Color {
    pub const fn new(r: u8, g: u8, b: u8) -> Self {
        Self { r, g, b }
    }

    pub const fn from_u32(v: u32) -> Self {
        let r = (v >> 16) as u8;
        let g = (v >> 8) as u8;
        let b = v as u8;
        Self { r, g, b }
    }

    pub const fn to_u32(self) -> u32 {
        0xFF000000 | ((self.r as u32) << 16) | ((self.g as u32) << 8) | (self.b as u32)
    }

    pub fn from_escape(config: &Config, bold: bool, esc: u32) -> Option<Self> {
        let map = if bold {
            &config.colors.bold
        } else {
            &config.colors.dim
        };
        map.lookup_escape(esc)
    }
}

impl FromStr for Color {
    type Err = &'static str;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let Some(s) = s.strip_prefix("#") else {
            return Err("Color should start with a `#`");
        };
        if s.len() != 6 {
            return Err("Color should be 6 hex digits");
        }
        let value = u32::from_str_radix(s, 16).map_err(|_| "Invalid color value")?;
        Ok(Color::from_u32(value))
    }
}

impl<'de> Deserialize<'de> for Color {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct V;

        impl Visitor<'_> for V {
            type Value = Color;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                write!(formatter, "a hex color in format `#XXYYZZ`")
            }

            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                if let Ok(color) = Color::from_str(v) {
                    Ok(color)
                } else {
                    Err(E::invalid_value(Unexpected::Str(v), &"#XXYYZZ"))
                }
            }

            fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                self.visit_str(&v)
            }
        }

        deserializer.deserialize_str(V)
    }
}
