use colors_transform::{Color, Rgb};
use ratatui::style::{self, Style};
use serde::{Deserialize, Serialize};

use super::style::AppStyle;

/// A style override where every field is optional, so it can be patched onto a base style
/// (used for tab-theme prefix/postfix overrides).
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AppStyleOptionsRaw {
    pub fg: Option<String>,
    pub bg: Option<String>,
    pub bold: Option<bool>,
    pub underline: Option<bool>,
    pub invert: Option<bool>,
}

impl AppStyleOptionsRaw {
    /// Converts to a ratatui [`Style`], leaving unset fields as no-ops.
    pub fn as_style(&self) -> Style {
        let mut add_modifier = style::Modifier::empty();
        let mut sub_modifier = style::Modifier::empty();
        if let Some(bold) = self.bold {
            if bold {
                add_modifier.insert(style::Modifier::BOLD);
            } else {
                sub_modifier.insert(style::Modifier::BOLD);
            }
        }
        if let Some(underline) = self.underline {
            if underline {
                add_modifier.insert(style::Modifier::UNDERLINED);
            } else {
                sub_modifier.insert(style::Modifier::UNDERLINED);
            }
        }
        if let Some(invert) = self.invert {
            if invert {
                add_modifier.insert(style::Modifier::REVERSED);
            } else {
                sub_modifier.insert(style::Modifier::REVERSED);
            }
        }
        Style {
            fg: self.fg.clone().map(|s| str_to_color(s.as_ref())),
            bg: self.bg.clone().map(|s| str_to_color(s.as_ref())),
            add_modifier,
            sub_modifier,
            //underline_color: None,  # only when ratatui with crossterm
        }
    }
}

/// TOML-deserializable form of [`AppStyle`](super::style::AppStyle).
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AppStyleRaw {
    #[serde(default)]
    pub fg: String,
    #[serde(default)]
    pub bg: String,
    #[serde(default)]
    pub prefix: String,
    #[serde(default)]
    pub bold: bool,
    #[serde(default)]
    pub underline: bool,
    #[serde(default)]
    pub invert: bool,
}

impl AppStyleRaw {
    /// Converts to a resolved [`AppStyle`].
    pub fn to_style_theme(&self) -> AppStyle {
        let bg = Self::str_to_color(self.bg.as_str());
        let fg = Self::str_to_color(self.fg.as_str());

        let mut modifier = style::Modifier::empty();
        if self.bold {
            modifier.insert(style::Modifier::BOLD);
        }
        if self.underline {
            modifier.insert(style::Modifier::UNDERLINED);
        }
        if self.invert {
            modifier.insert(style::Modifier::REVERSED);
        }

        AppStyle::default()
            .set_fg(fg)
            .set_bg(bg)
            .set_prefix(self.prefix.clone())
            .insert(modifier)
    }

    /// Parses a color name, hex code (`#rrggbb`), or CSS-style RGB string into a ratatui color.
    pub fn str_to_color(s: &str) -> style::Color {
        str_to_color(s)
    }
}

impl std::default::Default for AppStyleRaw {
    fn default() -> Self {
        Self {
            bg: "".to_string(),
            fg: "".to_string(),
            prefix: "".to_string(),
            bold: false,
            underline: false,
            invert: false,
        }
    }
}

fn str_to_color(s: &str) -> style::Color {
    match s {
        "black" => style::Color::Black,
        "red" => style::Color::Red,
        "green" => style::Color::Green,
        "yellow" => style::Color::Yellow,
        "blue" => style::Color::Blue,
        "magenta" => style::Color::Magenta,
        "cyan" => style::Color::Cyan,
        "gray" => style::Color::Gray,
        "dark_gray" => style::Color::DarkGray,
        "light_red" => style::Color::LightRed,
        "light_green" => style::Color::LightGreen,
        "light_yellow" => style::Color::LightYellow,
        "light_blue" => style::Color::LightBlue,
        "light_magenta" => style::Color::LightMagenta,
        "light_cyan" => style::Color::LightCyan,
        "white" => style::Color::White,
        "reset" | "" => style::Color::Reset,
        s if s.starts_with('#') => {
            let rgb = match Rgb::from_hex_str(s) {
                Ok(s) => s,
                _ => return style::Color::Reset,
            };
            let r = rgb.get_red() as u8;
            let g = rgb.get_green() as u8;
            let b = rgb.get_blue() as u8;
            style::Color::Rgb(r, g, b)
        }
        s => match s.parse::<Rgb>() {
            Ok(rgb) => {
                let r = rgb.get_red() as u8;
                let g = rgb.get_green() as u8;
                let b = rgb.get_blue() as u8;
                style::Color::Rgb(r, g, b)
            }
            Err(_) => style::Color::Reset,
        },
    }
}
