#![allow(unused, dead_code)]
use gpui::{Hsla, Length};
use std::{
    cell::LazyCell,
    sync::{Arc, LazyLock, OnceLock},
};
use theme::{Theme, ThemeColors, ThemeRegistry};
use ui::{
    IntoElement, RenderOnce, component_prelude::Documented, prelude::*, utils::inner_corner_radius,
};

#[derive(Clone, PartialEq)]
pub enum ThemePreviewStyle {
    Bordered,
    Borderless,
    SideBySide(Arc<Theme>),
}

/// Shows a preview of a theme as an abstract illustration
/// of a thumbnail-sized editor.
#[derive(IntoElement, RegisterComponent, Documented)]
pub struct ThemePreviewTile {
    theme: Arc<Theme>,
    seed: f32,
    style: ThemePreviewStyle,
}

static CHILD_RADIUS: LazyLock<Pixels> = LazyLock::new(|| {
    inner_corner_radius(
        ThemePreviewTile::ROOT_RADIUS,
        ThemePreviewTile::ROOT_BORDER,
        ThemePreviewTile::ROOT_PADDING,
        ThemePreviewTile::CHILD_BORDER,
    )
});

impl ThemePreviewTile {
    pub const SKELETON_HEIGHT_DEFAULT: Pixels = px(2.);
    pub const SIDEBAR_SKELETON_ITEM_COUNT: usize = 8;
    pub const SIDEBAR_WIDTH_DEFAULT: DefiniteLength = relative(0.25);
    pub const ROOT_RADIUS: Pixels = px(8.0);
    pub const ROOT_BORDER: Pixels = px(2.0);
    pub const ROOT_PADDING: Pixels = px(2.0);
    pub const CHILD_BORDER: Pixels = px(1.0);

    pub fn new(theme: Arc<Theme>, seed: f32) -> Self {
        Self {
            theme,
            seed,
            style: ThemePreviewStyle::Bordered,
        }
    }

    pub fn style(mut self, style: ThemePreviewStyle) -> Self {
        self.style = style;
        self
    }

    pub fn item_skeleton(w: Length, h: Length, bg: Hsla) -> impl IntoElement {
        div().w(w).h(h).rounded_full().bg(bg)
    }

    pub fn render_sidebar_skeleton_items(
        seed: f32,
        colors: &ThemeColors,
        skeleton_height: impl Into<Length> + Clone,
    ) -> [impl IntoElement; Self::SIDEBAR_SKELETON_ITEM_COUNT] {
        let skeleton_height = skeleton_height.into();
        std::array::from_fn(|index| {
            let width = {
                let value = (seed * 1000.0 + index as f32 * 10.0).sin() * 0.5 + 0.5;
                0.5 + value * 0.45
            };
            Self::item_skeleton(
                relative(width).into(),
                skeleton_height,
                colors.text.alpha(0.45),
            )
        })
    }

    pub fn render_pseudo_code_skeleton(
        seed: f32,
        theme: Arc<Theme>,
        skeleton_height: impl Into<Length>,
    ) -> impl IntoElement {
        let colors = theme.colors();
        let syntax = theme.syntax();

        let keyword_color = syntax.style_for_name("keyword").and_then(|s| s.color);
        let function_color = syntax.style_for_name("function").and_then(|s| s.color);
        let string_color = syntax.style_for_name("string").and_then(|s| s.color);
        let comment_color = syntax.style_for_name("comment").and_then(|s| s.color);
        let variable_color = syntax.style_for_name("variable").and_then(|s| s.color);
        let type_color = syntax.style_for_name("type").and_then(|s| s.color);
        let punctuation_color = syntax.style_for_name("punctuation").and_then(|s| s.color);

        let syntax_colors = [
            keyword_color,
            function_color,
            string_color,
            variable_color,
            type_color,
            punctuation_color,
            comment_color,
        ];

        let skeleton_height = skeleton_height.into();

        let line_width = |line_idx: usize, block_idx: usize| -> f32 {
            let val =
                (seed * 100.0 + line_idx as f32 * 20.0 + block_idx as f32 * 5.0).sin() * 0.5 + 0.5;
            0.05 + val * 0.2
        };

        let indentation = |line_idx: usize| -> f32 {
            let step = line_idx % 6;
            if step < 3 {
                step as f32 * 0.1
            } else {
                (5 - step) as f32 * 0.1
            }
        };

        let pick_color = |line_idx: usize, block_idx: usize| -> Hsla {
            let idx = ((seed * 10.0 + line_idx as f32 * 7.0 + block_idx as f32 * 3.0).sin() * 3.5)
                .abs() as usize
                % syntax_colors.len();
            syntax_colors[idx].unwrap_or(colors.text)
        };

        let line_count = 10;

        let lines = (0..line_count)
            .map(|line_idx| {
                let block_count = (((seed * 30.0 + line_idx as f32 * 12.0).sin() * 0.5 + 0.5) * 3.0)
                    .round() as usize
                    + 2;

                let indent = indentation(line_idx);

                let blocks = (0..block_count)
                    .map(|block_idx| {
                        let width = line_width(line_idx, block_idx);
                        let color = pick_color(line_idx, block_idx);
                        Self::item_skeleton(relative(width).into(), skeleton_height, color)
                    })
                    .collect::<Vec<_>>();

                h_flex().gap_0p5().ml(relative(indent)).children(blocks)
            })
            .collect::<Vec<_>>();

        v_flex().size_full().p_1().gap_1p5().children(lines)
    }

    pub fn render_sidebar(
        seed: f32,
        colors: &ThemeColors,
        width: impl Into<Length> + Clone,
        skeleton_height: impl Into<Length>,
    ) -> impl IntoElement {
        v_flex()
            .h_full()
            .w(width)
            .p_2()
            .gap_1()
            .bg(colors.panel_background)
            .children(Self::render_sidebar_skeleton_items(
                seed,
                colors,
                skeleton_height.into(),
            ))
    }

