use documented::Documented;
use gpui::{Hsla, PathBuilder, canvas, point};
use std::f32::consts::PI;

use crate::prelude::*;

/// A circular progress indicator that displays progress as an arc growing clockwise from the top.
#[derive(IntoElement, RegisterComponent, Documented)]
pub struct CircularProgress {
    value: f32,
    max_value: f32,
    size: Pixels,
    stroke_width: Pixels,
    bg_color: Hsla,
    progress_color: Hsla,
}

impl CircularProgress {
    pub fn new(value: f32, max_value: f32, size: Pixels, cx: &App) -> Self {
        Self {
            value,
            max_value,
            size,
            stroke_width: px(4.0),
            bg_color: semantic::border_muted(cx),
            progress_color: palette::primary(600),
        }
    }

    /// Sets the current progress value.
    pub fn value(mut self, value: f32) -> Self {
        self.value = value;
        self
    }

    /// Sets the maximum value for the progress indicator.
    pub fn max_value(mut self, max_value: f32) -> Self {
        self.max_value = max_value;
        self
    }

    /// Sets the size (diameter) of the circular progress indicator.
    pub fn size(mut self, size: Pixels) -> Self {
        self.size = size;
        self
    }

    /// Sets the stroke width of the circular progress indicator.
    pub fn stroke_width(mut self, stroke_width: Pixels) -> Self {
        self.stroke_width = stroke_width;
        self
    }

    /// Sets the background circle color.
    pub fn bg_color(mut self, color: Hsla) -> Self {
        self.bg_color = color;
        self
    }

    /// Sets the progress arc color.
    pub fn progress_color(mut self, color: Hsla) -> Self {
        self.progress_color = color;
        self
    }
}

impl RenderOnce for CircularProgress {
    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
        let value = self.value;
        let max_value = self.max_value;
        let size = self.size;
        let bg_color = self.bg_color;
        let progress_color = self.progress_color;

        canvas(
            |_, _, _| {},
            move |bounds, _, window, _cx| {
                let current_value = value;

                let center_x = bounds.origin.x + bounds.size.width / 2.0;
                let center_y = bounds.origin.y + bounds.size.height / 2.0;

                let stroke_width = self.stroke_width;
                let radius = (size / 2.0) - stroke_width;

                // Draw background circle (full 360 degrees)
                let mut bg_builder = PathBuilder::stroke(stroke_width);

                // Start at rightmost point
                bg_builder.move_to(point(center_x + radius, center_y));

                // Draw full circle using two 180-degree arcs
                bg_builder.arc_to(
                    point(radius, radius),
                    px(0.),
                    false,
                    true,
                    point(center_x - radius, center_y),
                );
                bg_builder.arc_to(
                    point(radius, radius),
                    px(0.),
                    false,
                    true,
                    point(center_x + radius, center_y),
                );
                bg_builder.close();

                if let Ok(path) = bg_builder.build() {
                    window.paint_path(path, bg_color);
                }

                // Draw progress arc if there's any progress
                let progress = (current_value / max_value).clamp(0.0, 1.0);
                if progress > 0.0 {
                    let mut progress_builder = PathBuilder::stroke(stroke_width);

                    // Handle 100% progress as a special case by drawing a full circle
                    if progress >= 0.999 {
                        // Start at rightmost point
                        progress_builder.move_to(point(center_x + radius, center_y));

                        // Draw full circle using two 180-degree arcs
                        progress_builder.arc_to(
                            point(radius, radius),
                            px(0.),
                            false,
                            true,
                            point(center_x - radius, center_y),
                        );
                        progress_builder.arc_to(
                            point(radius, radius),
                            px(0.),
                            false,
                            true,
                            point(center_x + radius, center_y),
                        );
                        progress_builder.close();
                    } else {
                        // Start at 12 o'clock (top) position
                        let start_x = center_x;
                        let start_y = center_y - radius;
                        progress_builder.move_to(point(start_x, start_y));

                        // Calculate the end point of the arc based on progress
                        // Progress sweeps clockwise from -90° (top)
                        let angle = -PI / 2.0 + (progress * 2.0 * PI);
                        let end_x = center_x + radius * angle.cos();
                        let end_y = center_y + radius * angle.sin();

                        // Use large_arc flag when progress > 0.5 (more than 180 degrees)
                        let large_arc = progress > 0.5;

                        progress_builder.arc_to(
                            point(radius, radius),
                            px(0.),
                            large_arc,
                            true, // sweep clockwise
                            point(end_x, end_y),
                        );
                    }

                    if let Ok(path) = progress_builder.build() {
                        window.paint_path(path, progress_color);
                    }
                }
            },
        )
        .size(size)
    }
}

impl Component for CircularProgress {
    fn scope() -> ComponentScope {
        ComponentScope::Status
    }

    fn description() -> Option<&'static str> {
        Some(
            "A circular progress indicator that displays progress as an arc growing clockwise from the top.",
        )
    }

    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
        let max_value = 100.0;
        let container = || v_flex().items_center().gap_1();

        Some(
            example_group(vec![single_example(
                "Examples",
                h_flex()
                    .gap_6()
                    .child(
                        container()
                            .child(CircularProgress::new(0.0, max_value, px(48.0), cx))
                            .child(Label::new("0%").size(LabelSize::Small)),
                    )
                    .child(
                        container()
                            .child(CircularProgress::new(25.0, max_value, px(48.0), cx))
                            .child(Label::new("25%").size(LabelSize::Small)),
                    )
                    .child(
                        container()
                            .child(CircularProgress::new(50.0, max_value, px(48.0), cx))
                            .child(Label::new("50%").size(LabelSize::Small)),
                    )
                    .child(
                        container()
                            .child(CircularProgress::new(75.0, max_value, px(48.0), cx))
                            .child(Label::new("75%").size(LabelSize::Small)),
                    )
                    .child(
                        container()
                            .child(CircularProgress::new(100.0, max_value, px(48.0), cx))
                            .child(Label::new("100%").size(LabelSize::Small)),
                    )
                    .into_any_element(),
            )])
            .into_any_element(),
        )
    }
}
