use nalgebra_glm::{Vec2, Vec4};

use crate::ecs::text::components::TextProperties;
use crate::render::wgpu::passes::geometry::{UiLayer, UiRect};

use super::super::layout::LayoutStack;
use super::super::types::{Rect, UiId, UiResponse, WidgetState};
use super::types::{ImmediateUi, KeyInput, ReservedAreas, UiWidget};

impl ImmediateUi {
    pub fn new() -> Self {
        Self {
            enabled: true,
            ..Default::default()
        }
    }

    pub fn begin_frame(
        &mut self,
        screen_size: Vec2,
        mouse_pos: Vec2,
        mouse_down: bool,
        mouse_just_pressed: bool,
        mouse_just_released: bool,
        scroll_delta: Vec2,
    ) {
        self.apply_theme();
        self.theme_state.clear_preview();

        self.frame_widgets.clear();
        self.frame_rects.clear();
        self.frame_text_meshes.clear();
        self.deferred_rects.clear();
        self.deferred_widgets.clear();
        self.layout_stack = LayoutStack::new();
        self.id_stack.clear();
        self.clip_stack.clear();
        self.scroll_area_stack.clear();
        self.reserved_areas = ReservedAreas::default();
        self.screen_size = screen_size;
        self.mouse_pos = mouse_pos;
        self.mouse_down = mouse_down;
        self.mouse_just_pressed = mouse_just_pressed;
        self.mouse_just_released = mouse_just_released;
        self.scroll_delta = scroll_delta;
        self.hot_widget = None;
        self.current_layer = UiLayer::Background;
        self.next_z_index = 0;

        if let Some(bg) = self.background_color {
            self.frame_rects.push(UiRect {
                position: Vec2::new(0.0, 0.0),
                size: screen_size,
                color: bg,
                corner_radius: 0.0,
                border_width: 0.0,
                border_color: Vec4::new(0.0, 0.0, 0.0, 0.0),
                clip_rect: None,
                layer: UiLayer::Background,
                z_index: 0,
            });
            self.next_z_index = 1;
        }
    }

    pub(super) fn apply_theme(&mut self) {
        let theme = self.theme_state.active_theme();
        self.style.text_color = theme.text_color;
        self.style.text_color_disabled = theme.text_color_disabled;
        self.style.background_color = theme.background_color;
        self.style.background_color_hovered = theme.background_color_hovered;
        self.style.background_color_active = theme.background_color_active;
        self.style.border_color = theme.border_color;
        self.style.border_width = theme.border_width;
        self.style.corner_radius = theme.corner_radius;
        self.style.padding = theme.padding;
        self.style.spacing = theme.spacing;
        self.style.font_size = theme.font_size;
        self.style.button_height = theme.button_height;
        self.style.slider_height = theme.slider_height;
        self.style.checkbox_size = theme.checkbox_size;
        self.style.panel_header_height = theme.panel_header_height;
    }

    pub fn set_layer(&mut self, layer: UiLayer) {
        self.current_layer = layer;
    }

    pub fn end_frame(&mut self) {
        self.frame_rects.append(&mut self.deferred_rects);
        self.frame_widgets.append(&mut self.deferred_widgets);

        if self.mouse_just_released {
            self.active_widget = None;
        }

        self.char_input.clear();
        self.key_input.clear();
    }

    pub fn push_id(&mut self, id: &str) {
        let new_id = if let Some(parent) = self.id_stack.last() {
            UiId::with_parent(*parent, id)
        } else {
            UiId::new(id)
        };
        self.id_stack.push(new_id);
    }

    pub fn pop_id(&mut self) {
        self.id_stack.pop();
    }

    pub(super) fn make_id(&self, id: &str) -> UiId {
        if let Some(parent) = self.id_stack.last() {
            UiId::with_parent(*parent, id)
        } else {
            UiId::new(id)
        }
    }

    pub fn set_background(&mut self, color: Option<Vec4>) {
        self.background_color = color;
    }

