use crate::ecs::components::{JointPalette, SkinnedMesh};
use archetype_ecs::prelude::{Entity, World};
use archetype_ecs::system::{System, SystemAccess};
use ash_renderer::Renderer;
use std::any::TypeId;

/// System that uploads joint matrices to the renderer's global SSBO
pub struct JointUploadSystem;

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

impl System for JointUploadSystem {
    fn name(&self) -> &'static str {
        "JointUploadSystem"
    }

    fn access(&self) -> SystemAccess {
        let mut access = SystemAccess::empty();
        access
            .reads
            .push(TypeId::of::<crate::ecs::components::JointPalette>());
        access
            .writes
            .push(TypeId::of::<crate::ecs::components::SkinnedMesh>());
        access.reads.push(TypeId::of::<Arc<Mutex<Renderer>>>());
        access
    }

    fn run(&mut self, world: &mut World) -> archetype_ecs::Result<()> {
        // 1. Collect all matrices
        // We scope this to drop the query borrow before getting renderer
        let entries: Vec<_> = world
            .query_mut::<(Entity, &mut SkinnedMesh, &JointPalette)>()
            .into_iter()
            .map(|(e, mesh, palette)| (e, mesh, palette.clone()))
            .collect();

        if entries.is_empty() {
            return Ok(());
        }

        let mut all_joints = Vec::new();
        let mut updates = Vec::new();

        for (entity, _, palette) in entries {
            let offset = all_joints.len() as u32;
            all_joints.extend_from_slice(&palette.matrices);
            updates.push((entity, offset));
        }

        // 2. Upload to GPU
        {
            let renderer_res = match world.resource::<Arc<Mutex<Renderer>>>() {
                Some(r) => r,
                None => return Ok(()), // Renderer not present, skip upload
            };

            if let Ok(mut renderer) = renderer_res.lock() {
                // SAFETY: We are providing a valid slice of matrices.
                unsafe {
                    if let Err(e) = renderer.update_joint_ssbo(&all_joints) {
                        eprintln!("Renderer error: {e}");
                    }
                }
            }
        }

        // 3. Update component offsets
        for (entity, offset) in updates {
            if let Some(mesh) = world.get_component_mut::<SkinnedMesh>(entity) {
                mesh.joint_offset = offset;
            }
        }

        Ok(())
    }
}
