use std::collections::HashMap;
use std::fs::File;
use std::io::{BufReader, BufWriter};
use std::path::Path;
#[cfg(feature = "audio")]
use std::str::FromStr;

use freecs::Entity;

use crate::ecs::audio::components::AudioSource;
#[cfg(feature = "audio")]
use crate::ecs::audio::systems::load_sound_from_cursor;
use crate::ecs::bounding_volume::components::{BoundingVolume, OrientedBoundingBox};
use crate::ecs::generational_registry::registry_entry_by_name;
use crate::ecs::lines::components::Lines;
use crate::ecs::material::resources::material_registry_insert;
use crate::ecs::mesh::components::InstancedMesh;
use crate::ecs::prefab::resources::mesh_cache_insert;
use crate::ecs::prefab::{import_gltf_from_path, spawn_prefab};
use crate::ecs::world::Vec3;
use crate::ecs::world::commands::WorldCommand;
use crate::ecs::world::components::{
    CastsShadow, GlobalTransform, LocalTransformDirty, MaterialRef, Name, Parent, RenderMesh,
    Visibility,
};
use crate::ecs::world::{
    ANIMATION_PLAYER, AUDIO_SOURCE, BOUNDING_VOLUME, CAMERA, CASTS_SHADOW, GLOBAL_TRANSFORM,
    INSTANCED_MESH, LIGHT, LINES, LOCAL_TRANSFORM, LOCAL_TRANSFORM_DIRTY, MATERIAL_REF, NAME,
    PARENT, RENDER_MESH, SCRIPT, VISIBILITY, World,
};
#[cfg(feature = "physics")]
use crate::ecs::world::{COLLIDER, PHYSICS_INTERPOLATION, RIGID_BODY};
use crate::render::wgpu::texture_cache::texture_cache_add_reference;

use super::asset_uuid::AssetUuid;
use super::components::{
    Scene, SceneComponents, SceneEntity, SceneHdrSkybox, SceneLight, SceneMaterial,
    ScenePrefabInstance,
};
#[cfg(feature = "physics")]
use super::components::{SceneBodyType, SceneJoint};
use super::registry::AssetRegistry;

