//! ProjectBox custom Elements:
//! - `ProjectBoxHeaderElement` — paints header + tab bar directly (zero div overhead)
//! - `ProjectBoxFullElement`   — wraps header paint + content AnyElement as a single
//!   custom Element, eliminating the outer root div from ProjectBox::render()

use gpui::*;
use std::sync::atomic::Ordering;

use crate::state::AgentStatus;
use crate::theme;

pub const HEADER_HEIGHT: f32 = 36.0;
pub const TAB_BAR_HEIGHT: f32 = 28.0;
pub const CHROME_HEIGHT: f32 = HEADER_HEIGHT + TAB_BAR_HEIGHT;

const TAB_LABELS: [&str; 3] = ["Model", "Docs", "Files"];

// ── Shared paint helper ───────────────────────────────────────────────────────

/// Paint header background + tab bar into `bounds`. Used by both Elements.
fn paint_chrome(
    bounds: Bounds<Pixels>,
    name: &SharedString,
    cwd: &SharedString,
    accent: Rgba,
    status: AgentStatus,
    blink_visible: &Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
    active_tab: usize,
    window: &mut Window,
    cx: &mut App,
) {
    let o = bounds.origin;
    let w = bounds.size.width;
    let hh = px(HEADER_HEIGHT);
    let tbh = px(TAB_BAR_HEIGHT);

    // Header background (rounded top corners)
    window.paint_quad(PaintQuad {
        bounds: Bounds { origin: o, size: size(w, hh) },
        corner_radii: Corners {
            top_left: px(6.0), top_right: px(6.0),
            bottom_left: px(0.0), bottom_right: px(0.0),
        },
        background: theme::MANTLE.into(),
        border_widths: Edges::default(),
        border_color: transparent_black(),
        border_style: BorderStyle::default(),
    });
    // Header bottom border
    window.paint_quad(fill(
        Bounds { origin: point(o.x, o.y + hh - px(1.0)), size: size(w, px(1.0)) },
        theme::SURFACE0,
    ));

    // Accent stripe
    let stripe_x = o.x + px(12.0);
    window.paint_quad(
        fill(Bounds { origin: point(stripe_x, o.y + (hh - px(20.0)) * 0.5),
                      size: size(px(3.0), px(20.0)) }, accent)
        .corner_radii(px(2.0)),
    );

    // Status dot
    let dot_color = match status {
        AgentStatus::Running => if blink_visible.as_ref()
            .map(|b| b.load(Ordering::Relaxed)).unwrap_or(true)
            { theme::GREEN } else { theme::SURFACE1 },
        AgentStatus::Idle  => theme::OVERLAY0,
        AgentStatus::Done  => theme::BLUE,
        AgentStatus::Error => theme::RED,
    };
    let dot_x = stripe_x + px(11.0);
    window.paint_quad(
        fill(Bounds { origin: point(dot_x, o.y + (hh - px(8.0)) * 0.5),
                      size: size(px(8.0), px(8.0)) }, dot_color)
        .corner_radii(px(4.0)),
    );

    // Name + CWD text
    let ts = window.text_system().clone();
    if !name.contains('\n') {
        let run = TextRun { len: name.len(), font: font(".SystemUIFont"),
                            color: theme::TEXT.into(), background_color: None,
                            underline: None, strikethrough: None };
        let shaped = ts.shape_line(name.clone(), px(13.0), &[run], None);
        let _ = shaped.paint(point(dot_x + px(12.0), o.y + (hh - px(13.0)) * 0.5), hh, window, cx);
    }
    if !cwd.contains('\n') {
        let run = TextRun { len: cwd.len(), font: font(".SystemUIFont"),
                            color: theme::OVERLAY0.into(), background_color: None,
                            underline: None, strikethrough: None };
        let shaped = ts.shape_line(cwd.clone(), px(10.0), &[run], None);
        let cwd_x = o.x + w - shaped.width - px(12.0);
        let _ = shaped.paint(point(cwd_x, o.y + (hh - px(10.0)) * 0.5), hh, window, cx);
    }

    // Tab bar
    let tab_y = o.y + hh;
    window.paint_quad(fill(
        Bounds { origin: point(o.x, tab_y), size: size(w, tbh) },
        theme::MANTLE,
    ));
    window.paint_quad(fill(
        Bounds { origin: point(o.x, tab_y + tbh - px(1.0)), size: size(w, px(1.0)) },
        theme::SURFACE0,
    ));
    let mut tab_x = o.x + px(8.0);
    for (i, label) in TAB_LABELS.iter().enumerate() {
        let active = i == active_tab;
        let color: Hsla = if active { accent.into() } else { theme::SUBTEXT0.into() };
        let run = TextRun { len: label.len(), font: font(".SystemUIFont"),
                            color, background_color: None,
                            underline: None, strikethrough: None };
        let shaped = ts.shape_line(SharedString::from(*label), px(11.0), &[run], None);
        let label_y = tab_y + (tbh - px(11.0)) * 0.5;
        let _ = shaped.paint(point(tab_x, label_y), tbh, window, cx);
        if active {
            window.paint_quad(fill(
                Bounds { origin: point(tab_x, tab_y + tbh - px(3.0)),
                         size: size(shaped.width, px(2.0)) },
                accent,
            ));
        }
        tab_x = tab_x + shaped.width + px(12.0);
    }
}

// ── ProjectBoxHeaderElement ───────────────────────────────────────────────────

