// TODO rm once theme finished
#![allow(unused_variables, dead_code)]

use inflorescence_iced_widget::{nav_scrollable, report};
use libflorescence::prelude::*;

use iced::advanced::widget::text;
use iced::gradient::{ColorStop, Linear};
use iced::theme::{self, palette};
use iced::widget::{button, container, scrollable, text_editor, text_input};
use iced::{
    border, color, window, Background, Border, Color, Gradient, Radians,
};

const BLUE: Color = color!(0x010032);
const BLUE_DARKER: Color = color!(0x010022);
const BLUE_LIGHT: Color = color!(0x020056);
const BLUE_DARK: Color = color!(0x000002);

const TEAL: Color = color!(0x3B8588);
const TEAL_LIGHTEST: Color = color!(0x97C9CB);
const TEAL_LIGHT: Color = color!(0x5F9FA2);
const TEAL_DARK: Color = color!(0x247074);
const TEAL_DARKEST: Color = color!(0x115B5E);

const MAGENTA: Color = color!(0xE933F6);
const MAGENTA_LIGHT: Color = color!(0xEE5DF8);
const MAGENTA_LIGHTER: Color = color!(0xF38EFA);
const MAGENTA_LIGHTEST: Color = color!(0xF7BCFC);
const MAGENTA_DARK: Color = color!(0xE208F1);
const MAGENTA_DARKER: Color = color!(0x9C06A6);
const MAGENTA_DARKEST: Color = color!(0x68436B);

const TURQOISE: Color = color!(0x75fad1);
const TURQOISE_LIGHT: Color = color!(0xA5FDE2);
const TURQOISE_DARK: Color = color!(0x4CF5C2);
const TURQOISE_DARKER: Color = color!(0x0CA592);

const TEXT_COLOR: Color = TEAL_LIGHTEST;
const TEXT_COLOR_FADED: Color = Color {
    a: 0.7,
    ..TEXT_COLOR
};

const DELETED_BG_COLOR: Color = Color::from_rgba8(190, 37, 40, 0.25);
const ADDED_BG_COLOR: Color = Color::from_rgba8(47, 148, 11, 0.25);

const REPORT_INFO_COLOR: Color = color!(0x08992d);
const REPORT_WARNING_COLOR: Color = color!(0x999208);
const REPORT_ERROR_COLOR: Color = color!(0x9d140e);

const NAV_SECTION_BORDER_WIDTH: f32 = 2.0;

const APP_BG: Background = Background::Gradient(Gradient::Linear(Linear {
    angle: Radians(0.),
    stops: [
        Some(ColorStop {
            offset: 0.,
            color: BLUE,
        }),
        Some(ColorStop {
            offset: 1.,
            color: BLUE_DARKER,
        }),
        None,
        None,
        None,
        None,
        None,
        None,
    ],
}));

const ACTIONS_BG: Background = Background::Color(BLUE_DARKER);

// TODO not sure if I'll use this?
const PALETTE: theme::Palette = theme::Palette {
    background: BLUE,
    text: scale_rgb(TEAL, 1.),
    primary: TURQOISE,
    success: TURQOISE,
    warning: MAGENTA,
    danger: MAGENTA,
};

#[derive(Debug, Default)]
pub struct Theme;

#[derive(Debug, Clone, Copy)]
pub enum Button {
    Normal,
    Selected,
    /// A button is selected, but the selection focus is in its children
    /// sub-selection. The button is less highlighted than `Selected` to
    /// indicate this
    ChildSelected,
}

#[derive(Debug, Clone, Copy)]
pub enum Container {
    Normal,
    FadedBorder,
    AppBg,
    ActionsBg,
    ActionsHighlightBg,
    DiffAddition,
    DiffDeletion,
    Report,
    ReportLevel { level: report::Level, is_read: bool },
    NavNonSelectedSection,
    NavSelectedSection,
}

#[derive(Debug, Clone, Copy)]
pub enum Scrollable {
    Normal,
    Selected,
}

