use cvkg::components::calendar::Date;
use cvkg::components::{
    AutoComplete, BifrostTabs, Breadcrumb, BreadcrumbItem, Combobox, DatePicker, MimirSpotlight,
    SpinnerVariant, Toggle,
};
use cvkg::core::{Event, Renderer, View};
use cvkg::prelude::AnyView;
use cvkg::prelude::*;
use cvkg_core::update_system_state;

// Shared system-state key for the command palette open flag (must match command_palette.rs).
const SPOTLIGHT_OPEN_HASH: u64 = 0xD00_0001;

// -- Component catalog ------------------------------------------------

struct GalleryEntry {
    name: &'static str,
    category: &'static str,
    render: fn(&GalleryState, &std::sync::Arc<std::sync::Mutex<GalleryState>>) -> AnyView,
}

fn catalog() -> Vec<GalleryEntry> {
    vec![
        // -- Forms --
        GalleryEntry {
            name: "Button",
            category: "Forms",
            render: |_state, _state_arc| {
                AnyView::new(
                    VStack::new(12.0)
                        .child(Button::new("Default Button", || {}))
                        .child(Button::new("Disabled Button", || {}).disabled(true)),
                )
            },
        },
        GalleryEntry {
            name: "Checkbox",
            category: "Forms",
            render: |state, state_arc| {
                let arc1 = state_arc.clone();
                let arc2 = state_arc.clone();
                AnyView::new(
                    VStack::new(8.0)
                        .child(
                            Checkbox::new(state.checkbox_1, move |val| {
                                let mut s = arc1.lock().unwrap_or_else(|e| e.into_inner());
                                s.checkbox_1 = val;
                            })
                            .label("Enable Berserk Mode")
                            .frame(Some(220.0), Some(30.0)),
                        )
                        .child(
                            Checkbox::new(state.checkbox_2, move |val| {
                                let mut s = arc2.lock().unwrap_or_else(|e| e.into_inner());
                                s.checkbox_2 = val;
                            })
                            .label("Auto-charge Rage")
                            .frame(Some(220.0), Some(30.0)),
                        ),
                )
            },
        },
        GalleryEntry {
            name: "Input",
            category: "Forms",
            render: |state, state_arc| {
                let arc = state_arc.clone();
                AnyView::new(
                    VStack::new(12.0)
                        .child(
                            Input::new(state.input_text.as_str()).on_change(move |text| {
                                let mut s = arc.lock().unwrap_or_else(|e| e.into_inner());
                                s.input_text = text;
                            }),
                        )
                        .child(
                            Text::new(format!("Typed: {}", state.input_text))
                                .font_size(14.0)
                                .color([0.7, 0.7, 0.7, 1.0]),
                        ),
                )
            },
        },
        GalleryEntry {
            name: "Toggle",
            category: "Forms",
            render: |state, state_arc| {
                let arc1 = state_arc.clone();
                let arc2 = state_arc.clone();
                AnyView::new(
                    VStack::new(8.0)
                        .child(Toggle::new("Shield Wall", state.toggle_1, move |val| {
                            let mut s = arc1.lock().unwrap_or_else(|e| e.into_inner());
                            s.toggle_1 = val;
                        }))
                        .child(Toggle::new("Odin's Sight", state.toggle_2, move |val| {
                            let mut s = arc2.lock().unwrap_or_else(|e| e.into_inner());
                            s.toggle_2 = val;
                        })),
                )
            },
        },
        GalleryEntry {
            name: "Slider",
            category: "Forms",
            render: |state, state_arc| {
                let arc = state_arc.clone();
                AnyView::new(
                    VStack::new(12.0)
                        .child(
                            Text::new(format!("Volume: {}%", (state.slider_value * 100.0) as i32))
                                .font_size(14.0)
                                .color([0.7, 0.7, 0.7, 1.0]),
                        )
                        .child(Slider::new(state.slider_value, 0.0..=1.0, move |val| {
                            let mut s = arc.lock().unwrap_or_else(|e| e.into_inner());
                            s.slider_value = val;
                        })),
                )
            },
        },
        GalleryEntry {
            name: "Combobox",
            category: "Forms",
            render: |state, state_arc| {
                let arc = state_arc.clone();
                AnyView::new(
                    VStack::new(8.0).child(
                        Combobox::new(vec![
                            "Odin".to_string(),
                            "Tyr".to_string(),
                            "Bor".to_string(),
                        ])
                        .selected(state.combobox_index)
                        .on_change(move |idx| {
                            let mut s = arc.lock().unwrap_or_else(|e| e.into_inner());
                            s.combobox_index = idx;
                        })
                        .frame(Some(220.0), Some(38.0)),
                    ),
                )
            },
        },
        GalleryEntry {
            name: "AutoComplete",
            category: "Forms",
            render: |_state, state_arc| {
                let arc = state_arc.clone();
                let suggestions = vec![
                    "Astrid".to_string(),
                    "Bjorn".to_string(),
                    "Freya".to_string(),
                    "Odin".to_string(),
                    "Thor".to_string(),
                ];
                AnyView::new(
                    VStack::new(8.0).child(
                        AutoComplete::new(
                            "Search warriors...".to_string(),
                            suggestions,
                            move |query| {
                                let mut s = arc.lock().unwrap_or_else(|e| e.into_inner());
                                s.autocomplete_query = query;
                            },
                            move |_selected| {},
                        )
                        .frame(Some(220.0), Some(38.0)),
                    ),
                )
            },
        },
        GalleryEntry {
            name: "DatePicker",
            category: "Forms",
            render: |state, state_arc| {
                let arc = state_arc.clone();
                AnyView::new(
                    VStack::new(8.0)
                        .child(
                            Text::new(format!("Selected: {}", state.selected_date.format()))
                                .font_size(14.0)
                                .color([1.0, 1.0, 1.0, 1.0]),
                        )
                        .child(
                            DatePicker::new(move |d, m, y| {
                                let mut s = arc.lock().unwrap_or_else(|e| e.into_inner());
                                s.selected_date = Date {
                                    year: y as i32,
                                    month: m,
                                    day: d,
                                };
                            })
                            .selected(
                                state.selected_date.day,
                                state.selected_date.month,
                                state.selected_date.year as u32,
                            )
                            .frame(Some(220.0), Some(38.0)),
                        )
                        .child(
                            Text::new("Date picker component")
                                .font_size(11.0)
                                .color([0.6, 0.6, 0.6, 1.0]),
                        ),
                )
            },
        },
        // -- Navigation --
        GalleryEntry {
            name: "Tabs",
            category: "Navigation",
            render: |state, state_arc| {
                let arc_clone = state_arc.clone();
                AnyView::new(BifrostTabs::new(
                    vec![
                        "Shield".to_string(),
                        "Rage".to_string(),
                        "Runes".to_string(),
                    ],
                    state.tab_index,
                    move |idx| {
                        let mut s = arc_clone.lock().unwrap_or_else(|e| e.into_inner());
                        s.tab_index = idx;
                    },
                ))
            },
        },
        GalleryEntry {
            name: "Breadcrumb",
            category: "Navigation",
            render: |_state, _state_arc| {
                AnyView::new(VStack::new(8.0).child(Breadcrumb::new(vec![
                    BreadcrumbItem::new("Home"),
                    BreadcrumbItem::new("Loadout"),
                    BreadcrumbItem::new("Berserker"),
                ])))
            },
        },
        // -- Overlays --
        GalleryEntry {
            name: "Tooltip",
            category: "Overlays",
            render: |_state, _state_arc| {
                AnyView::new(
                    Tooltip::new(
                        AnyView::new(
                            Text::new("Hover target")
                                .font_size(14.0)
                                .color([0.9, 0.9, 0.9, 1.0]),
                        ),
                        "Hidden wisdom: Runes guide the worthy",
                    )
                    .visible(true),
                )
            },
        },
        GalleryEntry {
            name: "Command Palette",
            category: "Overlays",
            render: |state, state_arc| {
                let arc = state_arc.clone();
                AnyView::new(
                    VStack::new(12.0)
                        .child(
                            Text::new(if state.command_palette_open {
                                "Command Palette is OPEN — select an item or click backdrop to close"
                            } else {
                                "Command Palette is CLOSED — click Open to launch it"
                            })
                                .font_size(14.0)
                                .color([1.0, 1.0, 1.0, 0.8]),
                        )
                        .child(
                            Button::new("Open Command Palette", move || {
                                // Mark open in gallery state
                                arc.lock().unwrap_or_else(|e| e.into_inner()).command_palette_open = true;
                                // Drive the MimirSpotlight system state directly.
                                update_system_state(|s| {
                                    let mut s = s.clone();
                                    s.set_component_state(SPOTLIGHT_OPEN_HASH, true);
                                    s
                                });
                            }),
                        )
                        .child(
                            // The palette reads its own open state from system state.
                            // .open() only seeds on first render; after that, state owns it.
                            MimirSpotlight::new()
                                .open()
                                .command("Save File", Some("Ctrl+S"), {
                                    let arc = state_arc.clone();
                                    move || {
                                        arc.lock().unwrap_or_else(|e| e.into_inner()).command_palette_open = false;
                                    }
                                })
                                .command("Open Preferences", Some("Ctrl+P"), {
                                    let arc = state_arc.clone();
                                    move || {
                                        arc.lock().unwrap_or_else(|e| e.into_inner()).command_palette_open = false;
                                    }
                                })
                                .command("Toggle Fullscreen", Some("F11"), {
                                    let arc = state_arc.clone();
                                    move || {
                                        arc.lock().unwrap_or_else(|e| e.into_inner()).command_palette_open = false;
                                    }
                                })
                                .search(state.command_query.as_str()),
                        ),
                )
            },
        },
        GalleryEntry {
            name: "Dialog",
            category: "Overlays",
            render: |_state, _state_arc| {
                AnyView::new(
                    VStack::new(12.0)
                        .child(
                            Text::new("Dialog component demo")
                                .font_size(14.0)
                                .color([0.9, 0.9, 0.9, 1.0]),
                        )
                        .child(Button::new("Open Modal", || {}))
                        .child(
                            Text::new("Click Open Modal to see overlay")
                                .font_size(11.0)
                                .color([0.6, 0.6, 0.6, 1.0]),
                        ),
                )
            },
        },
        // -- Data Display --
        GalleryEntry {
            name: "Progress",
            category: "Data Display",
            render: |state, _state_arc| {
                let t = state.start_time.elapsed().as_secs_f32();
                let progress = (t % 3.0) / 3.0;
                let pct = (progress * 100.0) as u32;
                AnyView::new(
                    VStack::new(8.0)
                        .child(
                            Text::new(format!("Progress: {}%", pct))
                                .font_size(14.0)
                                .color([0.7, 0.7, 0.7, 1.0]),
                        )
                        .child(Progress::new(progress)),
                )
            },
        },
        GalleryEntry {
            name: "Spinner",
            category: "Data Display",
            render: |_state, _state_arc| {
                let _t = _state.start_time.elapsed().as_secs_f32();
                AnyView::new(
                    HStack::new(8.0)
                        .child(Spinner::new().variant(SpinnerVariant::Ouroboros).size(48.0))
                        .child(
                            Text::new("Loading...")
                                .font_size(14.0)
                                .color([0.7, 0.7, 0.7, 1.0]),
                        ),
                )
            },
        },
        GalleryEntry {
            name: "Alert",
            category: "Feedback",
            render: |_state, _state_arc| {
                AnyView::new(
                    VStack::new(8.0)
                        .child(
                            Text::new("ALERT: Bifrost interference detected")
                                .font_size(14.0)
                                .color([1.0, 0.3, 0.3, 1.0]),
                        )
                        .child(
                            Text::new("Warning: Low runic charge")
                                .font_size(12.0)
                                .color([1.0, 0.7, 0.0, 1.0]),
                        )
                        .child(
                            Text::new("Info: All systems nominal")
                                .font_size(12.0)
                                .color([0.3, 1.0, 0.5, 1.0]),
                        ),
                )
            },
        },
    ]
}

