use anymotion::ecs::components::JointPalette;
use anymotion::ecs::resources::{AnimationClipAssets, SkeletonAssets};
use anymotion::prelude::*;
use archetype_ecs::{GlobalTransform, LocalTransform, Plugin, World};
use ash_renderer::prelude::*;
use winit::{
    application::ApplicationHandler,
    event::WindowEvent,
    event_loop::{ActiveEventLoop, EventLoop},
    window::{Window, WindowId},
};

use ash_renderer::renderer::{Mesh, SkinnedVertex, Vertex};

use std::sync::{Arc, Mutex};

struct App {
    window: Option<Window>,
    world: World,
}

impl ApplicationHandler for App {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
        let window = event_loop.create_window(Default::default()).unwrap();

        let surface_provider = ash_renderer::vulkan::WindowSurfaceProvider::new(&window);
        let mut renderer = Renderer::new(&surface_provider).expect("Vulkan init failed");

        // Create and upload mesh geometry
        let (vertices, indices) = create_test_mesh();

        let standard_vertices: Vec<Vertex> = vertices
            .iter()
            .map(|v| Vertex {
                position: v.position,
                normal: v.normal,
                uv: v.uv,
                color: [1.0, 1.0, 1.0],
                tangent: [1.0, 0.0, 0.0, 1.0],
            })
            .collect();

        // Create Mesh manually to support SkinnedVertex
        // MeshDescriptor does not expose skinned_vertices, so we must build the Mesh struct directly.
        let mut mesh = Mesh::default();
        mesh.name = "test".to_string();
        mesh.skinned_vertices = vertices;
        mesh.indices = Some(indices.clone());

        // We also populate standard Vertices for bounding box generation (clusters)
        mesh.vertices = standard_vertices;

        // Generate clusters for culling (optional for basic rendering but good practice)
        mesh.clusters = Mesh::generate_clusters(&indices, &mesh.vertices);

        // Upload mesh
        // Use handle 0 for this test mesh
        let mesh_handle = 0;
        renderer
            .register_mesh_handle(mesh_handle, &mut mesh)
            .expect("Failed to register skinned mesh");

        self.world.insert_resource(Arc::new(Mutex::new(renderer)));

        // Setup Animation World
        let plugin = AnimationPlugin::new();
        let mut app_builder = archetype_ecs::App::new();
        plugin.build(&mut app_builder);

        // Create Test Entities (Full Skeleton)
        let skeleton = create_test_skeleton();
        let clip = create_test_walk_clip().unwrap();

        let skele_assets = app_builder.world.resource_mut::<SkeletonAssets>().unwrap();
        let skele_handle = skele_assets.add(skeleton.clone());

        let clip_assets = app_builder
            .world
            .resource_mut::<AnimationClipAssets>()
            .unwrap();
        let clip_handle = clip_assets.add(clip);

        // Spawn ALL bone entities
        let mut bone_entities = Vec::new();
        for (i, _bone) in skeleton.bones.iter().enumerate() {
            let bone_entity = app_builder.world.spawn((
                BoneJoint {
                    bone_index: i,
                    skeleton: skele_handle,
                },
                LocalTransform::default(),
                GlobalTransform::identity(),
            ));
            bone_entities.push(bone_entity);
        }

        // Setup Parent Relationships (Matching create_test_skeleton)
        let _ = app_builder.world.add_component(
            bone_entities[1],
            Parent {
                entity: bone_entities[0],
            },
        );
        let _ = app_builder.world.add_component(
            bone_entities[2],
            Parent {
                entity: bone_entities[1],
            },
        );
        let _ = app_builder.world.add_component(
            bone_entities[3],
            Parent {
                entity: bone_entities[1],
            },
        );
        let _ = app_builder.world.add_component(
            bone_entities[4],
            Parent {
                entity: bone_entities[1],
            },
        );

        // Spawn Animated Character
        app_builder.world.spawn((
            Animator::new(skele_handle, clip_handle),
            SkinnedMesh {
                skeleton: skele_handle,
                mesh_handle: 0,
                bone_entities: bone_entities.clone(),
                joint_offset: 0,
            },
            GlobalTransform::identity(),
            JointPalette::new(skeleton.bones.len()),
        ));

