use super::NewsflashTheme;
use crate::App;
use crate::content_page::ContentPage;
use gdk4::RGBA;

pub struct StyleManager;

impl StyleManager {
    const SHARED_CSS: &'static str = r#"
@define-color card_fg_color @window_fg_color;
@define-color headerbar_border_color @window_fg_color;
@define-color popover_fg_color @window_fg_color;
@define-color dialog_fg_color @window_fg_color;
@define-color dark_fill_bg_color @headerbar_bg_color;
@define-color view_fg_color @window_fg_color;
    "#;
    const LIGHT_CSS_SUFFIX: &'static str = "@define-color card_bg_color alpha(white, .8);\n";
    const DARK_CSS_SUFFIX: &'static str = "@define-color card_bg_color alpha(white, .08);\n";

    pub fn apply_theme(theme: &NewsflashTheme) {
        let css = Self::generate_css(theme);
        let recoloring = App::default().recoloring();

        if let Some(recoloring) = recoloring.as_ref() {
            let css = css.as_deref().unwrap_or("");
            recoloring.load_from_string(css);
        }

        ContentPage::instance().load_branding();
    }

    pub fn generate_preview_tile_css() {
        let mut css = String::new();

        for theme in NewsflashTheme::iterator() {
            let name = theme.to_string().to_lowercase();
            let info = theme.info();
            let foreground = info.foreground;
            let background = info.background;

            let class = format!(
                "
                .article-theme-{name},
                .article-theme-{name} link {{
                    color: {foreground};
                    background: {background};
                }}
            "
            );

            css.push_str(&class);
        }

        if let Some(theme_tiles) = App::default().theme_tiles().as_ref() {
            theme_tiles.load_from_string(&css);
        }
    }

    fn generate_css(theme: &NewsflashTheme) -> Option<String> {
        let info = theme.info();

        // Don't restyle Adwaita as we already have it
        if info.name.starts_with("adwaita") {
            return None;
        }

        let alt = if info.is_dark { RGBA::WHITE } else { RGBA::BLACK };

        let mut css = String::from(Self::SHARED_CSS);
        css.push_str(&format!("/* {} */\n", info.name));

        css = Self::define_color(css, "window_bg_color", &info.background);
        css = Self::define_color(css, "window_fg_color", &info.foreground);

        if info.is_dark {
            css = Self::define_color_mixed(css, "headerbar_bg_color", &info.background, &RGBA::WHITE, 0.07);
        } else {
            css = Self::define_color_mixed(css, "headerbar_bg_color", &info.background, &RGBA::WHITE, 0.25);
        }

        css = Self::define_color(css, "headerbar_fg_color", &info.foreground);

        css = Self::define_color_mixed(
            css,
            "popover_bg_color",
            &info.background,
            &RGBA::WHITE,
            if info.is_dark { 0.07 } else { 0.25 },
        );

        if info.is_dark {
            css = Self::define_color_mixed(css, "dialog_bg_color", &info.background, &RGBA::WHITE, 0.07);
        } else {
            css = Self::define_color(css, "dialog_bg_color", &info.background);
        }

        if info.is_dark {
            css = Self::define_color_mixed(css, "view_bg_color", &info.background, &RGBA::BLACK, 0.1);
        } else {
            css = Self::define_color_mixed(css, "view_bg_color", &info.background, &RGBA::WHITE, 0.3);
        }

        css = Self::define_color(css, "view_fg_color", &info.foreground);

        if let Some(color) = info.accent {
            css = Self::define_color(css, "accent_color", &color);
        }

        css = Self::define_color_mixed(css, "sidebar_bg_color", &info.background, &alt, 0.05);
        css = Self::define_color_mixed(css, "sidebar_backdrop_color", &info.background, &alt, 0.1);
        css = Self::define_color_mixed(css, "sidebar_fg_color", &info.foreground, &alt, 0.05);
        css = Self::define_color_mixed(css, "sidebar_shade_color", &info.background, &RGBA::BLACK, 0.1);
        css = Self::define_color_mixed(css, "sidebar_border_color", &info.background, &RGBA::BLACK, 0.1);

        css = Self::define_color_mixed(css, "secondary_sidebar_bg_color", &info.background, &alt, 0.02);
        css = Self::define_color_mixed(css, "secondary_sidebar_backdrop_color", &info.background, &alt, 0.05);
        css = Self::define_color_mixed(css, "secondary_sidebar_fg_color", &info.foreground, &alt, 0.02);
        css = Self::define_color_mixed(
            css,
            "secondary_sidebar_shade_color",
            &info.background,
            &RGBA::BLACK,
            0.1,
        );
        css = Self::define_color_mixed(
            css,
            "secondary_sidebar_border_color",
            &info.background,
            &RGBA::BLACK,
            0.1,
        );

        if info.is_dark {
            css.push_str(Self::DARK_CSS_SUFFIX);
        } else {
            css.push_str(Self::LIGHT_CSS_SUFFIX);
        }

        Some(css)
    }

    fn define_color(mut css: String, name: &str, color: &RGBA) -> String {
        let mut opaque = *color;
        opaque.set_alpha(1.0);

        let color_str = opaque.to_str();
        css.push_str(&format!("@define-color {name} {color_str};\n"));
        css
    }

    fn define_color_mixed(mut css: String, name: &str, a: &RGBA, b: &RGBA, level: f64) -> String {
        let a_str = a.to_str();
        let b_str = b.to_str();

        let levelstr = format!("{level:.6}");

        css.push_str(&format!("@define-color {name} mix({a_str},{b_str},{levelstr});\n"));
        css
    }
}