// -- Gallery app state -----------------------------------------------

struct GalleryState {
    selected: usize,
    entries: Vec<GalleryEntry>,
    toggle_1: bool,
    toggle_2: bool,
    checkbox_1: bool,
    checkbox_2: bool,
    slider_value: f32,
    input_text: String,
    start_time: std::time::Instant,
    tab_index: usize,
    combobox_index: Option<usize>,
    autocomplete_query: String,
    selected_date: Date,
    command_query: String,
    command_palette_open: bool,
}

impl GalleryState {
    fn new() -> Self {
        Self {
            selected: 0,
            entries: catalog(),
            toggle_1: false,
            toggle_2: true,
            checkbox_1: false,
            checkbox_2: true,
            slider_value: 0.5,
            input_text: "Placeholder text".to_string(),
            start_time: std::time::Instant::now(),
            tab_index: 0,
            combobox_index: None,
            autocomplete_query: String::new(),
            selected_date: Date {
                year: 2026,
                month: 6,
                day: 27,
            },
            command_query: String::new(),
            command_palette_open: true,
        }
    }
}

// -- Gallery app view ------------------------------------------------

struct GalleryApp {
    state: std::sync::Arc<std::sync::Mutex<GalleryState>>,
}

impl View for GalleryApp {
    type Body = HStack;