        self.world = app_builder.world;
        self.window = Some(window);
    }

    fn window_event(&mut self, _el: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
        match event {
            WindowEvent::RedrawRequested => {
                if let Some(w) = &self.window {
                    let dt = 0.016;
                    // Run standard animation pipeline
                    AnimationPipeline::update(&mut self.world, dt);

                    // Render
                    if let Some(renderer_res) = self.world.resource::<Arc<Mutex<Renderer>>>() {
                        if let Ok(mut renderer) = renderer_res.lock() {
                            let size = w.inner_size();
                            if size.width > 0 && size.height > 0 {
                                let aspect = size.width as f32 / size.height as f32;

                                let view = glam::Mat4::look_at_rh(
                                    glam::Vec3::new(0.0, 2.0, 5.0),
                                    glam::Vec3::ZERO,
                                    glam::Vec3::Y,
                                );
                                let proj = glam::Mat4::perspective_rh(1.0, aspect, 0.1, 100.0);

                                // Queue Skinned Draw Command
                                // This is crucial! Without this, the renderer treats it as a static mesh (Vertex vs SkinnedVertex stride mismatch)
                                renderer.draw_skinned_mesh(
                                    0, // mesh_handle
                                    0, // material_handle
                                    glam::Mat4::IDENTITY,
                                    0, // joint_offset
                                );

                                // Handle potential errors
                                if let Err(e) = renderer.render_frame(
                                    view,
                                    proj,
                                    glam::Vec3::new(0.0, 2.0, 5.0),
                                ) {
                                    eprintln!("Render error: {e}");
                                }
                            }
                        }
                    }
                    w.request_redraw();
                }
            }
            WindowEvent::Resized(size) => {
                if let Some(renderer_res) = self.world.resource::<Arc<Mutex<Renderer>>>() {
                    if let Ok(mut renderer) = renderer_res.lock() {
                        if size.width > 0 && size.height > 0 {
                            let _ = renderer.request_swapchain_resize(ash::vk::Extent2D {
                                width: size.width,
                                height: size.height,
                            });
                        }
                    }
                }
            }
            WindowEvent::CloseRequested => {
                _el.exit();
            }
            _ => {}
        }
    }
}

fn create_test_mesh() -> (Vec<SkinnedVertex>, Vec<u32>) {
    // Create a simple quad influenced by the Hips bone (Index 1)
    let vertices = vec![
        SkinnedVertex {
            position: [-1.0, -1.0, 0.0],
            normal: [0.0, 0.0, 1.0],
            uv: [0.0, 1.0],
            joint_indices: [1, 0, 0, 0], // Influence by Hips (1)
            joint_weights: [1.0, 0.0, 0.0, 0.0],
        },
        SkinnedVertex {
            position: [1.0, -1.0, 0.0],
            normal: [0.0, 0.0, 1.0],
            uv: [1.0, 1.0],
            joint_indices: [1, 0, 0, 0],
            joint_weights: [1.0, 0.0, 0.0, 0.0],
        },
        SkinnedVertex {
            position: [1.0, 1.0, 0.0],
            normal: [0.0, 0.0, 1.0],
            uv: [1.0, 0.0],
            joint_indices: [1, 0, 0, 0],
            joint_weights: [1.0, 0.0, 0.0, 0.0],
        },
        SkinnedVertex {
            position: [-1.0, 1.0, 0.0],
            normal: [0.0, 0.0, 1.0],
            uv: [0.0, 0.0],
            joint_indices: [1, 0, 0, 0],
            joint_weights: [1.0, 0.0, 0.0, 0.0],
        },
    ];

    let indices = vec![0, 1, 2, 2, 3, 0];
    (vertices, indices)
}

fn main() {
    let event_loop = EventLoop::new().unwrap();
    event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll);
    let mut app = App {
        window: None,
        world: World::new(),
    };
    let _ = event_loop.run_app(&mut app);
}