#[derive(Debug, Clone, Copy)]
pub enum Text {
    Normal,
    SlightlyFaded,
    HighlightOnLightBg,
}

#[derive(Debug, Clone, Copy)]
pub enum TextEditor {
    Normal,
}

#[derive(Debug, Clone, Copy)]
pub enum TextInput {
    Normal,
}

pub fn theme<S>(_state: &S, _window_id: window::Id) -> Theme {
    Theme
}

impl theme::Base for Theme {
    fn base(&self) -> theme::Style {
        theme::Style {
            background_color: BLUE,
            text_color: TEXT_COLOR,
        }
    }

    fn palette(&self) -> Option<theme::Palette> {
        None
    }

    fn default(_preference: theme::Mode) -> Self {
        Theme
    }

    fn mode(&self) -> theme::Mode {
        theme::Mode::None
    }

    fn name(&self) -> &str {
        "inflorescence"
    }
}

impl button::Catalog for Theme {
    type Class<'a> = Button;

    fn default<'a>() -> Self::Class<'a> {
        Button::Normal
    }

    fn style(
        &self,
        class: &Self::Class<'_>,
        status: button::Status,
    ) -> button::Style {
        let base = button::Style {
            background: Some(Background::Color(TURQOISE)),
            text_color: TEAL_DARKEST,
            ..button::Style::default()
        };

        let base = match status {
            button::Status::Active | button::Status::Pressed => base,
            button::Status::Hovered => button::Style {
                background: Some(Background::Color(TURQOISE_LIGHT)),
                text_color: BLUE,
                ..base
            },
            button::Status::Disabled => button::Style {
                background: base
                    .background
                    .map(|background| background.scale_alpha(0.7)),
                text_color: base.text_color.scale_alpha(0.7),
                ..base
            },
        };

        let border_radius = border::radius(2.);
        const BORDER_WIDTH: f32 = 1.;
        const CHILD_SELECTED_BLEND_RATIO: f32 = 0.5;
        match class {
            Button::Normal => button::Style {
                border: Border {
                    color: Color::TRANSPARENT,
                    width: BORDER_WIDTH,
                    radius: border_radius,
                },
                ..base
            },
            Button::Selected => button::Style {
                background: Some(Background::Color(
                    if let button::Status::Hovered = status {
                        MAGENTA_LIGHTEST
                    } else {
                        MAGENTA_LIGHTER
                    },
                )),
                text_color: BLUE_DARK,
                border: Border {
                    color: MAGENTA,
                    width: BORDER_WIDTH,
                    radius: border_radius,
                },
                ..base
            },
            Button::ChildSelected => button::Style {
                background: Some(Background::Color(
                    if let button::Status::Hovered = status {
                        blend(
                            MAGENTA_LIGHTEST,
                            TURQOISE_LIGHT,
                            CHILD_SELECTED_BLEND_RATIO,
                        )
                    } else {
                        blend(
                            MAGENTA_LIGHTER,
                            TURQOISE,
                            CHILD_SELECTED_BLEND_RATIO,
                        )
                    },
                )),
                text_color: BLUE_DARK,
                border: Border {
                    color: MAGENTA,
                    width: BORDER_WIDTH,
                    radius: border_radius,
                },
                ..base
            },
        }
    }
}

impl container::Catalog for Theme {
    type Class<'a> = Container;

    fn default<'a>() -> Self::Class<'a> {
        Container::Normal
    }