    fn body(self) -> Self::Body {
        unreachable!("GalleryApp renders via render(), not body()")
    }

    fn changed(&self) -> bool {
        true
    }

    fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
        let state = self.state.lock().unwrap_or_else(|e| e.into_inner());
        let selected = state.selected;
        let entries = &state.entries;
        let num_entries = entries.len();
        let half = num_entries as f32 / 2.0;

        // 1. Draw Background Area
        renderer.push_vnode(rect, "GalleryApp");
        renderer.fill_rect(rect, [0.02, 0.02, 0.025, 1.0]);

        // 2. Draw 3D Carousel (Top Panel)
        let mut draw_order: Vec<usize> = (0..num_entries).collect();
        draw_order.sort_by(|&a, &b| {
            let mut diff_a = (a as i32 - selected as i32) as f32;
            while diff_a > half {
                diff_a -= num_entries as f32;
            }
            while diff_a < -half {
                diff_a += num_entries as f32;
            }

            let mut diff_b = (b as i32 - selected as i32) as f32;
            while diff_b > half {
                diff_b -= num_entries as f32;
            }
            while diff_b < -half {
                diff_b += num_entries as f32;
            }

            let cos_a = (diff_a * 0.45).cos();
            let cos_b = (diff_b * 0.45).cos();
            cos_a
                .partial_cmp(&cos_b)
                .unwrap_or(std::cmp::Ordering::Equal)
        });