    pub fn add_char_input(&mut self, character: char) {
        self.char_input.push(character);
    }

    pub fn add_key_input(&mut self, key: winit::keyboard::KeyCode, pressed: bool) {
        self.key_input.push(KeyInput { key, pressed });
    }

    pub(super) fn current_clip(&self) -> Option<Rect> {
        self.clip_stack.last().copied()
    }

    pub fn push_clip(&mut self, rect: Rect) {
        let effective_clip = if let Some(current) = self.current_clip() {
            current.intersect(&rect).unwrap_or(rect)
        } else {
            rect
        };
        self.clip_stack.push(effective_clip);
    }

    pub fn pop_clip(&mut self) {
        self.clip_stack.pop();
    }

    pub(super) fn add_rect(&mut self, mut rect: UiRect) {
        rect.clip_rect = self.current_clip();
        rect.layer = self.current_layer;
        rect.z_index = self.current_panel_z_base + self.next_z_index;
        self.next_z_index += 1;
        self.frame_rects.push(rect);
    }

    pub fn add_rect_raw(&mut self, rect: UiRect) {
        self.frame_rects.push(rect);
    }

    pub(super) fn add_text(&mut self, text: &str, position: Vec2, properties: TextProperties) {
        self.frame_widgets.push(UiWidget {
            text: text.to_string(),
            position,
            properties,
            clip_rect: self.current_clip(),
            layer: self.current_layer,
            z_index: self.current_panel_z_base + self.next_z_index,
        });
        self.next_z_index += 1;
    }

    pub fn add_text_raw(
        &mut self,
        text: &str,
        position: Vec2,
        properties: TextProperties,
        clip_rect: Option<Rect>,
        layer: UiLayer,
        z_index: i32,
    ) {
        self.frame_widgets.push(UiWidget {
            text: text.to_string(),
            position,
            properties,
            clip_rect,
            layer,
            z_index,
        });
    }

    pub(super) fn get_state(&self, id: UiId) -> WidgetState {
        self.widget_states.get(&id).copied().unwrap_or_default()
    }

    pub(super) fn set_state(&mut self, id: UiId, state: WidgetState) {
        self.widget_states.insert(id, state);
    }

    pub(super) fn interact(&mut self, id: UiId, rect: Rect) -> UiResponse {
        self.interact_internal(id, rect, true)
    }

    pub(super) fn interact_unclipped(&mut self, id: UiId, rect: Rect) -> UiResponse {
        self.interact_internal(id, rect, false)
    }

    fn interact_internal(&mut self, id: UiId, rect: Rect, use_clip: bool) -> UiResponse {
        let clipped_rect = if use_clip {
            if let Some(clip) = self.clip_stack.last() {
                rect.intersect(clip)
            } else {
                Some(rect)
            }
        } else {
            Some(rect)
        };
        let hovered = clipped_rect
            .map(|r| r.contains(self.mouse_pos))
            .unwrap_or(false);
        let was_active = self.active_widget == Some(id);

        if hovered {
            self.hot_widget = Some(id);
        }

        let clicked = hovered && self.mouse_just_released && was_active;
        let active = if hovered && self.mouse_just_pressed {
            self.active_widget = Some(id);
            true
        } else {
            was_active && self.mouse_down
        };

        let state = self.get_state(id);
        let drag_started = active && !was_active;
        let dragging = active && state.drag_start.is_some();
        let drag_released = was_active && self.mouse_just_released;

        let mut new_state = state;
        new_state.hovered = hovered;
        new_state.active = active;
        if drag_started {
            new_state.drag_start = Some(self.mouse_pos);
        }
        if drag_released {
            new_state.drag_start = None;
        }
        self.set_state(id, new_state);

        UiResponse {
            clicked,
            double_clicked: false,
            hovered,
            active,
            drag_started,
            dragging,
            drag_released,
            changed: false,
            rect,
            id,
        }
    }
}