    fn style(&self, class: &Self::Class<'_>) -> container::Style {
        match class {
            Container::Normal => container::Style::default(),
            Container::FadedBorder => container::Style {
                border: Border {
                    color: TEXT_COLOR_FADED,
                    width: 2.0,
                    ..default()
                },
                ..default()
            },
            Container::AppBg => container::Style {
                background: Some(APP_BG),
                ..default()
            },
            Container::ActionsBg => container::Style {
                background: Some(ACTIONS_BG),
                ..default()
            },
            Container::ActionsHighlightBg => container::Style {
                background: Some(Background::Color(MAGENTA_DARKEST)),
                ..default()
            },
            Container::DiffAddition => container::Style {
                background: Some(Background::Color(ADDED_BG_COLOR)),
                ..default()
            },
            Container::DiffDeletion => container::Style {
                background: Some(Background::Color(DELETED_BG_COLOR)),
                ..default()
            },
            Container::Report => container::Style {
                background: Some(ACTIONS_BG),
                border: Border {
                    color: TURQOISE_DARKER,
                    width: 1.0,
                    ..default()
                },
                ..default()
            },
            Container::ReportLevel { level, is_read } => {
                let color = match level {
                    report::Level::Info => REPORT_INFO_COLOR,
                    report::Level::Warning => REPORT_WARNING_COLOR,
                    report::Level::Error => REPORT_ERROR_COLOR,
                };
                let color = if *is_read {
                    Color { a: 0.7, ..color }
                } else {
                    color
                };
                container::Style {
                    background: Some(Background::Color(color)),
                    ..default()
                }
            }
            Container::NavNonSelectedSection => container::Style {
                border: Border {
                    color: Color::TRANSPARENT,
                    width: NAV_SECTION_BORDER_WIDTH,
                    ..default()
                },
                ..default()
            },
            Container::NavSelectedSection => container::Style {
                border: Border {
                    color: MAGENTA_LIGHT,
                    width: NAV_SECTION_BORDER_WIDTH,
                    ..default()
                },
                ..default()
            },
        }
    }
}

impl scrollable::Catalog for Theme {
    type Class<'a> = Scrollable;

    fn default<'a>() -> Self::Class<'a> {
        Scrollable::Normal
    }

    fn style(
        &self,
        class: &Self::Class<'_>,
        status: scrollable::Status,
    ) -> scrollable::Style {
        let background = palette::Background::new(
            match class {
                Scrollable::Normal => PALETTE.background,
                Scrollable::Selected => MAGENTA_DARKEST,
            },
            PALETTE.text,
        );
        // TODO not same as default
        let scrollbar = scrollable::Rail {
            background: Some(background.weak.color.into()),
            border: border::rounded(2),
            scroller: scrollable::Scroller {
                background: Background::Color(match class {
                    Scrollable::Normal => background.strong.color,
                    Scrollable::Selected => MAGENTA_LIGHTER,
                }),
                border: border::rounded(2),
            },
        };
        scrollable::Style {
            container: container::Style::default(),
            vertical_rail: scrollbar,
            horizontal_rail: scrollbar,
            gap: None,
            auto_scroll: scrollable::AutoScroll {
                background: Background::from(Color::default()),
                border: default(),
                shadow: default(),
                icon: default(),
            },
        }
    }
}

impl nav_scrollable::Catalog for Theme {
    type Class<'a> = Scrollable;

    fn default<'a>() -> Self::Class<'a> {
        Scrollable::Normal
    }

    fn style(
        &self,
        class: &Self::Class<'_>,
        status: nav_scrollable::Status,
    ) -> nav_scrollable::Style {
        let background = palette::Background::new(
            match class {
                Scrollable::Normal => PALETTE.background,
                Scrollable::Selected => MAGENTA_DARKEST,
            },
            PALETTE.text,
        );
        // TODO not same as default
        let scrollbar = nav_scrollable::Rail {
            background: Some(background.weak.color.into()),
            border: border::rounded(2),
            scroller: nav_scrollable::Scroller {
                background: Background::Color(match class {
                    Scrollable::Normal => background.strong.color,
                    Scrollable::Selected => MAGENTA_LIGHTER,
                }),
                border: border::rounded(2),
            },
        };
        nav_scrollable::Style {
            container: container::Style::default(),
            vertical_rail: scrollbar,
            horizontal_rail: scrollbar,
            gap: None,
        }
    }
}