        // Carousel area bounds
        let carousel_height = 200.0;
        let carousel_rect = Rect {
            x: rect.x,
            y: rect.y + 20.0,
            width: rect.width,
            height: carousel_height,
        };

        let center_x = carousel_rect.x + carousel_rect.width / 2.0;
        let center_y = carousel_rect.y + carousel_rect.height / 2.0;

        for i in draw_order {
            let mut diff = (i as i32 - selected as i32) as f32;
            while diff > half {
                diff -= num_entries as f32;
            }
            while diff < -half {
                diff += num_entries as f32;
            }

            let calculate_card_rect = |d: f32| -> Rect {
                let angle = d * 0.42;
                let cos_a = angle.cos();
                let sin_a = angle.sin();
                let scale = 1.0 / (1.0 + 0.35 * (1.0 - cos_a));
                let card_w = 190.0 * scale * cos_a;
                let card_h = 110.0 * scale;
                Rect {
                    x: center_x + 360.0 * sin_a * scale - card_w / 2.0,
                    y: center_y + 12.0 * (1.0 - cos_a) * scale - card_h / 2.0,
                    width: card_w,
                    height: card_h,
                }
            };

            let angle = diff * 0.42;
            let cos_a = angle.cos();
            let scale = 1.0 / (1.0 + 0.35 * (1.0 - cos_a));
            let card_rect = calculate_card_rect(diff);

            let z_index = diff.abs() * 10.0;
            renderer.set_z_index(z_index);

            let is_selected = i == selected;

            let bg_color = if is_selected {
                [0.06, 0.055, 0.06, 1.0]
            } else {
                [0.02, 0.018, 0.02, 1.0]
            };

            let border_color = if is_selected {
                [0.65, 0.58, 0.42, 1.0]
            } else {
                [0.14, 0.13, 0.12, 1.0]
            };

            renderer.push_vnode(card_rect, "CarouselCard");
            renderer.fill_rounded_rect(card_rect, 6.0, bg_color);

            let bevel_h = if is_selected { 2.0 } else { 1.0 };
            let bevel_top = Rect {
                x: card_rect.x + 6.0,
                y: card_rect.y + 1.0,
                width: card_rect.width - 12.0,
                height: bevel_h,
            };
            let bevel_alpha = if is_selected { 0.65 } else { 0.22 };
            renderer.fill_rounded_rect(bevel_top, 1.0, [0.80, 0.72, 0.55, bevel_alpha]);

            let left_bevel = Rect {
                x: card_rect.x + 1.0,
                y: card_rect.y + 6.0,
                width: 1.2,
                height: card_rect.height - 12.0,
            };
            renderer.fill_rounded_rect(left_bevel, 1.0, [0.60, 0.52, 0.38, bevel_alpha * 0.45]);

            let shadow_bottom = Rect {
                x: card_rect.x + 6.0,
                y: card_rect.y + card_rect.height - 2.0,
                width: card_rect.width - 12.0,
                height: 2.0,
            };
            renderer.fill_rounded_rect(shadow_bottom, 1.0, [0.0, 0.0, 0.0, 0.95]);

            renderer.stroke_rounded_rect(
                card_rect,
                6.0,
                border_color,
                if is_selected { 1.5 } else { 0.8 },
            );

            let abs_diff = diff.abs();
            let text_alpha = if is_selected {
                1.0
            } else if abs_diff <= 1.05 {
                0.55
            } else if abs_diff <= 2.05 {
                0.25
            } else {
                0.0
            };

            let text_color = if is_selected {
                [0.0, 1.0, 0.95, text_alpha]
            } else {
                [0.75, 0.70, 0.62, text_alpha]
            };

            if !is_selected {
                let covering_diff = if diff < 0.0 { diff + 1.0 } else { diff - 1.0 };
                let covering_rect = calculate_card_rect(covering_diff);
                let clip_rect = if diff < 0.0 {
                    Rect {
                        x: card_rect.x,
                        y: card_rect.y,
                        width: (covering_rect.x - card_rect.x).max(0.0),
                        height: card_rect.height,
                    }
                } else {
                    let start_x = covering_rect.x + covering_rect.width;
                    Rect {
                        x: start_x,
                        y: card_rect.y,
                        width: ((card_rect.x + card_rect.width) - start_x).max(0.0),
                        height: card_rect.height,
                    }
                };
                renderer.push_clip_rect(clip_rect);
            }

            let cat_font_size = 9.0 * scale;
            let (cat_w, _) = renderer.measure_text(entries[i].category, cat_font_size);
            renderer.draw_text_raw(
                entries[i].category,
                card_rect.x + (card_rect.width - cat_w) / 2.0,
                card_rect.y + 16.0 * scale,
                cat_font_size,
                text_color,
            );

            let name_font_size = 15.0 * scale * cos_a.max(0.6);
            let (name_w, _) = renderer.measure_text(entries[i].name, name_font_size);
            renderer.draw_text_raw(
                entries[i].name,
                card_rect.x + (card_rect.width - name_w) / 2.0,
                card_rect.y + 35.0 * scale,
                name_font_size,
                text_color,
            );

            if !is_selected {
                renderer.pop_clip_rect();
            }

            let state_arc_clone = self.state.clone();
            renderer.register_handler(
                "pointerclick",
                std::sync::Arc::new(move |_| {
                    let mut s = state_arc_clone.lock().unwrap_or_else(|e| e.into_inner());
                    s.selected = i;
                }),
            );

            renderer.pop_vnode();
        }

