use gtk::prelude::*;
use gtk::glib::prelude::ToVariant;
use std::cell::RefCell;

#[derive(Clone)]
pub struct StepIndicator {
    container: gtk::Box,
    grid: gtk::Grid,
    dots: RefCell<Vec<StepDot>>,
    /// Maps visual index -> actual step index
    step_map: RefCell<Vec<usize>>,
}

#[derive(Clone)]
struct StepDot {
    button: gtk::Button,
    icon: gtk::Image,
    label: gtk::Label,
}

impl StepIndicator {
    pub fn new(step_names: &[String]) -> Self {
        let container = gtk::Box::builder()
            .orientation(gtk::Orientation::Horizontal)
            .halign(gtk::Align::Center)
            .spacing(0)
            .margin_top(8)
            .margin_bottom(8)
            .margin_start(12)
            .margin_end(12)
            .build();

        container.set_overflow(gtk::Overflow::Hidden);

        container.update_property(&[
            gtk::accessible::Property::Label("Wizard step indicator"),
        ]);

        let grid = gtk::Grid::builder()
            .column_homogeneous(false)
            .row_spacing(2)
            .column_spacing(0)
            .hexpand(false)
            .build();

        let indices: Vec<usize> = (0..step_names.len()).collect();
        let dots = Self::build_dots(&grid, step_names, &indices);

        container.append(&grid);

        // First step starts as current
        let total = dots.len();
        if let Some(first) = dots.first() {
            first.icon.set_icon_name(Some("radio-checked-symbolic"));
            first.button.set_sensitive(true);
            first.label.add_css_class("accent");
            first.button.update_property(&[
                gtk::accessible::Property::Label(
                    &format!("Step 1 of {}: {} (current)", total, first.label.label())
                ),
            ]);
        }
        // Label all non-current dots
        for (i, dot) in dots.iter().enumerate().skip(1) {
            dot.button.update_property(&[
                gtk::accessible::Property::Label(
                    &format!("Step {} of {}: {}", i + 1, total, dot.label.label())
                ),
            ]);
        }

        Self {
            container,
            grid,
            dots: RefCell::new(dots),
            step_map: RefCell::new(indices),
        }
    }

    fn build_dots(grid: &gtk::Grid, names: &[String], step_indices: &[usize]) -> Vec<StepDot> {
        let mut dots = Vec::new();

        for (visual_i, (name, &actual_i)) in names.iter().zip(step_indices.iter()).enumerate() {
            let col = (visual_i * 2) as i32;

            if visual_i > 0 {
                let line = gtk::Separator::builder()
                    .orientation(gtk::Orientation::Horizontal)
                    .hexpand(false)
                    .valign(gtk::Align::Center)
                    .build();
                line.set_size_request(12, -1);
                grid.attach(&line, col - 1, 0, 1, 1);
            }

            let icon = gtk::Image::builder()
                .icon_name("radio-symbolic")
                .pixel_size(16)
                .build();

            let button = gtk::Button::builder()
                .child(&icon)
                .has_frame(false)
                .tooltip_text(format!("Step {}: {} (Alt+{})", visual_i + 1, name, actual_i + 1))
                .sensitive(false)
                .action_name("win.goto-step")
                .action_target(&(actual_i as i32 + 1).to_variant())
                .halign(gtk::Align::Center)
                .build();
            button.add_css_class("circular");

            let label = gtk::Label::builder()
                .label(name)
                .css_classes(["caption"])
                .ellipsize(gtk::pango::EllipsizeMode::End)
                .width_chars(10)
                .halign(gtk::Align::Center)
                .build();

            grid.attach(&button, col, 0, 1, 1);
            grid.attach(&label, col, 1, 1, 1);

            dots.push(StepDot {
                button,
                icon,
                label,
            });
        }

        dots
    }

    /// Rebuild the indicator to show only the given steps.
    /// `visible_steps` is a list of (actual_step_index, name).
    pub fn rebuild(&self, visible_steps: &[(usize, String)]) {
        // Clear the grid
        while let Some(child) = self.grid.first_child() {
            self.grid.remove(&child);
        }

        let names: Vec<String> = visible_steps.iter().map(|(_, n)| n.clone()).collect();
        let indices: Vec<usize> = visible_steps.iter().map(|(i, _)| *i).collect();

        let dots = Self::build_dots(&self.grid, &names, &indices);
        *self.dots.borrow_mut() = dots;
        *self.step_map.borrow_mut() = indices;
    }

    /// Set the current step by actual step index. Finds the visual position.
    pub fn set_current(&self, actual_index: usize) {
        let dots = self.dots.borrow();
        let map = self.step_map.borrow();
        let total = dots.len();
        for (visual_i, dot) in dots.iter().enumerate() {
            let is_current = map.get(visual_i) == Some(&actual_index);
            if is_current {
                dot.icon.set_icon_name(Some("radio-checked-symbolic"));
                dot.button.set_sensitive(true);
                dot.label.add_css_class("accent");
                dot.button.update_property(&[
                    gtk::accessible::Property::Label(
                        &format!("Step {} of {}: {} (current)", visual_i + 1, total, dot.label.label())
                    ),
                ]);
            } else if dot.icon.icon_name().as_deref() != Some("emblem-ok-symbolic") {
                dot.icon.set_icon_name(Some("radio-symbolic"));
                dot.label.remove_css_class("accent");
                dot.button.update_property(&[
                    gtk::accessible::Property::Label(
                        &format!("Step {} of {}: {}", visual_i + 1, total, dot.label.label())
                    ),
                ]);
            }
        }
    }

    /// Mark a step as completed by actual step index.
    pub fn set_completed(&self, actual_index: usize) {
        let dots = self.dots.borrow();
        let map = self.step_map.borrow();
        let total = dots.len();
        if let Some(visual_i) = map.iter().position(|&i| i == actual_index) {
            if let Some(dot) = dots.get(visual_i) {
                dot.icon.set_icon_name(Some("emblem-ok-symbolic"));
                dot.button.set_sensitive(true);
                dot.label.remove_css_class("accent");
                dot.button.update_property(&[
                    gtk::accessible::Property::Label(
                        &format!("Step {} of {}: {} (completed)", visual_i + 1, total, dot.label.label())
                    ),
                ]);
            }
        }
    }

    pub fn widget(&self) -> &gtk::Box {
        &self.container
    }
}

// Allow appending the step indicator to containers
impl std::ops::Deref for StepIndicator {
    type Target = gtk::Box;

    fn deref(&self) -> &Self::Target {
        &self.container
    }
}