impl text::Catalog for Theme {
    type Class<'a> = Text;

    fn default<'a>() -> Self::Class<'a> {
        Text::Normal
    }

    fn style(&self, item: &Self::Class<'_>) -> text::Style {
        match item {
            Text::Normal => text::Style { color: None },
            Text::SlightlyFaded => text::Style {
                color: Some(TEXT_COLOR_FADED),
            },
            Text::HighlightOnLightBg => text::Style {
                color: Some(MAGENTA_DARKER),
            },
        }
    }
}

impl text_editor::Catalog for Theme {
    type Class<'a> = TextEditor;

    fn default<'a>() -> Self::Class<'a> {
        TextEditor::Normal
    }

    fn style(
        &self,
        class: &Self::Class<'_>,
        status: text_editor::Status,
    ) -> text_editor::Style {
        // TODO not same as default
        let background =
            palette::Background::new(PALETTE.background, PALETTE.text);
        let primary = palette::Primary::generate(
            PALETTE.primary,
            PALETTE.background,
            PALETTE.text,
        );

        let active = text_editor::Style {
            background: Background::Color(background.base.color),
            border: Border {
                radius: 2.0.into(),
                width: 1.0,
                color: background.strong.color,
            },
            placeholder: background.strongest.color,
            value: background.base.text,
            selection: primary.weak.color,
        };

        match status {
            text_editor::Status::Active => active,
            text_editor::Status::Hovered => text_editor::Style {
                border: Border {
                    color: background.base.text,
                    ..active.border
                },
                ..active
            },
            text_editor::Status::Focused { .. } => text_editor::Style {
                border: Border {
                    color: primary.strong.color,
                    ..active.border
                },
                ..active
            },
            text_editor::Status::Disabled => text_editor::Style {
                background: Background::Color(background.weak.color),
                value: active.placeholder,
                ..active
            },
        }
    }
}

impl text_input::Catalog for Theme {
    type Class<'a> = TextInput;

    fn default<'a>() -> Self::Class<'a> {
        TextInput::Normal
    }

    fn style(
        &self,
        class: &Self::Class<'_>,
        status: text_input::Status,
    ) -> text_input::Style {
        let background =
            palette::Background::new(PALETTE.background, PALETTE.text);
        let primary = palette::Primary::generate(
            PALETTE.primary,
            PALETTE.background,
            PALETTE.text,
        );

        let active = text_input::Style {
            background: Background::Color(background.base.color),
            border: Border {
                radius: 2.0.into(),
                width: 1.0,
                color: background.strongest.color,
            },
            icon: background.weak.text,
            placeholder: background.strongest.color,
            value: background.base.text,
            selection: primary.weak.color,
        };

        match status {
            text_input::Status::Active => active,
            text_input::Status::Hovered => text_input::Style {
                border: Border {
                    color: background.base.text,
                    ..active.border
                },
                ..active
            },
            text_input::Status::Focused { .. } => text_input::Style {
                border: Border {
                    color: primary.strong.color,
                    ..active.border
                },
                ..active
            },
            text_input::Status::Disabled => text_input::Style {
                background: Background::Color(background.weak.color),
                value: active.placeholder,
                ..active
            },
        }
    }
}

const fn scale_rgb(color: Color, scale: f32) -> Color {
    let Color { r, g, b, a } = color;
    Color {
        r: r * scale,
        g: g * scale,
        b: b * scale,
        a,
    }
}

const fn blend(a: Color, b: Color, a_ratio: f32) -> Color {
    debug_assert!(a_ratio >= 0.0);
    debug_assert!(a_ratio <= 1.0);

    let b_ratio = 1.0 - a_ratio;
    Color {
        r: a.r * a_ratio + b.r * b_ratio,
        g: a.g * a_ratio + b.g * b_ratio,
        b: a.b * a_ratio + b.b * b_ratio,
        a: a.a * a_ratio + b.a * b_ratio,
    }
}