        // Reset Z-index to default for the rest of the UI
        renderer.set_z_index(0.0);

        // Register scroll-wheel handler for carousel cycling (only once per frame via VDOM setup)
        let wheel_state = self.state.clone();
        static WHEEL_HANDLER_READY: std::sync::atomic::AtomicBool =
            std::sync::atomic::AtomicBool::new(false);

        if !WHEEL_HANDLER_READY.load(std::sync::atomic::Ordering::Relaxed) {
            renderer.register_handler(
                "pointerwheel",
                std::sync::Arc::new(move |evt| {
                    if let Event::PointerWheel { delta_y, .. } = evt {
                        let mut s = wheel_state.lock().unwrap_or_else(|e| e.into_inner());
                        let num = s.entries.len();
                        if num > 0 {
                            // Throttle: only process wheel movements that represent a full step (delta > 50 or < -50)
                            // Native scroll events send line-based deltas scaled by ~10, so check for meaningful movement
                            let raw_delta = delta_y.abs();
                            if raw_delta > 30.0 {
                                if delta_y > 0.0 {
                                    s.selected = (s.selected + 1) % num;
                                } else {
                                    s.selected = (s.selected + num - 1) % num;
                                }
                                // Reset threshold tracking by normalizing the wheel accumulator approach
                            }
                        }
                    }
                }),
            );
            WHEEL_HANDLER_READY.store(true, std::sync::atomic::Ordering::Relaxed);
        }