/// Standalone header + tab bar element. Used when the outer container is still a div.
/// Kept for compatibility; ProjectBoxFullElement supersedes it.
pub struct ProjectBoxHeaderElement {
    pub id: ElementId,
    pub name: SharedString,
    pub cwd: SharedString,
    pub accent: Rgba,
    pub status: AgentStatus,
    pub blink_visible: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
    pub active_tab: usize,
}

impl IntoElement for ProjectBoxHeaderElement {
    type Element = Self;
    fn into_element(self) -> Self { self }
}

impl Element for ProjectBoxHeaderElement {
    type RequestLayoutState = ();
    type PrepaintState = ();

    fn id(&self) -> Option<ElementId> { Some(self.id.clone()) }
    fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { None }

    fn request_layout(
        &mut self, _id: Option<&GlobalElementId>, _iid: Option<&InspectorElementId>,
        window: &mut Window, cx: &mut App,
    ) -> (LayoutId, ()) {
        let style = Style {
            size: Size {
                width: Length::Definite(relative(1.0)),
                height: Length::Definite(DefiniteLength::Absolute(AbsoluteLength::Pixels(
                    px(CHROME_HEIGHT)))),
            },
            flex_shrink: 0.0,
            ..Style::default()
        };
        (window.request_layout(style, [], cx), ())
    }

    fn prepaint(
        &mut self, _id: Option<&GlobalElementId>, _iid: Option<&InspectorElementId>,
        _bounds: Bounds<Pixels>, _rl: &mut (), _window: &mut Window, _cx: &mut App,
    ) {}

    fn paint(
        &mut self, _id: Option<&GlobalElementId>, _iid: Option<&InspectorElementId>,
        bounds: Bounds<Pixels>, _rl: &mut (), _pp: &mut (),
        window: &mut Window, cx: &mut App,
    ) {
        paint_chrome(bounds, &self.name, &self.cwd, self.accent, self.status,
                     &self.blink_visible, self.active_tab, window, cx);
    }
}

// ── ProjectBoxFullElement ─────────────────────────────────────────────────────

/// Single custom Element for the entire ProjectBox card (header + tabs + content).
/// ProjectBox::render() returns this — no outer root div needed.
pub struct ProjectBoxFullElement {
    pub id: ElementId,
    pub name: SharedString,
    pub cwd: SharedString,
    pub accent: Rgba,
    pub status: AgentStatus,
    pub blink_visible: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
    pub active_tab: usize,
    /// Content area div (AgentPane + TerminalView or placeholder). Moved out in
    /// request_layout and carried as RequestLayoutState through to paint.
    pub content: AnyElement,
}

impl IntoElement for ProjectBoxFullElement {
    type Element = Self;
    fn into_element(self) -> Self { self }
}

impl Element for ProjectBoxFullElement {
    /// Content AnyElement carried from request_layout → prepaint → paint.
    type RequestLayoutState = AnyElement;
    type PrepaintState = ();

    fn id(&self) -> Option<ElementId> { Some(self.id.clone()) }
    fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { None }

    fn request_layout(
        &mut self,
        _id: Option<&GlobalElementId>,
        _iid: Option<&InspectorElementId>,
        window: &mut Window,
        cx: &mut App,
    ) -> (LayoutId, AnyElement) {
        // Move content out of self so we can call request_layout on it (needs &mut self).
        let mut content = Empty.into_any_element();
        std::mem::swap(&mut content, &mut self.content);
        let child_id = content.request_layout(window, cx);

        // Root style: flex-1 with min constraints (allows 2 boxes side-by-side in flex-wrap grid).
        let style = Style {
            flex_grow: 1.0,
            flex_shrink: 0.0,
            min_size: size(px(400.0).into(), px(300.0).into()),
            display: Display::Flex,
            flex_direction: FlexDirection::Column,
            overflow: Point { x: Overflow::Hidden, y: Overflow::Hidden },
            ..Style::default()
        };
        (window.request_layout(style, [child_id], cx), content)
    }

    fn prepaint(
        &mut self,
        _id: Option<&GlobalElementId>,
        _iid: Option<&InspectorElementId>,
        bounds: Bounds<Pixels>,
        content: &mut AnyElement,
        window: &mut Window,
        cx: &mut App,
    ) -> () {
        // Place content below the painted chrome (header + tab bar).
        let content_origin = point(bounds.origin.x, bounds.origin.y + px(CHROME_HEIGHT));
        content.prepaint_at(content_origin, window, cx);
    }

    fn paint(
        &mut self,
        _id: Option<&GlobalElementId>,
        _iid: Option<&InspectorElementId>,
        bounds: Bounds<Pixels>,
        content: &mut AnyElement,
        _pp: &mut (),
        window: &mut Window,
        cx: &mut App,
    ) {
        // 0. Card background + border (rounded rect)
        window.paint_quad(PaintQuad {
            bounds,
            corner_radii: Corners::all(px(8.0)),
            background: theme::BASE.into(),
            border_widths: Edges::all(px(1.0)),
            border_color: theme::SURFACE0.into(),
            border_style: BorderStyle::default(),
        });
        // 1. Paint header + tabs directly.
        paint_chrome(bounds, &self.name, &self.cwd, self.accent, self.status,
                     &self.blink_visible, self.active_tab, window, cx);
        // 2. Paint the content child (already prepainted at correct origin).
        content.paint(window, cx);
    }
}
