use dear_imgui_rs::{Condition, Context, WindowFlags};
use dear_imgui_wgpu::{WgpuInitInfo, WgpuRenderer};
use glam::EulerRot;
use sdl3_sys::events::SDL_Event;

use crate::entity::EntityHandle;
use crate::world::World;

pub struct FrameStats
{
    pub fps: f32,
    pub frame_ms: f32,
    pub physics_budget_ms: f32,
    pub draw_call_count: usize,
}

pub struct Inspector
{
    imgui: Context,
    renderer: WgpuRenderer,
}

impl Inspector
{
    pub fn new(
        device: &wgpu::Device,
        queue: &wgpu::Queue,
        surface_format: wgpu::TextureFormat,
    ) -> Self
    {
        let mut imgui = Context::create();
        let init_info = WgpuInitInfo::new(device.clone(), queue.clone(), surface_format);
        let renderer =
            WgpuRenderer::new(init_info, &mut imgui).expect("Failed to create imgui wgpu renderer");
        Self { imgui, renderer }
    }

    pub fn init_platform(&mut self, window: &sdl3::video::Window)
    {
        dear_imgui_sdl3::init_for_other(&mut self.imgui, window)
            .expect("Failed to init imgui sdl3 platform");
    }

    pub fn begin_frame(&mut self)
    {
        dear_imgui_sdl3::sdl3_new_frame(&mut self.imgui);
    }

    pub fn process_event(&mut self, raw_event: &SDL_Event)
    {
        dear_imgui_sdl3::process_sys_event(raw_event);
    }

    pub fn wants_keyboard(&self) -> bool
    {
        self.imgui.io().want_capture_keyboard()
    }

    pub fn wants_mouse(&self) -> bool
    {
        self.imgui.io().want_capture_mouse()
    }

    pub fn build_minimal_ui(&mut self, world: &World)
    {
        let ui = self.imgui.frame();
        let state_name = world
            .player_tags
            .all()
            .first()
            .and_then(|e| world.state_machines.get(*e))
            .map(|sm| sm.get_current_state_name())
            .unwrap_or("—");
        ui.window("Player State")
            .position([10.0, 10.0], Condition::FirstUseEver)
            .flags(WindowFlags::ALWAYS_AUTO_RESIZE)
            .build(|| {
                ui.text(format!("State: {}", state_name));
            });
    }

    pub fn build_ui(
        &mut self,
        stats: &FrameStats,
        world: &World,
        selected_entity: Option<EntityHandle>,
        show_player_state: bool,
    )
    {
        let ui = self.imgui.frame();

        if show_player_state
        {
            let state_name = world
                .player_tags
                .all()
                .first()
                .and_then(|e| world.state_machines.get(*e))
                .map(|sm| sm.get_current_state_name())
                .unwrap_or("—");
            ui.window("Player State")
                .position([10.0, 10.0], Condition::FirstUseEver)
                .flags(WindowFlags::ALWAYS_AUTO_RESIZE)
                .build(|| {
                    ui.text(format!("State: {}", state_name));
                });
        }

        ui.window("Inspector")
            .position([10.0, 40.0], Condition::FirstUseEver)
            .build(|| {
                ui.text(format!("FPS:        {:.1}", stats.fps));
                ui.text(format!("Frame:      {:.1} ms", stats.frame_ms));
                ui.text(format!("Physics:    {:.1} ms", stats.physics_budget_ms));
                ui.text(format!("Draw calls: {}", stats.draw_call_count));
            });

        if let Some(entity) = selected_entity
        {
            ui.window("Entity")
                .position([10.0, 120.0], Condition::FirstUseEver)
                .build(|| {
                    let name = world
                        .names
                        .get(entity)
                        .cloned()
                        .unwrap_or_else(|| format!("Entity #{}", entity));
                    ui.text(format!("Name: {}", name));
                    ui.text(format!("ID:   {}", entity));

                    ui.separator();
                    ui.text("Transform");

                    if let Some(transform) = world.transforms.get(entity)
                    {
                        let p = transform.position;
                        ui.text(format!("  Pos   ({:.2}, {:.2}, {:.2})", p.x, p.y, p.z));

                        let (ex, ey, ez) = transform.rotation.to_euler(EulerRot::XYZ);
                        ui.text(format!(
                            "  Rot   ({:.1}, {:.1}, {:.1}) deg",
                            ex.to_degrees(),
                            ey.to_degrees(),
                            ez.to_degrees()
                        ));

                        let s = transform.scale;
                        ui.text(format!("  Scale ({:.2}, {:.2}, {:.2})", s.x, s.y, s.z));
                    }
                    else
                    {
                        ui.text("  (no transform)");
                    }

                    ui.separator();
                    ui.text("Components");

                    if world.meshes.get(entity).is_some()
                    {
                        ui.text("  Mesh");
                    }
                    if world.physics.get(entity).is_some()
                    {
                        ui.text("  Physics");
                    }
                    if let Some(m) = world.movements.get(entity)
                    {
                        ui.text(format!("  Movement (max_speed {:.1})", m.max_walking_speed));
                    }
                    if world.jumps.get(entity).is_some()
                    {
                        ui.text("  Jump");
                    }
                    if world.inputs.get(entity).is_some()
                    {
                        ui.text("  Input");
                    }
                    if world.player_tags.get(entity).is_some()
                    {
                        ui.text("  [Player]");
                    }
                    if world.tree_tags.get(entity).is_some()
                    {
                        ui.text("  [Tree]");
                    }
                    if let Some(cam) = world.cameras.get(entity)
                    {
                        ui.text(format!(
                            "  Camera fov={:.0} near={:.2} far={:.0}",
                            cam.fov.to_degrees(),
                            cam.near,
                            cam.far
                        ));
                    }
                    if let Some(spot) = world.spotlights.get(entity)
                    {
                        let o = spot.offset;
                        ui.text(format!(
                            "  Spotlight offset ({:.1}, {:.1}, {:.1})",
                            o.x, o.y, o.z
                        ));
                    }
                    if let Some(ti) = world.tree_instances.get(entity)
                    {
                        ui.text(format!("  TreeInstances ({})", ti.instances.len()));
                    }
                    if world.follows.get(entity).is_some()
                    {
                        ui.text("  Follow");
                    }
                    if world.rotates.get(entity).is_some()
                    {
                        ui.text("  Rotate");
                    }
                    if let Some(d) = world.dissolves.get(entity)
                    {
                        ui.text(format!("  Dissolve ({:.2})", d.amount));
                    }
                });
        }
    }

    pub fn render(&mut self, encoder: &mut wgpu::CommandEncoder, view: &wgpu::TextureView)
    {
        let draw_data = self.imgui.render();

        let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
            label: Some("ImGui Render Pass"),
            color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                view,
                resolve_target: None,
                ops: wgpu::Operations {
                    load: wgpu::LoadOp::Load,
                    store: wgpu::StoreOp::Store,
                },
                depth_slice: None,
            })],
            depth_stencil_attachment: None,
            occlusion_query_set: None,
            timestamp_writes: None,
            multiview_mask: None,
        });

        self.renderer
            .render_draw_data(draw_data, &mut render_pass)
            .expect("Failed to render imgui");
    }
}