    pub fn render_pane(
        seed: f32,
        theme: Arc<Theme>,
        skeleton_height: impl Into<Length>,
    ) -> impl IntoElement {
        div()
            .p_2()
            .size_full()
            .overflow_hidden()
            .bg(theme.colors().editor_background)
            .child(Self::render_pseudo_code_skeleton(
                seed,
                theme,
                skeleton_height.into(),
            ))
    }

    pub fn render_editor(
        seed: f32,
        theme: Arc<Theme>,
        sidebar_width: impl Into<Length> + Clone,
        skeleton_height: impl Into<Length> + Clone,
    ) -> impl IntoElement {
        div()
            .flex()
            .size_full()
            .bg(theme.colors().background.alpha(1.00))
            .child(Self::render_sidebar(
                seed,
                theme.colors(),
                sidebar_width,
                skeleton_height.clone(),
            ))
            .child(Self::render_pane(seed, theme, skeleton_height))
    }

    fn render_borderless(seed: f32, theme: Arc<Theme>) -> impl IntoElement {
        Self::render_editor(
            seed,
            theme,
            Self::SIDEBAR_WIDTH_DEFAULT,
            Self::SKELETON_HEIGHT_DEFAULT,
        )
    }

    fn render_border(seed: f32, theme: Arc<Theme>) -> impl IntoElement {
        div()
            .size_full()
            .p(Self::ROOT_PADDING)
            .rounded(Self::ROOT_RADIUS)
            .child(
                div()
                    .size_full()
                    .rounded(*CHILD_RADIUS)
                    .border(Self::CHILD_BORDER)
                    .border_color(theme.colors().border)
                    .child(Self::render_editor(
                        seed,
                        theme.clone(),
                        Self::SIDEBAR_WIDTH_DEFAULT,
                        Self::SKELETON_HEIGHT_DEFAULT,
                    )),
            )
    }

    fn render_side_by_side(
        seed: f32,
        theme: Arc<Theme>,
        other_theme: Arc<Theme>,
        border_color: Hsla,
    ) -> impl IntoElement {
        let sidebar_width = relative(0.20);

        div()
            .size_full()
            .p(Self::ROOT_PADDING)
            .rounded(Self::ROOT_RADIUS)
            .child(
                h_flex()
                    .size_full()
                    .relative()
                    .rounded(*CHILD_RADIUS)
                    .border(Self::CHILD_BORDER)
                    .border_color(border_color)
                    .overflow_hidden()
                    .child(div().size_full().child(Self::render_editor(
                        seed,
                        theme,
                        sidebar_width,
                        Self::SKELETON_HEIGHT_DEFAULT,
                    )))
                    .child(
                        div()
                            .size_full()
                            .absolute()
                            .left_1_2()
                            .bg(other_theme.colors().editor_background)
                            .child(Self::render_editor(
                                seed,
                                other_theme,
                                sidebar_width,
                                Self::SKELETON_HEIGHT_DEFAULT,
                            )),
                    ),
            )
            .into_any_element()
    }
}

impl RenderOnce for ThemePreviewTile {
    fn render(self, _window: &mut ui::Window, _cx: &mut ui::App) -> impl IntoElement {
        match self.style {
            ThemePreviewStyle::Bordered => {
                Self::render_border(self.seed, self.theme).into_any_element()
            }
            ThemePreviewStyle::Borderless => {
                Self::render_borderless(self.seed, self.theme).into_any_element()
            }
            ThemePreviewStyle::SideBySide(other_theme) => Self::render_side_by_side(
                self.seed,
                self.theme,
                other_theme,
                _cx.theme().colors().border,
            )
            .into_any_element(),
        }
    }
}

impl Component for ThemePreviewTile {
    fn scope() -> ComponentScope {
        ComponentScope::Onboarding
    }

    fn name() -> &'static str {
        "Theme Preview Tile"
    }

    fn sort_name() -> &'static str {
        "Theme Preview Tile"
    }

    fn description() -> Option<&'static str> {
        Some(Self::DOCS)
    }

    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
        let theme_registry = ThemeRegistry::global(cx);

        let one_dark = theme_registry.get("One Dark");
        let one_light = theme_registry.get("One Light");
        let gruvbox_dark = theme_registry.get("Gruvbox Dark");
        let gruvbox_light = theme_registry.get("Gruvbox Light");

        let themes_to_preview = vec![
            one_dark.clone().ok(),
            one_light.ok(),
            gruvbox_dark.ok(),
            gruvbox_light.ok(),
        ]
        .into_iter()
        .flatten()
        .collect::<Vec<_>>();

        Some(
            v_flex()
                .gap_6()
                .p_4()
                .children({
                    if let Some(one_dark) = one_dark.ok() {
                        vec![example_group(vec![single_example(
                            "Default",
                            div()
                                .w(px(240.))
                                .h(px(180.))
                                .child(ThemePreviewTile::new(one_dark, 0.42))
                                .into_any_element(),
                        )])]
                    } else {
                        vec![]
                    }
                })
                .child(
                    example_group(vec![single_example(
                        "Default Themes",
                        h_flex()
                            .gap_4()
                            .children(
                                themes_to_preview
                                    .into_iter()
                                    .map(|theme| {
                                        div()
                                            .w(px(200.))
                                            .h(px(140.))
                                            .child(ThemePreviewTile::new(theme, 0.42))
                                    })
                                    .collect::<Vec<_>>(),
                            )
                            .into_any_element(),
                    )])
                    .grow(),
                )
                .into_any_element(),
        )
    }
}