#[derive(Debug, thiserror::Error)]
pub enum SceneError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    #[error("JSON error: {0}")]
    Json(#[from] serde_json::Error),

    #[error("Binary error: {0}")]
    Binary(#[from] bincode::Error),

    #[error("Asset not found: {0}")]
    AssetNotFound(String),

    #[error("Invalid scene structure")]
    InvalidStructure,

    #[error("Cyclic parent reference detected")]
    CyclicReference,
}

pub fn save_scene_json(scene: &mut Scene, path: &Path) -> Result<(), SceneError> {
    scene.compute_spawn_order();
    let file = File::create(path)?;
    let writer = BufWriter::new(file);
    serde_json::to_writer_pretty(writer, scene)?;
    Ok(())
}

pub fn load_scene_json(path: &Path) -> Result<Scene, SceneError> {
    let file = File::open(path)?;
    let reader = BufReader::new(file);
    let mut scene: Scene = serde_json::from_reader(reader)?;
    scene.rebuild_uuid_index();
    Ok(scene)
}

pub fn save_scene_binary(scene: &mut Scene, path: &Path) -> Result<(), SceneError> {
    scene.compute_spawn_order();
    let data = bincode::serialize(scene)?;
    let compressed = lz4_flex::compress_prepend_size(&data);
    std::fs::write(path, compressed)?;
    Ok(())
}

pub fn load_scene_binary(path: &Path) -> Result<Scene, SceneError> {
    let compressed = std::fs::read(path)?;
    let data = lz4_flex::decompress_size_prepended(&compressed)
        .map_err(|error| std::io::Error::new(std::io::ErrorKind::InvalidData, error))?;
    let mut scene: Scene = bincode::deserialize(&data)?;
    scene.rebuild_uuid_index();
    Ok(scene)
}

pub fn save_scene(scene: &mut Scene, path: &Path) -> Result<(), SceneError> {
    match path.extension().and_then(|extension| extension.to_str()) {
        Some("bin") => save_scene_binary(scene, path),
        _ => save_scene_json(scene, path),
    }
}

pub fn load_scene(path: &Path) -> Result<Scene, SceneError> {
    match path.extension().and_then(|extension| extension.to_str()) {
        Some("bin") => load_scene_binary(path),
        _ => load_scene_json(path),
    }
}

pub struct SpawnSceneResult {
    pub uuid_to_entity: HashMap<AssetUuid, Entity>,
    pub root_entities: Vec<Entity>,
    pub warnings: Vec<String>,
}

#[cfg(feature = "physics")]
fn scene_joint_to_generic_joint(
    joint: &SceneJoint,
) -> (
    rapier3d::prelude::GenericJoint,
    crate::ecs::physics::resources::JointType,
) {
    use crate::ecs::physics::resources::JointType;
    use rapier3d::prelude::*;

    match joint {
        SceneJoint::Fixed {
            parent_anchor,
            child_anchor,
        } => (
            FixedJointBuilder::new()
                .local_anchor1(point![parent_anchor[0], parent_anchor[1], parent_anchor[2]])
                .local_anchor2(point![child_anchor[0], child_anchor[1], child_anchor[2]])
                .build()
                .into(),
            JointType::Fixed,
        ),
        SceneJoint::Revolute {
            parent_anchor,
            child_anchor,
            axis,
            limits,
        } => {
            let axis_unit = UnitVector::new_normalize(vector![axis[0], axis[1], axis[2]]);
            let mut builder = RevoluteJointBuilder::new(axis_unit)
                .local_anchor1(point![parent_anchor[0], parent_anchor[1], parent_anchor[2]])
                .local_anchor2(point![child_anchor[0], child_anchor[1], child_anchor[2]]);
            if let Some([min, max]) = limits {
                builder = builder.limits([*min, *max]);
            }
            (builder.build().into(), JointType::Revolute)
        }
        SceneJoint::Prismatic {
            parent_anchor,
            child_anchor,
            axis,
            limits,
        } => {
            let axis_unit = UnitVector::new_normalize(vector![axis[0], axis[1], axis[2]]);
            let mut builder = PrismaticJointBuilder::new(axis_unit)
                .local_anchor1(point![parent_anchor[0], parent_anchor[1], parent_anchor[2]])
                .local_anchor2(point![child_anchor[0], child_anchor[1], child_anchor[2]]);
            if let Some([min, max]) = limits {
                builder = builder.limits([*min, *max]);
            }
            (builder.build().into(), JointType::Prismatic)
        }
        SceneJoint::Spherical {
            parent_anchor,
            child_anchor,
        } => (
            SphericalJointBuilder::new()
                .local_anchor1(point![parent_anchor[0], parent_anchor[1], parent_anchor[2]])
                .local_anchor2(point![child_anchor[0], child_anchor[1], child_anchor[2]])
                .build()
                .into(),
            JointType::Spherical,
        ),
        SceneJoint::Rope {
            parent_anchor,
            child_anchor,
            max_distance,
        } => (
            RopeJointBuilder::new(*max_distance)
                .local_anchor1(point![parent_anchor[0], parent_anchor[1], parent_anchor[2]])
                .local_anchor2(point![child_anchor[0], child_anchor[1], child_anchor[2]])
                .build()
                .into(),
            JointType::Rope,
        ),
        SceneJoint::Spring {
            parent_anchor,
            child_anchor,
            rest_length,
            stiffness,
            damping,
        } => (
            SpringJointBuilder::new(*rest_length, *stiffness, *damping)
                .local_anchor1(point![parent_anchor[0], parent_anchor[1], parent_anchor[2]])
                .local_anchor2(point![child_anchor[0], child_anchor[1], child_anchor[2]])
                .build()
                .into(),
            JointType::Spring,
        ),
    }
}

pub fn spawn_scene(
    world: &mut World,
    scene: &Scene,
    asset_registry: Option<&AssetRegistry>,
) -> Result<SpawnSceneResult, SceneError> {
    let mut uuid_to_entity: HashMap<AssetUuid, Entity> = HashMap::new();
    let mut root_entities: Vec<Entity> = Vec::new();
    let mut warnings: Vec<String> = Vec::new();

    world.resources.graphics.atmosphere = scene.atmosphere;

    if let Some(hdr_skybox) = &scene.hdr_skybox {
        match hdr_skybox {
            SceneHdrSkybox::Embedded { data } => {
                world.queue_command(WorldCommand::LoadHdrSkybox {
                    hdr_data: data.clone(),
                });
            }
            SceneHdrSkybox::Reference { path } => {
                world.queue_command(WorldCommand::LoadHdrSkyboxFromPath {
                    path: std::path::PathBuf::from(path),
                });
            }
            SceneHdrSkybox::Asset { uuid } => {
                if let Some(registry) = asset_registry {
                    if let Some(path) = registry.resolve_uuid(*uuid) {
                        world.queue_command(WorldCommand::LoadHdrSkyboxFromPath { path });
                    } else {
                        warnings.push(format!("HDR skybox asset not found: {}", uuid));
                    }
                } else {
                    warnings.push(format!(
                        "Asset registry required to resolve HDR skybox UUID: {}",
                        uuid
                    ));
                }
            }
        }
    }

    for (uuid, embedded_texture) in &scene.embedded_textures {
        if let Some((rgba_data, width, height)) = embedded_texture.to_rgba() {
            world.queue_command(WorldCommand::LoadTexture {
                name: uuid.to_string(),
                rgba_data,
                width,
                height,
            });
        }
    }

    let spawn_order = if !scene.spawn_order.is_empty() {
        scene.spawn_order.clone()
    } else {
        compute_spawn_order(&scene.entities)?
    };

    for scene_entity_uuid in spawn_order {
        let scene_entity = scene
            .find_entity(scene_entity_uuid)
            .ok_or(SceneError::InvalidStructure)?;

        let parent_entity = scene_entity
            .parent
            .and_then(|parent_uuid| uuid_to_entity.get(&parent_uuid).copied());

        let entity = spawn_scene_entity(
            world,
            scene,
            scene_entity,
            parent_entity,
            asset_registry,
            &mut warnings,
        );

        if scene_entity.parent.is_none() {
            root_entities.push(entity);
        }

        uuid_to_entity.insert(scene_entity.uuid, entity);
    }

    if let Some(navmesh) = &scene.navmesh {
        world.resources.navmesh = navmesh.to_navmesh_world();
    }

    #[cfg(feature = "physics")]
    for joint_connection in &scene.joints {
        if let (Some(&parent_entity), Some(&child_entity)) = (
            uuid_to_entity.get(&joint_connection.parent_entity),
            uuid_to_entity.get(&joint_connection.child_entity),
        ) {
            let (generic_joint, joint_type) = scene_joint_to_generic_joint(&joint_connection.joint);
            world.resources.physics.pending_joints.push(
                crate::ecs::physics::resources::PendingJoint {
                    parent_entity,
                    child_entity,
                    joint: generic_joint,
                    joint_type,
                    collisions_enabled: joint_connection.collisions_enabled,
                },
            );
        } else {
            warnings.push(format!(
                "Joint references unknown entities: parent={}, child={}",
                joint_connection.parent_entity, joint_connection.child_entity
            ));
        }
    }

    Ok(SpawnSceneResult {
        uuid_to_entity,
        root_entities,
        warnings,
    })
}

fn compute_spawn_order(entities: &[SceneEntity]) -> Result<Vec<AssetUuid>, SceneError> {
    let uuid_set: std::collections::HashSet<AssetUuid> = entities.iter().map(|e| e.uuid).collect();

    let mut result: Vec<AssetUuid> = Vec::with_capacity(entities.len());
    let mut visited: std::collections::HashSet<AssetUuid> = std::collections::HashSet::new();
    let mut in_stack: std::collections::HashSet<AssetUuid> = std::collections::HashSet::new();

    let entity_map: HashMap<AssetUuid, &SceneEntity> =
        entities.iter().map(|e| (e.uuid, e)).collect();

    fn visit(
        uuid: AssetUuid,
        entity_map: &HashMap<AssetUuid, &SceneEntity>,
        uuid_set: &std::collections::HashSet<AssetUuid>,
        visited: &mut std::collections::HashSet<AssetUuid>,
        in_stack: &mut std::collections::HashSet<AssetUuid>,
        result: &mut Vec<AssetUuid>,
    ) -> Result<(), SceneError> {
        if visited.contains(&uuid) {
            return Ok(());
        }

        if in_stack.contains(&uuid) {
            return Err(SceneError::CyclicReference);
        }

        in_stack.insert(uuid);

        if let Some(entity) = entity_map.get(&uuid)
            && let Some(parent_uuid) = entity.parent
            && uuid_set.contains(&parent_uuid)
        {
            visit(parent_uuid, entity_map, uuid_set, visited, in_stack, result)?;
        }

        in_stack.remove(&uuid);
        visited.insert(uuid);
        result.push(uuid);

        Ok(())
    }

    for entity in entities {
        visit(
            entity.uuid,
            &entity_map,
            &uuid_set,
            &mut visited,
            &mut in_stack,
            &mut result,
        )?;
    }

    Ok(result)
}

fn spawn_scene_entity(
    world: &mut World,
    scene: &Scene,
    scene_entity: &SceneEntity,
    parent_entity: Option<Entity>,
    asset_registry: Option<&AssetRegistry>,
    warnings: &mut Vec<String>,
) -> Entity {
    let mut component_mask = LOCAL_TRANSFORM | GLOBAL_TRANSFORM | LOCAL_TRANSFORM_DIRTY;

    if scene_entity.name.is_some() {
        component_mask |= NAME;
    }
    if parent_entity.is_some() {
        component_mask |= PARENT;
    }
    if scene_entity.components.visible {
        component_mask |= VISIBILITY;
    }

    let entity = world.spawn_entities(component_mask, 1)[0];

    world.set_local_transform(entity, scene_entity.transform);
    world.set_global_transform(entity, GlobalTransform::default());
    world.set_local_transform_dirty(entity, LocalTransformDirty);

    if let Some(name) = &scene_entity.name {
        world.set_name(entity, Name(name.clone()));
    }

    if let Some(parent) = parent_entity {
        world.set_parent(entity, Parent(Some(parent)));
        world
            .resources
            .children_cache
            .entry(parent)
            .or_default()
            .push(entity);
    }

    if scene_entity.components.visible {
        world.set_visibility(entity, Visibility { visible: true });
    }

    apply_scene_components(
        world,
        scene,
        entity,
        &scene_entity.components,
        asset_registry,
        warnings,
    );

    entity
}

fn apply_scene_components(
    world: &mut World,
    scene: &Scene,
    entity: Entity,
    components: &SceneComponents,
    asset_registry: Option<&AssetRegistry>,
    warnings: &mut Vec<String>,
) {
    if let Some(mesh) = &components.mesh {
        apply_mesh_component(world, entity, mesh, asset_registry, warnings);
    }

    if let Some(instanced_mesh) = &components.instanced_mesh {
        apply_instanced_mesh_component(world, entity, instanced_mesh, asset_registry, warnings);
    }

    if !components.lines.is_empty() {
        world.add_components(entity, LINES);
        world.set_lines(
            entity,
            Lines {
                lines: components.lines.clone(),
                version: 0,
            },
        );
    }

    if let Some(light) = &components.light {
        world.add_components(entity, LIGHT);
        world.set_light(entity, light.to_light());
    }

    if let Some(camera) = &components.camera {
        world.add_components(entity, CAMERA);
        world.set_camera(entity, camera.to_camera());
    }

    #[cfg(feature = "physics")]
    if let Some(physics) = &components.physics {
        apply_physics_component(world, entity, physics);
    }

    if let Some(script) = &components.script {
        world.add_components(entity, SCRIPT);
        world.set_script(entity, script.clone());
    }

    if let Some(audio) = &components.audio {
        apply_audio_component(world, scene, entity, audio, asset_registry, warnings);
    }

    if let Some(prefab) = &components.prefab {
        apply_prefab_component(world, entity, prefab, asset_registry, warnings);
    }

    if let Some(bounding_volume) = &components.bounding_volume {
        world.add_components(entity, BOUNDING_VOLUME);
        world.set_bounding_volume(entity, *bounding_volume);
    }

    if let Some(animation_player) = &components.animation_player {
        world.add_components(entity, ANIMATION_PLAYER);
        let player = if animation_player.clip_refs.is_empty() {
            animation_player.to_animation_player()
        } else {
            animation_player.to_animation_player_with_resolved_clips(asset_registry, warnings)
        };
        world.set_animation_player(entity, player);
    }

    if components.casts_shadow {
        world.add_components(entity, CASTS_SHADOW);
        world.set_casts_shadow(entity, CastsShadow);
    }
}

fn apply_mesh_component(
    world: &mut World,
    entity: Entity,
    mesh: &super::components::SceneMesh,
    asset_registry: Option<&AssetRegistry>,
    warnings: &mut Vec<String>,
) {
    let mesh_name = if let Some(uuid) = mesh.mesh_uuid {
        if let Some(registry) = asset_registry {
            if let Some(entry) = registry.get_entry(uuid) {
                entry.name.clone().unwrap_or_else(|| uuid.to_string())
            } else {
                warnings.push(format!("Mesh asset not found: {}", uuid));
                return;
            }
        } else {
            uuid.to_string()
        }
    } else if let Some(name) = &mesh.mesh_name {
        name.clone()
    } else {
        warnings.push("Mesh component has neither UUID nor name".to_string());
        return;
    };

    world.add_components(
        entity,
        RENDER_MESH | MATERIAL_REF | CASTS_SHADOW | BOUNDING_VOLUME,
    );

    let render_mesh = RenderMesh::new(mesh_name.clone());
    world.set_bounding_volume(entity, BoundingVolume::from_mesh_type(&mesh_name));
    if let Some(&index) = world
        .resources
        .mesh_cache
        .registry
        .name_to_index
        .get(&render_mesh.name)
    {
        world.resources.mesh_cache.registry.add_reference(index);
    }
    world.set_render_mesh(entity, render_mesh);
    world.resources.mesh_render_state.mark_entity_added(entity);

    let material_name = if let Some(scene_material) = &mesh.material {
        let mat = scene_material.to_material();
        let name = format!("Scene_{}_{}", mesh_name, entity.id);
        material_registry_insert(&mut world.resources.material_registry, name.clone(), mat);
        name
    } else {
        "Default".to_string()
    };

    if let Some(&index) = world
        .resources
        .material_registry
        .registry
        .name_to_index
        .get(&material_name)
    {
        world
            .resources
            .material_registry
            .registry
            .add_reference(index);
    }

    if let Some(mat) =
        registry_entry_by_name(&world.resources.material_registry.registry, &material_name)
    {
        if let Some(base_texture) = &mat.base_texture {
            texture_cache_add_reference(&mut world.resources.texture_cache, base_texture);
        }
        if let Some(emissive_texture) = &mat.emissive_texture {
            texture_cache_add_reference(&mut world.resources.texture_cache, emissive_texture);
        }
    }

    world.set_material_ref(entity, MaterialRef::new(material_name));
    world.set_casts_shadow(entity, CastsShadow);
}

fn compute_instanced_mesh_bounding_volume(
    mesh_name: &str,
    instances: &[super::components::SceneMeshInstance],
) -> BoundingVolume {
    if instances.is_empty() {
        return BoundingVolume::from_mesh_type(mesh_name);
    }

    let base_bv = BoundingVolume::from_mesh_type(mesh_name);
    let base_obb = &base_bv.obb;

    let mut min_corner = Vec3::new(f32::MAX, f32::MAX, f32::MAX);
    let mut max_corner = Vec3::new(f32::MIN, f32::MIN, f32::MIN);

    for instance in instances {
        let local_transform = instance.to_local_transform();
        let instance_matrix = nalgebra_glm::translation(&local_transform.translation)
            * nalgebra_glm::quat_to_mat4(&local_transform.rotation)
            * nalgebra_glm::scaling(&local_transform.scale);

        let transformed_obb = base_obb.transform(&instance_matrix);
        let corners = transformed_obb.get_corners();

        for corner in &corners {
            min_corner.x = min_corner.x.min(corner.x);
            min_corner.y = min_corner.y.min(corner.y);
            min_corner.z = min_corner.z.min(corner.z);
            max_corner.x = max_corner.x.max(corner.x);
            max_corner.y = max_corner.y.max(corner.y);
            max_corner.z = max_corner.z.max(corner.z);
        }
    }

    let combined_obb = OrientedBoundingBox::from_aabb(min_corner, max_corner);
    let sphere_radius = nalgebra_glm::length(&combined_obb.half_extents);

    BoundingVolume::new(combined_obb, sphere_radius)
}

fn apply_instanced_mesh_component(
    world: &mut World,
    entity: Entity,
    instanced_mesh: &super::components::SceneInstancedMesh,
    asset_registry: Option<&AssetRegistry>,
    warnings: &mut Vec<String>,
) {
    use crate::ecs::mesh::components::{InstanceCustomData, InstanceTransform};

    let mesh_name = if let Some(uuid) = instanced_mesh.mesh_uuid {
        if let Some(registry) = asset_registry {
            if let Some(entry) = registry.get_entry(uuid) {
                entry.name.clone().unwrap_or_else(|| uuid.to_string())
            } else {
                warnings.push(format!("Instanced mesh asset not found: {}", uuid));
                return;
            }
        } else {
            uuid.to_string()
        }
    } else if let Some(name) = &instanced_mesh.mesh_name {
        name.clone()
    } else {
        warnings.push("Instanced mesh component has neither UUID nor name".to_string());
        return;
    };

    world.add_components(
        entity,
        INSTANCED_MESH | MATERIAL_REF | CASTS_SHADOW | BOUNDING_VOLUME,
    );

    let bounding_volume =
        compute_instanced_mesh_bounding_volume(&mesh_name, &instanced_mesh.instances);
    world.set_bounding_volume(entity, bounding_volume);

    let transforms: Vec<InstanceTransform> = instanced_mesh
        .instances
        .iter()
        .map(|i| {
            let local = i.to_local_transform();
            InstanceTransform::new(local.translation, local.rotation, local.scale)
        })
        .collect();

    let mut component = InstancedMesh::with_instances(&mesh_name, transforms);

    for (index, instance) in instanced_mesh.instances.iter().enumerate() {
        if let Some(color) = instance.color {
            component.custom_data[index] = InstanceCustomData { tint: color };
        }
    }

    world.set_instanced_mesh(entity, component);

    let material_name = if let Some(scene_material) = &instanced_mesh.material {
        let mat = scene_material.to_material();
        let name = format!("SceneInstanced_{}_{}", mesh_name, entity.id);
        material_registry_insert(&mut world.resources.material_registry, name.clone(), mat);
        name
    } else {
        "Default".to_string()
    };

    world.set_material_ref(entity, MaterialRef::new(material_name));
    world.set_casts_shadow(entity, CastsShadow);
}

#[cfg(feature = "physics")]
fn apply_physics_component(
    world: &mut World,
    entity: Entity,
    physics: &super::components::ScenePhysics,
) {
    let transform = world
        .get_local_transform(entity)
        .copied()
        .unwrap_or_default();

    let is_dynamic = matches!(physics.body_type, SceneBodyType::Dynamic);

    let mut components = RIGID_BODY | COLLIDER;
    if is_dynamic {
        components |= PHYSICS_INTERPOLATION;
    }
    world.add_components(entity, components);

    let mut rigid_body = physics.to_rigid_body();
    rigid_body = rigid_body.with_translation(
        transform.translation.x,
        transform.translation.y,
        transform.translation.z,
    );
    world.set_rigid_body(entity, rigid_body);

    let collider = physics.to_collider();
    world.set_collider(entity, collider);

    let rigid_body_comp = world.get_rigid_body(entity).cloned().unwrap();
    let collider_comp = world.get_collider(entity).cloned();
    let rapier_body = rigid_body_comp.to_rapier_rigid_body();
    let rapier_handle = world.resources.physics.add_rigid_body(rapier_body);
    if let Some(collider_comp) = collider_comp {
        let rapier_collider = collider_comp.to_rapier_collider();
        world
            .resources
            .physics
            .add_collider(rapier_collider, rapier_handle);
    }
    if let Some(rigid_body_mut) = world.get_rigid_body_mut(entity) {
        rigid_body_mut.handle = Some(rapier_handle.into());
    }
}

fn apply_audio_component(
    world: &mut World,
    scene: &Scene,
    entity: Entity,
    audio: &super::components::SceneAudioSource,
    _asset_registry: Option<&AssetRegistry>,
    warnings: &mut Vec<String>,
) {
    world.add_components(entity, AUDIO_SOURCE);

    let audio_ref = if let Some(name) = &audio.audio_name {
        name.clone()
    } else if let Some(uuid) = audio.audio_uuid {
        uuid.to_string()
    } else {
        warnings.push("Audio component has neither UUID nor name".to_string());
        return;
    };

    #[cfg(feature = "audio")]
    {
        if let Some(embedded_audio) = scene.embedded_audio.get(
            &audio
                .audio_uuid
                .unwrap_or_else(|| AssetUuid::from_str(&audio_ref).unwrap_or(AssetUuid::NIL)),
        ) {
            if let Ok(sound_data) = load_sound_from_cursor(embedded_audio.data.clone()) {
                world
                    .resources
                    .audio
                    .load_sound(audio_ref.clone(), sound_data);
            }
        } else {
            warnings.push(format!("Audio '{}' not found in scene", audio_ref));
        }
    }

    #[cfg(not(feature = "audio"))]
    {
        let _ = scene;
        warnings.push("Audio feature is not enabled - audio source will not play".to_string());
    }

    world.set_audio_source(
        entity,
        AudioSource {
            audio_ref: Some(audio_ref),
            volume: audio.volume,
            looping: audio.looping,
            playing: audio.playing,
            spatial: audio.spatial,
            reverb: audio.reverb,
        },
    );
}

fn apply_prefab_component(
    world: &mut World,
    entity: Entity,
    prefab_instance: &ScenePrefabInstance,
    asset_registry: Option<&AssetRegistry>,
    warnings: &mut Vec<String>,
) {
    let prefab_path = if let Some(uuid) = prefab_instance.prefab_uuid {
        if let Some(registry) = asset_registry {
            registry.resolve_uuid(uuid)
        } else {
            warnings.push(format!(
                "Asset registry required to resolve prefab UUID: {}",
                uuid
            ));
            return;
        }
    } else if let Some(path) = &prefab_instance.prefab_path {
        Some(std::path::PathBuf::from(path))
    } else {
        warnings.push("Prefab instance has neither UUID nor path".to_string());
        return;
    };

    if let Some(path) = prefab_path {
        match import_gltf_from_path(&path) {
            Ok(result) => {
                for (mesh_name, mesh) in result.meshes {
                    mesh_cache_insert(&mut world.resources.mesh_cache, mesh_name, mesh);
                }

                for (texture_name, (rgba_data, width, height)) in result.textures {
                    world.queue_command(WorldCommand::LoadTexture {
                        name: texture_name,
                        rgba_data,
                        width,
                        height,
                    });
                }

                if let Some(prefab) = result.prefabs.into_iter().next() {
                    let transform = world
                        .get_local_transform(entity)
                        .copied()
                        .unwrap_or_default();
                    let prefab_root = spawn_prefab(world, &prefab, transform.translation);

                    world.add_components(prefab_root, PARENT);
                    world.set_parent(prefab_root, Parent(Some(entity)));
                    world
                        .resources
                        .children_cache
                        .entry(entity)
                        .or_default()
                        .push(prefab_root);
                } else {
                    warnings.push(format!("No prefabs found in file '{}'", path.display()));
                }
            }
            Err(error) => {
                warnings.push(format!(
                    "Failed to load prefab from '{}': {}",
                    path.display(),
                    error
                ));
            }
        }
    }
}

pub fn world_to_scene(world: &World, name: impl Into<String>) -> Scene {
    let mut scene = Scene::new(name);
    scene.atmosphere = world.resources.graphics.atmosphere;

    let mut entity_to_uuid: HashMap<Entity, AssetUuid> = HashMap::new();

    let entities: Vec<Entity> = world.query_entities(LOCAL_TRANSFORM).collect();

    for entity in &entities {
        let uuid = AssetUuid::new();
        entity_to_uuid.insert(*entity, uuid);
    }

    for entity in &entities {
        let entity = *entity;
        let uuid = entity_to_uuid[&entity];

        let parent_uuid = world
            .get_parent(entity)
            .and_then(|p| p.0)
            .and_then(|parent_entity| entity_to_uuid.get(&parent_entity).copied());

        let transform = world
            .get_local_transform(entity)
            .copied()
            .unwrap_or_default();

        let name = world.get_name(entity).map(|n| n.0.clone());

        let mut scene_entity = SceneEntity {
            uuid,
            parent: parent_uuid,
            name,
            transform,
            layer: None,
            chunk_id: None,
            components: SceneComponents::new(),
        };

        if let Some(render_mesh) = world.get_render_mesh(entity) {
            let material = world
                .get_material_ref(entity)
                .and_then(|mat_ref| {
                    registry_entry_by_name(
                        &world.resources.material_registry.registry,
                        &mat_ref.name,
                    )
                })
                .map(SceneMaterial::from);

            scene_entity.components.mesh = Some(super::components::SceneMesh {
                mesh_uuid: None,
                mesh_name: Some(render_mesh.name.clone()),
                material,
            });
        }

        if let Some(light) = world.get_light(entity) {
            scene_entity.components.light = Some(SceneLight::from(light));
        }

        if let Some(camera) = world.get_camera(entity) {
            scene_entity.components.camera = Some(super::components::SceneCamera::from(camera));
        }

        #[cfg(feature = "physics")]
        if let Some(rigid_body) = world.get_rigid_body(entity)
            && let Some(collider) = world.get_collider(entity)
        {
            use crate::ecs::physics::types::RigidBodyType;

            let body_type = match rigid_body.body_type {
                RigidBodyType::Fixed => SceneBodyType::Static,
                RigidBodyType::Dynamic => SceneBodyType::Dynamic,
                RigidBodyType::KinematicPositionBased => SceneBodyType::KinematicPositionBased,
                RigidBodyType::KinematicVelocityBased => SceneBodyType::KinematicVelocityBased,
            };

            let scene_collider = match &collider.shape {
                crate::ecs::physics::ColliderShape::Ball { radius } => {
                    super::components::SceneCollider::Ball { radius: *radius }
                }
                crate::ecs::physics::ColliderShape::Cuboid { hx, hy, hz } => {
                    super::components::SceneCollider::Cuboid {
                        half_extents: [*hx, *hy, *hz],
                    }
                }
                crate::ecs::physics::ColliderShape::Cylinder {
                    half_height,
                    radius,
                } => super::components::SceneCollider::Cylinder {
                    half_height: *half_height,
                    radius: *radius,
                },
                crate::ecs::physics::ColliderShape::Capsule {
                    half_height,
                    radius,
                } => super::components::SceneCollider::Capsule {
                    half_height: *half_height,
                    radius: *radius,
                },
                crate::ecs::physics::ColliderShape::TriMesh { vertices, indices } => {
                    super::components::SceneCollider::TriMesh {
                        vertices: vertices.clone(),
                        indices: indices.clone(),
                    }
                }
                crate::ecs::physics::ColliderShape::ConvexMesh { vertices } => {
                    super::components::SceneCollider::ConvexHull {
                        points: vertices.clone(),
                    }
                }
                crate::ecs::physics::ColliderShape::Cone {
                    half_height,
                    radius,
                } => super::components::SceneCollider::Cone {
                    half_height: *half_height,
                    radius: *radius,
                },
                crate::ecs::physics::ColliderShape::HeightField {
                    nrows,
                    ncols,
                    heights,
                    scale,
                } => super::components::SceneCollider::HeightField {
                    nrows: *nrows,
                    ncols: *ncols,
                    heights: heights.clone(),
                    scale: *scale,
                },
            };

            scene_entity.components.physics = Some(super::components::ScenePhysics {
                body_type,
                collider: scene_collider,
                friction: collider.friction,
                restitution: collider.restitution,
                mass: Some(rigid_body.mass),
                is_sensor: collider.is_sensor,
                collision_membership: collider.collision_groups.memberships,
                collision_filter: collider.collision_groups.filter,
                solver_membership: collider.solver_groups.memberships,
                solver_filter: collider.solver_groups.filter,
            });
        }

        if let Some(script) = world.get_script(entity) {
            scene_entity.components.script = Some(script.clone());
        }

        if let Some(audio_source) = world.get_audio_source(entity) {
            scene_entity.components.audio = Some(super::components::SceneAudioSource {
                audio_uuid: None,
                audio_name: audio_source.audio_ref.clone(),
                volume: audio_source.volume,
                looping: audio_source.looping,
                playing: audio_source.playing,
                spatial: audio_source.spatial,
                reverb: audio_source.reverb,
            });
        }

        if let Some(bounding_volume) = world.get_bounding_volume(entity) {
            scene_entity.components.bounding_volume = Some(*bounding_volume);
        }

        if let Some(animation_player) = world.get_animation_player(entity) {
            scene_entity.components.animation_player = Some(
                super::components::SceneAnimationPlayer::from(animation_player),
            );
        }

        scene_entity.components.casts_shadow = world.get_casts_shadow(entity).is_some();

        if let Some(visibility) = world.get_visibility(entity) {
            scene_entity.components.visible = visibility.visible;
        }

        scene.add_entity(scene_entity);
    }

    #[cfg(feature = "physics")]
    {
        use crate::ecs::physics::resources::JointType;

        for (handle, info) in &world.resources.physics.joint_registry {
            let parent_uuid = entity_to_uuid.get(&info.parent_entity).copied();
            let child_uuid = entity_to_uuid.get(&info.child_entity).copied();

            if let (Some(parent_uuid), Some(child_uuid)) = (parent_uuid, child_uuid)
                && let Some(rapier_joint) = world.resources.physics.impulse_joint_set.get(*handle)
            {
                let anchor1 = rapier_joint.data.local_anchor1();
                let anchor2 = rapier_joint.data.local_anchor2();
                let parent_anchor = [anchor1.x, anchor1.y, anchor1.z];
                let child_anchor = [anchor2.x, anchor2.y, anchor2.z];

                let scene_joint = match info.joint_type {
                    JointType::Fixed => SceneJoint::Fixed {
                        parent_anchor,
                        child_anchor,
                    },
                    JointType::Revolute => {
                        let axis = rapier_joint.data.local_axis1();
                        SceneJoint::Revolute {
                            parent_anchor,
                            child_anchor,
                            axis: [axis.x, axis.y, axis.z],
                            limits: rapier_joint
                                .data
                                .limits(rapier3d::prelude::JointAxis::AngX)
                                .map(|l| [l.min, l.max]),
                        }
                    }
                    JointType::Prismatic => {
                        let axis = rapier_joint.data.local_axis1();
                        SceneJoint::Prismatic {
                            parent_anchor,
                            child_anchor,
                            axis: [axis.x, axis.y, axis.z],
                            limits: rapier_joint
                                .data
                                .limits(rapier3d::prelude::JointAxis::LinX)
                                .map(|l| [l.min, l.max]),
                        }
                    }
                    JointType::Spherical => SceneJoint::Spherical {
                        parent_anchor,
                        child_anchor,
                    },
                    JointType::Rope => SceneJoint::Rope {
                        parent_anchor,
                        child_anchor,
                        max_distance: rapier_joint
                            .data
                            .limits(rapier3d::prelude::JointAxis::LinX)
                            .map(|l| l.max)
                            .unwrap_or(1.0),
                    },
                    JointType::Spring => SceneJoint::Spring {
                        parent_anchor,
                        child_anchor,
                        rest_length: 1.0,
                        stiffness: 1.0,
                        damping: 0.1,
                    },
                };

                scene.joints.push(super::components::SceneJointConnection {
                    parent_entity: parent_uuid,
                    child_entity: child_uuid,
                    joint: scene_joint,
                    collisions_enabled: info.collisions_enabled,
                });
            }
        }
    }

    if !world.resources.navmesh.triangles.is_empty() {
        scene.navmesh = Some(super::components::SceneNavMesh::from_navmesh_world(
            &world.resources.navmesh,
        ));
    }

    scene
}