        // Note: The VDOM stores event handlers in a map and applies patches.
        // For the render()-only GalleryApp, we ensure the handler is registered exactly once by using an AtomicBool flag.

        // 3. Draw Divider Line
        let div_y = carousel_rect.y + carousel_rect.height + 15.0;
        renderer.draw_line(
            rect.x + 40.0,
            div_y,
            rect.x + rect.width - 40.0,
            div_y,
            [0.35, 0.12, 0.15, 0.6],
            1.0,
        );

        // 4. Draw Selected Component Title & View
        let title_y = div_y + 15.0;
        renderer.draw_text_raw(
            format!("GALLERY / {}", entries[selected].name.to_uppercase()).as_str(),
            rect.x + 40.0,
            title_y,
            12.0,
            [0.9, 0.2, 0.25, 0.85],
        );

        // Render target preview area
        let detail = (entries[selected].render)(&state, &self.state);
        let preview_rect = Rect {
            x: rect.x + 40.0,
            y: title_y + 25.0,
            width: rect.width - 80.0,
            height: rect.height - (title_y + 25.0) - 20.0,
        };

        let centered_detail = VStack::new(0.0)
            .distribution(cvkg::core::Distribution::Center)
            .alignment(cvkg::core::Alignment::Center)
            .child(detail)
            .flex(1.0)
            .frame(None, None);

        centered_detail.render(renderer, preview_rect);

        renderer.pop_vnode();
    }
}

impl GalleryApp {
    fn new() -> Self {
        Self {
            state: std::sync::Arc::new(std::sync::Mutex::new(GalleryState::new())),
        }
    }
}

fn main() {
    // Install panic hook that writes crash dump to disk
    std::panic::set_hook(Box::new(|info| {
        let msg = if let Some(s) = info.payload().downcast_ref::<&str>() {
            s.to_string()
        } else if let Some(s) = info.payload().downcast_ref::<String>() {
            s.clone()
        } else {
            "unknown panic".to_string()
        };
        let bt = std::backtrace::Backtrace::force_capture();
        eprintln!("[CVKG PANIC] {msg}");
        eprintln!(
            "[CVKG PANIC] Backtrace:
{bt}"
        );
        if let Ok(mut file) = std::fs::File::create("cvkg-crash.log") {
            use std::io::Write;
            let _ = writeln!(file, "CVKG Panic Dump");
            let _ = writeln!(file, "Message: {msg}");
            let _ = writeln!(
                file,
                "Backtrace:
{bt}"
            );
        }
    }));

    cvkg::native::NativeRenderer::run(GalleryApp::new(), None);
}

#[cfg(test)]
mod smoke_tests {
    #[test]
    fn test_gallery_compiles() {
        assert!(true, "gallery smoke test");
    }
}
