//! 3D transformation gizmos for editor-style entity manipulation.
//!
//! Provides visual handles for translating, rotating, and scaling entities interactively:
//!
//! - [`GizmoState`]: Active gizmo instance tracking target entity and mode
//! - [`GizmoMode`]: Translation (local/global), rotation, or scale mode
//! - [`GizmoConfig`]: Visual appearance settings (sizes, colors)
//! - `create_translation_gizmo`: Spawn translation arrows
//! - `create_rotation_gizmo`: Spawn rotation rings
//! - `create_scale_gizmo`: Spawn scale cubes
//! - `update_gizmo_transforms`: Sync gizmo position to target
//! - `gizmo_drag_system`: Handle mouse interaction
//!
//! # Creating a Gizmo
//!
//! ```ignore
//! // Create translation gizmo attached to an entity
//! let gizmo = create_translation_gizmo(world, selected_entity, GizmoMode::LocalTranslation);
//! self.active_gizmo = Some(gizmo);
//!
//! // Switch to rotation mode
//! let gizmo = create_rotation_gizmo(world, selected_entity);
//! self.active_gizmo = Some(gizmo);
//!
//! // Switch to scale mode
//! let gizmo = create_scale_gizmo(world, selected_entity);
//! self.active_gizmo = Some(gizmo);
//! ```
//!
//! # Updating Gizmo Each Frame
//!
//! ```ignore
//! fn run_systems(&mut self, world: &mut World) {
//!     if let Some(gizmo) = &self.active_gizmo {
//!         // Keep gizmo positioned at target entity
//!         update_gizmo_transforms(world, gizmo);
//!
//!         // Scale gizmo based on camera distance (constant screen size)
//!         scale_gizmo_for_camera(world, gizmo);
//!
//!         // Handle mouse dragging
//!         gizmo_drag_system(world, gizmo, &mut self.drag_state);
//!     }
//! }
//! ```
//!
//! # Gizmo Modes
//!
//! | Mode | Description |
//! |------|-------------|
//! | `LocalTranslation` | Move along entity's local axes |
//! | `GlobalTranslation` | Move along world axes |
//! | `Rotation` | Rotate around axes (ring handles) |
//! | `Scale` | Scale along axes (cube handles) |
//!
//! # Mouse Interaction
//!
//! ```ignore
//! fn on_mouse_input(&mut self, world: &mut World, button: MouseButton, state: KeyState) {
//!     if button == MouseButton::Left {
//!         if state == KeyState::Pressed {
//!             // Check if mouse is over a gizmo axis
//!             if let Some(axis) = pick_gizmo_axis(world, &self.gizmo) {
//!                 self.drag_state = Some(GizmoDragState::new(axis));
//!             }
//!         } else {
//!             self.drag_state = None;
//!         }
//!     }
//! }
//! ```
//!
//! # Removing Gizmo
//!
//! ```ignore
//! if let Some(gizmo) = self.active_gizmo.take() {
//!     destroy_gizmo(world, &gizmo);
//! }
//! ```
//!
//! # Gizmo Configuration
//!
//! ```ignore
//! let config = GizmoConfig {
//!     arrow_length: 2.0,      // Length of translation arrows
//!     arrow_radius: 0.06,     // Thickness of arrow shafts
//!     cone_height: 0.5,       // Arrow head height
//!     cone_radius: 0.15,      // Arrow head radius
//!     scale_factor: 0.08,     // Screen-size scaling factor
//!     ..Default::default()
//! };
//! ```
//!
//! # Axis Colors
//!
//! | Axis | Color |
//! |------|-------|
//! | X | Red |
//! | Y | Green |
//! | Z | Blue |
//! | XY Plane | Yellow |
//! | YZ Plane | Cyan |
//! | XZ Plane | Magenta |

use crate::ecs::generational_registry::registry_entry_by_name_mut;
use crate::ecs::material::resources::material_registry_insert;
use crate::prelude::*;

#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum GizmoMode {
    #[default]
    LocalTranslation,
    GlobalTranslation,
    Rotation,
    Scale,
}

pub struct GizmoState {
    pub root: Entity,
    pub target: Entity,
    pub mode: GizmoMode,
    pub axis_entities: GizmoAxisEntities,
}

#[derive(Debug, Clone, Default)]
pub struct GizmoAxisEntities {
    pub x_axis: Option<Entity>,
    pub y_axis: Option<Entity>,
    pub z_axis: Option<Entity>,
    pub center_cube: Option<Entity>,
    pub xy_plane: Option<Entity>,
    pub yz_plane: Option<Entity>,
    pub xz_plane: Option<Entity>,
    pub x_axis_line: Option<Entity>,
    pub y_axis_line: Option<Entity>,
    pub z_axis_line: Option<Entity>,
}

#[derive(Debug, Clone, Copy)]
pub struct GizmoConfig {
    pub arrow_length: f32,
    pub arrow_radius: f32,
    pub cone_height: f32,
    pub cone_radius: f32,
    pub highlight_factor: f32,
    pub normal_factor: f32,
    pub scale_factor: f32,
    pub min_scale: f32,
    pub max_scale: f32,
}

impl Default for GizmoConfig {
    fn default() -> Self {
        Self {
            arrow_length: 2.0,
            arrow_radius: 0.06,
            cone_height: 0.5,
            cone_radius: 0.15,
            highlight_factor: 1.0,
            normal_factor: 1.0,
            scale_factor: 0.08,
            min_scale: 0.3,
            max_scale: 50.0,
        }
    }
}

fn set_gizmo_layer(world: &mut World, entity: Entity) {
    if let Some(render_layer) = world.get_render_layer_mut(entity) {
        render_layer.0 = RenderLayer::OVERLAY;
    }
}

struct AxisArrowParams {
    parent: Entity,
    direction: Vec3,
    color: Vec3,
    length: f32,
    cylinder_radius: f32,
    cone_height: f32,
    cone_radius: f32,
    axis_name: String,
}

struct AxisWithCubeParams {
    parent: Entity,
    direction: Vec3,
    color: Vec3,
    length: f32,
    cylinder_radius: f32,
    cube_size: f32,
    axis_name: String,
}

struct RotationRingParams {
    parent: Entity,
    axis: Vec3,
    color: Vec3,
    ring_radius: f32,
    axis_name: String,
}

pub fn create_translation_gizmo(
    world: &mut World,
    target_entity: Entity,
    mode: GizmoMode,
) -> GizmoState {
    let config = GizmoConfig::default();
    let gizmo_root = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | NAME
            | VISIBILITY
            | RENDER_LAYER,
        1,
    )[0];

    let name_str = match mode {
        GizmoMode::LocalTranslation => "Local Translation Gizmo",
        GizmoMode::GlobalTranslation => "Global Translation Gizmo",
        _ => "Translation Gizmo",
    };

    if let Some(name) = world.get_name_mut(gizmo_root) {
        name.0 = name_str.to_string();
    }

    if let Some(transform) = world.get_local_transform(gizmo_root) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.scale = nalgebra_glm::vec3(1.0, 1.0, 1.0);
        world.assign_local_transform(gizmo_root, new_transform);
    }

    let mut axis_entities = GizmoAxisEntities::default();

    let arrow_length = config.arrow_length;
    let arrow_radius = config.arrow_radius;
    let cone_height = config.cone_height;
    let cone_radius = config.cone_radius;

    axis_entities.x_axis = Some(create_axis_arrow(
        world,
        AxisArrowParams {
            parent: gizmo_root,
            direction: nalgebra_glm::vec3(1.0, 0.0, 0.0),
            color: nalgebra_glm::vec3(0.7, 0.0, 0.0),
            length: arrow_length,
            cylinder_radius: arrow_radius,
            cone_height,
            cone_radius,
            axis_name: "X Axis".to_string(),
        },
    ));

    axis_entities.y_axis = Some(create_axis_arrow(
        world,
        AxisArrowParams {
            parent: gizmo_root,
            direction: nalgebra_glm::vec3(0.0, 1.0, 0.0),
            color: nalgebra_glm::vec3(0.0, 0.7, 0.0),
            length: arrow_length,
            cylinder_radius: arrow_radius,
            cone_height,
            cone_radius,
            axis_name: "Y Axis".to_string(),
        },
    ));

    axis_entities.z_axis = Some(create_axis_arrow(
        world,
        AxisArrowParams {
            parent: gizmo_root,
            direction: nalgebra_glm::vec3(0.0, 0.0, 1.0),
            color: nalgebra_glm::vec3(0.0, 0.0, 0.7),
            length: arrow_length,
            cylinder_radius: arrow_radius,
            cone_height,
            cone_radius,
            axis_name: "Z Axis".to_string(),
        },
    ));

    let center_cube = create_center_cube(world, gizmo_root);
    axis_entities.center_cube = Some(center_cube);

    axis_entities.xy_plane = Some(create_plane_cube(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 0.0, 1.0),
        nalgebra_glm::vec3(0.4, 0.4, 0.0),
        arrow_length * 0.3,
        "XY Plane",
    ));

    axis_entities.yz_plane = Some(create_plane_cube(
        world,
        gizmo_root,
        nalgebra_glm::vec3(1.0, 0.0, 0.0),
        nalgebra_glm::vec3(0.0, 0.4, 0.4),
        arrow_length * 0.3,
        "YZ Plane",
    ));

    axis_entities.xz_plane = Some(create_plane_cube(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 1.0, 0.0),
        nalgebra_glm::vec3(0.4, 0.0, 0.4),
        arrow_length * 0.3,
        "XZ Plane",
    ));

    axis_entities.x_axis_line = Some(create_infinite_axis_line(
        world,
        gizmo_root,
        nalgebra_glm::vec3(1.0, 0.0, 0.0),
        nalgebra_glm::vec3(0.7, 0.0, 0.0),
        "X Axis Line",
    ));

    axis_entities.y_axis_line = Some(create_infinite_axis_line(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 1.0, 0.0),
        nalgebra_glm::vec3(0.0, 0.7, 0.0),
        "Y Axis Line",
    ));

    axis_entities.z_axis_line = Some(create_infinite_axis_line(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 0.0, 1.0),
        nalgebra_glm::vec3(0.0, 0.0, 0.7),
        "Z Axis Line",
    ));

    GizmoState {
        root: gizmo_root,
        target: target_entity,
        mode,
        axis_entities,
    }
}

pub fn create_scale_gizmo(world: &mut World, target_entity: Entity) -> GizmoState {
    let config = GizmoConfig::default();
    let gizmo_root = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | NAME
            | VISIBILITY
            | RENDER_LAYER,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(gizmo_root) {
        name.0 = "Scale Gizmo".to_string();
    }

    if let Some(transform) = world.get_local_transform(gizmo_root) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.scale = nalgebra_glm::vec3(1.0, 1.0, 1.0);
        world.assign_local_transform(gizmo_root, new_transform);
    }

    let mut axis_entities = GizmoAxisEntities::default();

    let arrow_length = config.arrow_length;
    let arrow_radius = config.arrow_radius;
    let cube_size = config.cone_height * 0.5;

    axis_entities.x_axis = Some(create_axis_with_cube(
        world,
        AxisWithCubeParams {
            parent: gizmo_root,
            direction: nalgebra_glm::vec3(1.0, 0.0, 0.0),
            color: nalgebra_glm::vec3(0.7, 0.0, 0.0),
            length: arrow_length,
            cylinder_radius: arrow_radius,
            cube_size,
            axis_name: "X Axis".to_string(),
        },
    ));

    axis_entities.y_axis = Some(create_axis_with_cube(
        world,
        AxisWithCubeParams {
            parent: gizmo_root,
            direction: nalgebra_glm::vec3(0.0, 1.0, 0.0),
            color: nalgebra_glm::vec3(0.0, 0.7, 0.0),
            length: arrow_length,
            cylinder_radius: arrow_radius,
            cube_size,
            axis_name: "Y Axis".to_string(),
        },
    ));

    axis_entities.z_axis = Some(create_axis_with_cube(
        world,
        AxisWithCubeParams {
            parent: gizmo_root,
            direction: nalgebra_glm::vec3(0.0, 0.0, 1.0),
            color: nalgebra_glm::vec3(0.0, 0.0, 0.7),
            length: arrow_length,
            cylinder_radius: arrow_radius,
            cube_size,
            axis_name: "Z Axis".to_string(),
        },
    ));

    let center_cube = create_center_cube(world, gizmo_root);
    axis_entities.center_cube = Some(center_cube);

    axis_entities.xy_plane = Some(create_plane_cube(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 0.0, 1.0),
        nalgebra_glm::vec3(0.4, 0.4, 0.0),
        arrow_length * 0.3,
        "XY Plane",
    ));

    axis_entities.yz_plane = Some(create_plane_cube(
        world,
        gizmo_root,
        nalgebra_glm::vec3(1.0, 0.0, 0.0),
        nalgebra_glm::vec3(0.0, 0.4, 0.4),
        arrow_length * 0.3,
        "YZ Plane",
    ));

    axis_entities.xz_plane = Some(create_plane_cube(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 1.0, 0.0),
        nalgebra_glm::vec3(0.4, 0.0, 0.4),
        arrow_length * 0.3,
        "XZ Plane",
    ));

    axis_entities.x_axis_line = Some(create_infinite_axis_line(
        world,
        gizmo_root,
        nalgebra_glm::vec3(1.0, 0.0, 0.0),
        nalgebra_glm::vec3(0.7, 0.0, 0.0),
        "X Axis Line",
    ));

    axis_entities.y_axis_line = Some(create_infinite_axis_line(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 1.0, 0.0),
        nalgebra_glm::vec3(0.0, 0.7, 0.0),
        "Y Axis Line",
    ));

    axis_entities.z_axis_line = Some(create_infinite_axis_line(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 0.0, 1.0),
        nalgebra_glm::vec3(0.0, 0.0, 0.7),
        "Z Axis Line",
    ));

    GizmoState {
        root: gizmo_root,
        target: target_entity,
        mode: GizmoMode::Scale,
        axis_entities,
    }
}

pub fn create_rotation_gizmo(world: &mut World, target_entity: Entity) -> GizmoState {
    let gizmo_root = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | NAME
            | VISIBILITY
            | RENDER_LAYER,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(gizmo_root) {
        name.0 = "Rotation Gizmo".to_string();
    }

    set_gizmo_layer(world, gizmo_root);

    if let Some(transform) = world.get_local_transform(gizmo_root) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.rotation = Quat::identity();
        new_transform.scale = nalgebra_glm::vec3(1.0, 1.0, 1.0);
        world.assign_local_transform(gizmo_root, new_transform);
    }

    if let Some(visibility) = world.get_visibility_mut(gizmo_root) {
        visibility.visible = true;
    }

    let ring_radius = 2.0;

    let axis_entities = GizmoAxisEntities {
        x_axis: Some(create_rotation_ring(
            world,
            RotationRingParams {
                parent: gizmo_root,
                axis: nalgebra_glm::vec3(1.0, 0.0, 0.0),
                color: nalgebra_glm::vec3(0.9, 0.2, 0.2),
                ring_radius,
                axis_name: "X Rotation Ring".to_string(),
            },
        )),
        y_axis: Some(create_rotation_ring(
            world,
            RotationRingParams {
                parent: gizmo_root,
                axis: nalgebra_glm::vec3(0.0, 1.0, 0.0),
                color: nalgebra_glm::vec3(0.2, 0.9, 0.2),
                ring_radius,
                axis_name: "Y Rotation Ring".to_string(),
            },
        )),
        z_axis: Some(create_rotation_ring(
            world,
            RotationRingParams {
                parent: gizmo_root,
                axis: nalgebra_glm::vec3(0.0, 0.0, 1.0),
                color: nalgebra_glm::vec3(0.2, 0.2, 0.9),
                ring_radius,
                axis_name: "Z Rotation Ring".to_string(),
            },
        )),
        x_axis_line: Some(create_infinite_axis_line(
            world,
            gizmo_root,
            nalgebra_glm::vec3(1.0, 0.0, 0.0),
            nalgebra_glm::vec3(0.9, 0.2, 0.2),
            "X Axis Line",
        )),
        y_axis_line: Some(create_infinite_axis_line(
            world,
            gizmo_root,
            nalgebra_glm::vec3(0.0, 1.0, 0.0),
            nalgebra_glm::vec3(0.2, 0.9, 0.2),
            "Y Axis Line",
        )),
        z_axis_line: Some(create_infinite_axis_line(
            world,
            gizmo_root,
            nalgebra_glm::vec3(0.0, 0.0, 1.0),
            nalgebra_glm::vec3(0.2, 0.2, 0.9),
            "Z Axis Line",
        )),
        ..Default::default()
    };

    GizmoState {
        root: gizmo_root,
        target: target_entity,
        mode: GizmoMode::Rotation,
        axis_entities,
    }
}

fn create_axis_arrow(world: &mut World, params: AxisArrowParams) -> Entity {
    let axis_group = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | PARENT
            | NAME
            | VISIBILITY
            | RENDER_LAYER
            | BOUNDING_VOLUME,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(axis_group) {
        name.0 = params.axis_name.clone();
    }

    world.update_parent(axis_group, Some(Parent(Some(params.parent))));

    if let Some(transform) = world.get_local_transform(axis_group) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.rotation = Quat::identity();
        new_transform.scale = nalgebra_glm::vec3(1.0, 1.0, 1.0);
        world.assign_local_transform(axis_group, new_transform);
    }

    if let Some(visibility) = world.get_visibility_mut(axis_group) {
        visibility.visible = true;
    }

    set_gizmo_layer(world, axis_group);

    let rotation = if params.direction.y.abs() > 0.99 {
        Quat::identity()
    } else if params.direction.x.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if params.direction.x > 0.0 {
                -std::f32::consts::PI / 2.0
            } else {
                std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(0.0, 0.0, 1.0),
        )
    } else if params.direction.z.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if params.direction.z > 0.0 {
                std::f32::consts::PI / 2.0
            } else {
                -std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(1.0, 0.0, 0.0),
        )
    } else {
        Quat::identity()
    };

    let cylinder_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL_REF
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER,
        1,
    )[0];
    world
        .resources
        .mesh_render_state
        .mark_entity_added(cylinder_entity);

    if let Some(name) = world.get_name_mut(cylinder_entity) {
        name.0 = format!("{} Cylinder", params.axis_name);
    }

    set_gizmo_layer(world, cylinder_entity);

    world.update_parent(cylinder_entity, Some(Parent(Some(axis_group))));

    if let Some(transform) = world.get_local_transform(cylinder_entity) {
        let mut new_transform = *transform;
        new_transform.translation = params.direction * (params.length * 0.5);
        new_transform.rotation = rotation;
        new_transform.scale = nalgebra_glm::vec3(
            params.cylinder_radius / 0.5,
            params.length / 1.0,
            params.cylinder_radius / 0.5,
        );
        world.assign_local_transform(cylinder_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(cylinder_entity) {
        mesh.name = "Cylinder".to_string();
    }

    let cylinder_material_name = format!("Gizmo_{}_Cylinder", cylinder_entity.id);
    material_registry_insert(
        &mut world.resources.material_registry,
        cylinder_material_name.clone(),
        Material {
            base_color: [params.color.x, params.color.y, params.color.z, 1.0],
            unlit: true,
            alpha_mode: AlphaMode::Opaque,
            ..Default::default()
        },
    );
    if let Some(&index) = world
        .resources
        .material_registry
        .registry
        .name_to_index
        .get(&cylinder_material_name)
    {
        world
            .resources
            .material_registry
            .registry
            .add_reference(index);
    }
    world.set_material_ref(cylinder_entity, MaterialRef::new(cylinder_material_name));

    let cone_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL_REF
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER,
        1,
    )[0];
    world
        .resources
        .mesh_render_state
        .mark_entity_added(cone_entity);

    if let Some(name) = world.get_name_mut(cone_entity) {
        name.0 = format!("{} Cone", params.axis_name);
    }

    set_gizmo_layer(world, cone_entity);

    world.update_parent(cone_entity, Some(Parent(Some(axis_group))));

    if let Some(transform) = world.get_local_transform(cone_entity) {
        let mut new_transform = *transform;
        let cone_scale_radius = 3.0;
        let cone_scale_height = 2.0;
        let scaled_cone_height = params.cone_height * cone_scale_height;
        new_transform.translation = params.direction * (params.length + scaled_cone_height / 4.0);
        new_transform.rotation = rotation;
        new_transform.scale = nalgebra_glm::vec3(
            params.cone_radius / 1.0 * cone_scale_radius,
            params.cone_height / 2.0 * cone_scale_height,
            params.cone_radius / 1.0 * cone_scale_radius,
        );
        world.assign_local_transform(cone_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(cone_entity) {
        mesh.name = "Cone".to_string();
    }

    let cone_material_name = format!("Gizmo_{}_Cone", cone_entity.id);
    material_registry_insert(
        &mut world.resources.material_registry,
        cone_material_name.clone(),
        Material {
            base_color: [params.color.x, params.color.y, params.color.z, 1.0],
            unlit: true,
            alpha_mode: AlphaMode::Opaque,
            ..Default::default()
        },
    );
    if let Some(&index) = world
        .resources
        .material_registry
        .registry
        .name_to_index
        .get(&cone_material_name)
    {
        world
            .resources
            .material_registry
            .registry
            .add_reference(index);
    }
    world.set_material_ref(cone_entity, MaterialRef::new(cone_material_name));

    if let Some(bounding) = world.get_bounding_volume_mut(axis_group) {
        let total_length = params.length + params.cone_height;
        let click_radius = 0.15;

        let center = params.direction * (total_length * 0.5);

        bounding.obb.center = center;

        if params.direction.y.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(click_radius, total_length * 0.5, click_radius);
            bounding.obb.orientation = Quat::identity();
        } else if params.direction.x.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(total_length * 0.5, click_radius, click_radius);
            bounding.obb.orientation = Quat::identity();
        } else if params.direction.z.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(click_radius, click_radius, total_length * 0.5);
            bounding.obb.orientation = Quat::identity();
        }

        bounding.sphere_radius =
            (click_radius * click_radius + (total_length * 0.5) * (total_length * 0.5)).sqrt();
    }

    axis_group
}

fn create_center_cube(world: &mut World, parent: Entity) -> Entity {
    let center_cube = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL_REF
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER
            | BOUNDING_VOLUME,
        1,
    )[0];
    world
        .resources
        .mesh_render_state
        .mark_entity_added(center_cube);

    if let Some(name) = world.get_name_mut(center_cube) {
        name.0 = "Center Cube".to_string();
    }

    set_gizmo_layer(world, center_cube);

    world.update_parent(center_cube, Some(Parent(Some(parent))));

    if let Some(transform) = world.get_local_transform(center_cube) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.rotation = Quat::identity();
        new_transform.scale = nalgebra_glm::vec3(0.15, 0.15, 0.15);
        world.assign_local_transform(center_cube, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(center_cube) {
        mesh.name = "Cube".to_string();
    }

    let material_name = format!("Gizmo_{}_CenterCube", center_cube.id);
    material_registry_insert(
        &mut world.resources.material_registry,
        material_name.clone(),
        Material {
            base_color: [0.6, 0.6, 0.6, 1.0],
            unlit: true,
            alpha_mode: AlphaMode::Opaque,
            ..Default::default()
        },
    );
    if let Some(&index) = world
        .resources
        .material_registry
        .registry
        .name_to_index
        .get(&material_name)
    {
        world
            .resources
            .material_registry
            .registry
            .add_reference(index);
    }
    world.set_material_ref(center_cube, MaterialRef::new(material_name));

    if let Some(visibility) = world.get_visibility_mut(center_cube) {
        visibility.visible = true;
    }

    if let Some(bounding) = world.get_bounding_volume_mut(center_cube) {
        let click_size = 0.25;
        bounding.sphere_radius = click_size * 0.866;
        bounding.obb.center = Vec3::zeros();
        bounding.obb.half_extents =
            nalgebra_glm::vec3(click_size * 0.5, click_size * 0.5, click_size * 0.5);
        bounding.obb.orientation = Quat::identity();
    }

    center_cube
}

fn create_axis_with_cube(world: &mut World, params: AxisWithCubeParams) -> Entity {
    let axis_group = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | PARENT
            | NAME
            | VISIBILITY
            | RENDER_LAYER
            | BOUNDING_VOLUME,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(axis_group) {
        name.0 = params.axis_name.clone();
    }

    world.update_parent(axis_group, Some(Parent(Some(params.parent))));

    if let Some(transform) = world.get_local_transform(axis_group) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.rotation = Quat::identity();
        new_transform.scale = nalgebra_glm::vec3(1.0, 1.0, 1.0);
        world.assign_local_transform(axis_group, new_transform);
    }

    if let Some(visibility) = world.get_visibility_mut(axis_group) {
        visibility.visible = true;
    }

    set_gizmo_layer(world, axis_group);

    let rotation = if params.direction.y.abs() > 0.99 {
        Quat::identity()
    } else if params.direction.x.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if params.direction.x > 0.0 {
                -std::f32::consts::PI / 2.0
            } else {
                std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(0.0, 0.0, 1.0),
        )
    } else if params.direction.z.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if params.direction.z > 0.0 {
                std::f32::consts::PI / 2.0
            } else {
                -std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(1.0, 0.0, 0.0),
        )
    } else {
        Quat::identity()
    };

    let cylinder_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL_REF
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER,
        1,
    )[0];
    world
        .resources
        .mesh_render_state
        .mark_entity_added(cylinder_entity);

    if let Some(name) = world.get_name_mut(cylinder_entity) {
        name.0 = format!("{} Cylinder", params.axis_name);
    }

    set_gizmo_layer(world, cylinder_entity);

    world.update_parent(cylinder_entity, Some(Parent(Some(axis_group))));

    if let Some(transform) = world.get_local_transform(cylinder_entity) {
        let mut new_transform = *transform;
        new_transform.translation = params.direction * (params.length * 0.5);
        new_transform.rotation = rotation;
        new_transform.scale = nalgebra_glm::vec3(
            params.cylinder_radius / 0.5,
            params.length / 1.0,
            params.cylinder_radius / 0.5,
        );
        world.assign_local_transform(cylinder_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(cylinder_entity) {
        mesh.name = "Cylinder".to_string();
    }

    let cylinder_material_name = format!("Gizmo_{}_ScaleCylinder", cylinder_entity.id);
    material_registry_insert(
        &mut world.resources.material_registry,
        cylinder_material_name.clone(),
        Material {
            base_color: [params.color.x, params.color.y, params.color.z, 1.0],
            unlit: true,
            alpha_mode: AlphaMode::Opaque,
            ..Default::default()
        },
    );
    if let Some(&index) = world
        .resources
        .material_registry
        .registry
        .name_to_index
        .get(&cylinder_material_name)
    {
        world
            .resources
            .material_registry
            .registry
            .add_reference(index);
    }
    world.set_material_ref(cylinder_entity, MaterialRef::new(cylinder_material_name));

    let cube_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL_REF
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER,
        1,
    )[0];
    world
        .resources
        .mesh_render_state
        .mark_entity_added(cube_entity);

    if let Some(name) = world.get_name_mut(cube_entity) {
        name.0 = format!("{} Cube", params.axis_name);
    }

    set_gizmo_layer(world, cube_entity);

    world.update_parent(cube_entity, Some(Parent(Some(axis_group))));

    if let Some(transform) = world.get_local_transform(cube_entity) {
        let mut new_transform = *transform;
        new_transform.translation = params.direction * params.length;
        new_transform.rotation = Quat::identity();
        new_transform.scale =
            nalgebra_glm::vec3(params.cube_size, params.cube_size, params.cube_size);
        world.assign_local_transform(cube_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(cube_entity) {
        mesh.name = "Cube".to_string();
    }

    let cube_material_name = format!("Gizmo_{}_ScaleCube", cube_entity.id);
    material_registry_insert(
        &mut world.resources.material_registry,
        cube_material_name.clone(),
        Material {
            base_color: [params.color.x, params.color.y, params.color.z, 1.0],
            unlit: true,
            alpha_mode: AlphaMode::Opaque,
            ..Default::default()
        },
    );
    if let Some(&index) = world
        .resources
        .material_registry
        .registry
        .name_to_index
        .get(&cube_material_name)
    {
        world
            .resources
            .material_registry
            .registry
            .add_reference(index);
    }
    world.set_material_ref(cube_entity, MaterialRef::new(cube_material_name));

    if let Some(bounding) = world.get_bounding_volume_mut(axis_group) {
        let total_length = params.length + params.cube_size * 0.5;
        let click_radius = 0.15;

        let center = params.direction * (total_length * 0.5);
        bounding.obb.center = center;

        if params.direction.y.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(click_radius, total_length * 0.5, click_radius);
            bounding.obb.orientation = Quat::identity();
        } else if params.direction.x.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(total_length * 0.5, click_radius, click_radius);
            bounding.obb.orientation = Quat::identity();
        } else if params.direction.z.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(click_radius, click_radius, total_length * 0.5);
            bounding.obb.orientation = Quat::identity();
        }

        bounding.sphere_radius =
            (click_radius * click_radius + (total_length * 0.5) * (total_length * 0.5)).sqrt();
    }

    axis_group
}

fn create_rotation_ring(world: &mut World, params: RotationRingParams) -> Entity {
    let ring_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | PARENT
            | NAME
            | VISIBILITY
            | RENDER_LAYER
            | BOUNDING_VOLUME
            | LINES,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(ring_entity) {
        name.0 = params.axis_name.clone();
    }

    world.update_parent(ring_entity, Some(Parent(Some(params.parent))));

    if let Some(transform) = world.get_local_transform(ring_entity) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.rotation = Quat::identity();
        new_transform.scale = nalgebra_glm::vec3(1.0, 1.0, 1.0);
        world.assign_local_transform(ring_entity, new_transform);
    }

    if let Some(visibility) = world.get_visibility_mut(ring_entity) {
        visibility.visible = true;
    }

    set_gizmo_layer(world, ring_entity);

    let segments = 64;
    let color = nalgebra_glm::vec4(params.color.x, params.color.y, params.color.z, 1.0);

    if let Some(lines) = world.get_lines_mut(ring_entity) {
        lines.lines.clear();
        for segment_index in 0..segments {
            let angle1 = 2.0 * std::f32::consts::PI * (segment_index as f32) / (segments as f32);
            let angle2 =
                2.0 * std::f32::consts::PI * ((segment_index + 1) as f32) / (segments as f32);

            let (start, end) = if params.axis.x.abs() > 0.99 {
                (
                    nalgebra_glm::vec3(
                        0.0,
                        angle1.cos() * params.ring_radius,
                        angle1.sin() * params.ring_radius,
                    ),
                    nalgebra_glm::vec3(
                        0.0,
                        angle2.cos() * params.ring_radius,
                        angle2.sin() * params.ring_radius,
                    ),
                )
            } else if params.axis.z.abs() > 0.99 {
                (
                    nalgebra_glm::vec3(
                        angle1.cos() * params.ring_radius,
                        angle1.sin() * params.ring_radius,
                        0.0,
                    ),
                    nalgebra_glm::vec3(
                        angle2.cos() * params.ring_radius,
                        angle2.sin() * params.ring_radius,
                        0.0,
                    ),
                )
            } else {
                (
                    nalgebra_glm::vec3(
                        angle1.cos() * params.ring_radius,
                        0.0,
                        angle1.sin() * params.ring_radius,
                    ),
                    nalgebra_glm::vec3(
                        angle2.cos() * params.ring_radius,
                        0.0,
                        angle2.sin() * params.ring_radius,
                    ),
                )
            };

            lines.lines.push(Line { start, end, color });
        }
        lines.mark_dirty();
    }

    if let Some(bounding) = world.get_bounding_volume_mut(ring_entity) {
        bounding.sphere_radius = params.ring_radius;
        bounding.obb.center = Vec3::zeros();

        let hit_thickness = 0.15;
        if params.axis.x.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(hit_thickness, params.ring_radius, params.ring_radius);
        } else if params.axis.z.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(params.ring_radius, params.ring_radius, hit_thickness);
        } else {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(params.ring_radius, hit_thickness, params.ring_radius);
        }
        bounding.obb.orientation = Quat::identity();
    }

    ring_entity
}

fn create_plane_cube(
    world: &mut World,
    parent: Entity,
    plane_normal: Vec3,
    color: Vec3,
    size: f32,
    plane_name: &str,
) -> Entity {
    let plane_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL_REF
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER
            | BOUNDING_VOLUME,
        1,
    )[0];
    world
        .resources
        .mesh_render_state
        .mark_entity_added(plane_entity);

    if let Some(name) = world.get_name_mut(plane_entity) {
        name.0 = plane_name.to_string();
    }

    set_gizmo_layer(world, plane_entity);

    world.update_parent(plane_entity, Some(Parent(Some(parent))));

    if let Some(transform) = world.get_local_transform(plane_entity) {
        let mut new_transform = *transform;
        let offset = size * 0.5;

        if plane_normal.z.abs() > 0.5 {
            new_transform.translation = nalgebra_glm::vec3(offset, offset, 0.0);
            new_transform.rotation = Quat::identity();
            new_transform.scale = nalgebra_glm::vec3(size, size, size * 0.05);
        } else if plane_normal.x.abs() > 0.5 {
            new_transform.translation = nalgebra_glm::vec3(0.0, offset, offset);
            new_transform.rotation = Quat::identity();
            new_transform.scale = nalgebra_glm::vec3(size * 0.05, size, size);
        } else {
            new_transform.translation = nalgebra_glm::vec3(offset, 0.0, offset);
            new_transform.rotation = Quat::identity();
            new_transform.scale = nalgebra_glm::vec3(size, size * 0.05, size);
        }
        world.assign_local_transform(plane_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(plane_entity) {
        mesh.name = "Cube".to_string();
    }

    let material_name = format!("Gizmo_{}_Plane", plane_entity.id);
    material_registry_insert(
        &mut world.resources.material_registry,
        material_name.clone(),
        Material {
            base_color: [color.x, color.y, color.z, 0.4],
            unlit: true,
            alpha_mode: AlphaMode::Blend,
            ..Default::default()
        },
    );
    if let Some(&index) = world
        .resources
        .material_registry
        .registry
        .name_to_index
        .get(&material_name)
    {
        world
            .resources
            .material_registry
            .registry
            .add_reference(index);
    }
    world.set_material_ref(plane_entity, MaterialRef::new(material_name));

    if let Some(visibility) = world.get_visibility_mut(plane_entity) {
        visibility.visible = true;
    }

    if let Some(bounding) = world.get_bounding_volume_mut(plane_entity) {
        let click_size = size * 1.2;
        let thickness = 0.3;
        bounding.sphere_radius = click_size * 0.866;

        if plane_normal.z.abs() > 0.5 {
            bounding.obb.center = Vec3::zeros();
            bounding.obb.half_extents =
                nalgebra_glm::vec3(click_size * 0.5, click_size * 0.5, thickness);
            bounding.obb.orientation = Quat::identity();
        } else if plane_normal.x.abs() > 0.5 {
            bounding.obb.center = Vec3::zeros();
            bounding.obb.half_extents =
                nalgebra_glm::vec3(thickness, click_size * 0.5, click_size * 0.5);
            bounding.obb.orientation = Quat::identity();
        } else {
            bounding.obb.center = Vec3::zeros();
            bounding.obb.half_extents =
                nalgebra_glm::vec3(click_size * 0.5, thickness, click_size * 0.5);
            bounding.obb.orientation = Quat::identity();
        }
    }

    plane_entity
}

fn create_infinite_axis_line(
    world: &mut World,
    parent: Entity,
    direction: Vec3,
    color: Vec3,
    line_name: &str,
) -> Entity {
    let line_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL_REF
            | PARENT
            | NAME
            | RENDER_LAYER
            | VISIBILITY,
        1,
    )[0];
    world
        .resources
        .mesh_render_state
        .mark_entity_added(line_entity);

    if let Some(name) = world.get_name_mut(line_entity) {
        name.0 = line_name.to_string();
    }

    set_gizmo_layer(world, line_entity);

    world.update_parent(line_entity, Some(Parent(Some(parent))));

    let length = 2000.0;
    let radius = 0.02;

    let rotation = if direction.y.abs() > 0.99 {
        Quat::identity()
    } else if direction.x.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if direction.x > 0.0 {
                -std::f32::consts::PI / 2.0
            } else {
                std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(0.0, 0.0, 1.0),
        )
    } else if direction.z.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if direction.z > 0.0 {
                std::f32::consts::PI / 2.0
            } else {
                -std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(1.0, 0.0, 0.0),
        )
    } else {
        Quat::identity()
    };

    if let Some(transform) = world.get_local_transform(line_entity) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.rotation = rotation;
        new_transform.scale = nalgebra_glm::vec3(radius / 0.5, length / 1.0, radius / 0.5);
        world.assign_local_transform(line_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(line_entity) {
        mesh.name = "Cylinder".to_string();
    }

    let material_name = format!("Gizmo_{}_AxisLine", line_entity.id);
    material_registry_insert(
        &mut world.resources.material_registry,
        material_name.clone(),
        Material {
            base_color: [color.x, color.y, color.z, 0.5],
            unlit: true,
            alpha_mode: AlphaMode::Blend,
            ..Default::default()
        },
    );
    if let Some(&index) = world
        .resources
        .material_registry
        .registry
        .name_to_index
        .get(&material_name)
    {
        world
            .resources
            .material_registry
            .registry
            .add_reference(index);
    }
    world.set_material_ref(line_entity, MaterialRef::new(material_name));

    if let Some(visibility) = world.get_visibility_mut(line_entity) {
        visibility.visible = false;
    }

    line_entity
}

pub fn destroy_gizmo(world: &mut World, gizmo: &GizmoState) {
    let descendants = crate::ecs::transform::queries::query_descendants(world, gizmo.root);
    let all_entities: Vec<Entity> = std::iter::once(gizmo.root)
        .chain(descendants.iter().copied())
        .collect();

    for &entity in &all_entities {
        if world.entity_has_components(entity, crate::ecs::world::RENDER_MESH) {
            if let Some(material_ref) = world.get_material_ref(entity) {
                let material_name = material_ref.name.clone();
                if let Some(&index) = world
                    .resources
                    .material_registry
                    .registry
                    .name_to_index
                    .get(&material_name)
                {
                    world
                        .resources
                        .material_registry
                        .registry
                        .remove_reference(index);
                }
            }
            world
                .resources
                .mesh_render_state
                .mark_entity_removed(entity);
        }
    }

    world.despawn_entities(&descendants);
    world.despawn_entities(&[gizmo.root]);
}

pub fn is_gizmo_entity(world: &World, gizmo: &GizmoState, entity: Entity) -> bool {
    if entity == gizmo.root {
        return true;
    }
    let descendants = crate::ecs::transform::queries::query_descendants(world, gizmo.root);
    descendants.contains(&entity)
}

pub fn update_gizmo_transform(
    world: &mut World,
    gizmo: &GizmoState,
    target_position: Vec3,
    target_rotation: Quat,
    camera_distance: f32,
) {
    let config = GizmoConfig::default();
    let scale = (camera_distance * config.scale_factor)
        .max(config.min_scale)
        .min(config.max_scale);
    if let Some(transform) = world.get_local_transform(gizmo.root) {
        let mut new_transform = *transform;
        new_transform.translation = target_position;
        new_transform.scale = nalgebra_glm::vec3(scale, scale, scale);
        new_transform.rotation = target_rotation;
        world.assign_local_transform(gizmo.root, new_transform);
    }
}

fn set_entity_visible_recursive(world: &mut World, entity: Entity, visible: bool) {
    if let Some(vis) = world.get_visibility_mut(entity) {
        vis.visible = visible;
    }

    let descendants: Vec<Entity> = crate::ecs::transform::queries::query_descendants(world, entity)
        .into_iter()
        .collect();

    for descendant in descendants {
        if let Some(vis) = world.get_visibility_mut(descendant) {
            vis.visible = visible;
        }
    }
}

pub fn show_entities(world: &mut World, entities: &[Entity]) {
    for &entity in entities {
        if let Some(visibility) = world.get_visibility_mut(entity) {
            visibility.visible = true;
        }
    }
}

pub fn hide_entities(world: &mut World, entities: &[Entity]) {
    for &entity in entities {
        if let Some(visibility) = world.get_visibility_mut(entity) {
            visibility.visible = false;
        }
    }
}

pub fn check_gizmo_axis_hit(
    world: &World,
    gizmo: &GizmoState,
    picked_entity: Entity,
) -> Option<Vec3> {
    let axes = [
        (
            gizmo.axis_entities.x_axis,
            nalgebra_glm::vec3(1.0, 0.0, 0.0),
        ),
        (
            gizmo.axis_entities.y_axis,
            nalgebra_glm::vec3(0.0, 1.0, 0.0),
        ),
        (
            gizmo.axis_entities.z_axis,
            nalgebra_glm::vec3(0.0, 0.0, 1.0),
        ),
    ];

    for (axis_entity, axis_vec) in axes.iter() {
        if let Some(entity) = axis_entity
            && (picked_entity == *entity || is_child_of(world, picked_entity, *entity))
        {
            return Some(*axis_vec);
        }
    }

    if let Some(center_entity) = gizmo.axis_entities.center_cube
        && (picked_entity == center_entity || is_child_of(world, picked_entity, center_entity))
    {
        return Some(nalgebra_glm::vec3(1.0, 1.0, 1.0));
    }

    let planes = [
        (
            gizmo.axis_entities.xy_plane,
            nalgebra_glm::vec3(1.1, 1.1, 0.0),
        ),
        (
            gizmo.axis_entities.yz_plane,
            nalgebra_glm::vec3(0.0, 1.1, 1.1),
        ),
        (
            gizmo.axis_entities.xz_plane,
            nalgebra_glm::vec3(1.1, 0.0, 1.1),
        ),
    ];

    for (plane_entity, plane_vec) in planes.iter() {
        if let Some(entity) = plane_entity
            && (picked_entity == *entity || is_child_of(world, picked_entity, *entity))
        {
            return Some(*plane_vec);
        }
    }

    None
}

fn is_child_of(world: &World, child: Entity, parent: Entity) -> bool {
    let mut current = child;
    while let Some(parent_comp) = world.get_parent(current) {
        if let Some(parent_entity) = parent_comp.0 {
            if parent_entity == parent {
                return true;
            }
            current = parent_entity;
        } else {
            break;
        }
    }
    false
}

fn set_entity_material_color(world: &mut World, entity: Entity, color: [f32; 4]) {
    if let Some(material_ref) = world.get_material_ref(entity) {
        let name = material_ref.name.clone();
        if let Some(material) =
            registry_entry_by_name_mut(&mut world.resources.material_registry.registry, &name)
        {
            material.base_color = color;
            world
                .resources
                .mesh_render_state
                .mark_material_dirty(entity);
        }
    }
}

pub fn highlight_gizmo_axis(world: &mut World, gizmo: &GizmoState, axis: Option<Vec3>) {
    let axis_entities = &gizmo.axis_entities;

    let axes = [
        (axis_entities.x_axis, [0.7, 0.0, 0.0, 1.0]),
        (axis_entities.y_axis, [0.0, 0.7, 0.0, 1.0]),
        (axis_entities.z_axis, [0.0, 0.0, 0.7, 1.0]),
    ];

    let highlighted_axis = axis.and_then(|a| {
        if a.x > 0.5 && a.y < 0.1 && a.z < 0.1 {
            Some(0)
        } else if a.y > 0.5 && a.x < 0.1 && a.z < 0.1 {
            Some(1)
        } else if a.z > 0.5 && a.x < 0.1 && a.y < 0.1 {
            Some(2)
        } else {
            None
        }
    });

    let center_highlighted = axis.is_some_and(|a| a.x > 0.9 && a.y > 0.9 && a.z > 0.9);

    let (xy_highlighted, yz_highlighted, xz_highlighted) =
        axis.map_or((false, false, false), |a| {
            if a.x > 1.05 && a.y > 1.05 {
                (true, false, false)
            } else if a.y > 1.05 && a.z > 1.05 {
                (false, true, false)
            } else if a.x > 1.05 && a.z > 1.05 {
                (false, false, true)
            } else {
                (false, false, false)
            }
        });

    for (index, (axis_entity, color)) in axes.iter().enumerate() {
        if let Some(entity) = axis_entity {
            let is_highlighted = Some(index) == highlighted_axis;

            let base_color = if is_highlighted {
                [1.0, 1.0, 0.0, 1.0]
            } else {
                *color
            };

            let children: Vec<Entity> =
                crate::ecs::transform::queries::query_children(world, *entity);
            for child in children {
                set_entity_material_color(world, child, base_color);
            }
        }
    }

    if let Some(center_entity) = axis_entities.center_cube {
        let color = if center_highlighted {
            [1.0, 1.0, 0.0, 1.0]
        } else {
            [0.6, 0.6, 0.6, 1.0]
        };
        set_entity_material_color(world, center_entity, color);
    }

    if let Some(xy_plane) = axis_entities.xy_plane {
        let color = if xy_highlighted {
            [1.0, 1.0, 0.0, 0.7]
        } else {
            [0.4, 0.4, 0.0, 0.4]
        };
        set_entity_material_color(world, xy_plane, color);
    }

    if let Some(yz_plane) = axis_entities.yz_plane {
        let color = if yz_highlighted {
            [1.0, 1.0, 0.0, 0.7]
        } else {
            [0.0, 0.4, 0.4, 0.4]
        };
        set_entity_material_color(world, yz_plane, color);
    }

    if let Some(xz_plane) = axis_entities.xz_plane {
        let color = if xz_highlighted {
            [1.0, 1.0, 0.0, 0.7]
        } else {
            [0.4, 0.0, 0.4, 0.4]
        };
        set_entity_material_color(world, xz_plane, color);
    }
}

#[derive(Clone, Copy)]
pub enum GizmoDragMode {
    None,
    Axis(Vec3),
    Plane(Vec3),
    Free,
}

pub fn set_gizmo_drag_visibility(world: &mut World, gizmo: &GizmoState, drag_mode: GizmoDragMode) {
    let axis_entities = &gizmo.axis_entities;

    match drag_mode {
        GizmoDragMode::None => {
            if let Some(x_axis) = axis_entities.x_axis {
                set_entity_visible_recursive(world, x_axis, true);
            }
            if let Some(y_axis) = axis_entities.y_axis {
                set_entity_visible_recursive(world, y_axis, true);
            }
            if let Some(z_axis) = axis_entities.z_axis {
                set_entity_visible_recursive(world, z_axis, true);
            }
            if let Some(xy_plane) = axis_entities.xy_plane {
                set_entity_visible_recursive(world, xy_plane, true);
            }
            if let Some(yz_plane) = axis_entities.yz_plane {
                set_entity_visible_recursive(world, yz_plane, true);
            }
            if let Some(xz_plane) = axis_entities.xz_plane {
                set_entity_visible_recursive(world, xz_plane, true);
            }
            if let Some(center_cube) = axis_entities.center_cube {
                set_entity_visible_recursive(world, center_cube, true);
            }

            if let Some(x_line) = axis_entities.x_axis_line {
                hide_entities(world, &[x_line]);
            }
            if let Some(y_line) = axis_entities.y_axis_line {
                hide_entities(world, &[y_line]);
            }
            if let Some(z_line) = axis_entities.z_axis_line {
                hide_entities(world, &[z_line]);
            }
        }
        GizmoDragMode::Axis(axis) => {
            let mut all_parts = Vec::new();

            if let Some(x_axis) = axis_entities.x_axis {
                all_parts.push(x_axis);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world, x_axis,
                ));
            }
            if let Some(y_axis) = axis_entities.y_axis {
                all_parts.push(y_axis);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world, y_axis,
                ));
            }
            if let Some(z_axis) = axis_entities.z_axis {
                all_parts.push(z_axis);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world, z_axis,
                ));
            }
            if let Some(xy_plane) = axis_entities.xy_plane {
                all_parts.push(xy_plane);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world, xy_plane,
                ));
            }
            if let Some(yz_plane) = axis_entities.yz_plane {
                all_parts.push(yz_plane);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world, yz_plane,
                ));
            }
            if let Some(xz_plane) = axis_entities.xz_plane {
                all_parts.push(xz_plane);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world, xz_plane,
                ));
            }
            if let Some(center_cube) = axis_entities.center_cube {
                all_parts.push(center_cube);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world,
                    center_cube,
                ));
            }

            hide_entities(world, &all_parts);

            if let Some(x_line) = axis_entities.x_axis_line {
                hide_entities(world, &[x_line]);
            }
            if let Some(y_line) = axis_entities.y_axis_line {
                hide_entities(world, &[y_line]);
            }
            if let Some(z_line) = axis_entities.z_axis_line {
                hide_entities(world, &[z_line]);
            }

            if axis.x > 0.5 {
                if let Some(x_axis) = axis_entities.x_axis {
                    let mut to_show = vec![x_axis];
                    to_show.extend(crate::ecs::transform::queries::query_descendants(
                        world, x_axis,
                    ));
                    show_entities(world, &to_show);
                }
                if let Some(x_line) = axis_entities.x_axis_line {
                    show_entities(world, &[x_line]);
                }
            } else if axis.y > 0.5 {
                if let Some(y_axis) = axis_entities.y_axis {
                    let mut to_show = vec![y_axis];
                    to_show.extend(crate::ecs::transform::queries::query_descendants(
                        world, y_axis,
                    ));
                    show_entities(world, &to_show);
                }
                if let Some(y_line) = axis_entities.y_axis_line {
                    show_entities(world, &[y_line]);
                }
            } else if axis.z > 0.5 {
                if let Some(z_axis) = axis_entities.z_axis {
                    let mut to_show = vec![z_axis];
                    to_show.extend(crate::ecs::transform::queries::query_descendants(
                        world, z_axis,
                    ));
                    show_entities(world, &to_show);
                }
                if let Some(z_line) = axis_entities.z_axis_line {
                    show_entities(world, &[z_line]);
                }
            }
        }
        GizmoDragMode::Plane(normal) => {
            if let Some(center_cube) = axis_entities.center_cube {
                set_entity_visible_recursive(world, center_cube, false);
            }

            if let Some(x_line) = axis_entities.x_axis_line {
                hide_entities(world, &[x_line]);
            }
            if let Some(y_line) = axis_entities.y_axis_line {
                hide_entities(world, &[y_line]);
            }
            if let Some(z_line) = axis_entities.z_axis_line {
                hide_entities(world, &[z_line]);
            }

            if normal.z > 0.5 {
                if let Some(xy_plane) = axis_entities.xy_plane {
                    set_entity_visible_recursive(world, xy_plane, true);
                }
                if let Some(yz_plane) = axis_entities.yz_plane {
                    set_entity_visible_recursive(world, yz_plane, false);
                }
                if let Some(xz_plane) = axis_entities.xz_plane {
                    set_entity_visible_recursive(world, xz_plane, false);
                }
                if let Some(x_axis) = axis_entities.x_axis {
                    set_entity_visible_recursive(world, x_axis, true);
                }
                if let Some(y_axis) = axis_entities.y_axis {
                    set_entity_visible_recursive(world, y_axis, true);
                }
                if let Some(z_axis) = axis_entities.z_axis {
                    set_entity_visible_recursive(world, z_axis, false);
                }
                if let Some(x_line) = axis_entities.x_axis_line {
                    show_entities(world, &[x_line]);
                }
                if let Some(y_line) = axis_entities.y_axis_line {
                    show_entities(world, &[y_line]);
                }
            } else if normal.x > 0.5 {
                if let Some(yz_plane) = axis_entities.yz_plane {
                    set_entity_visible_recursive(world, yz_plane, true);
                }
                if let Some(xy_plane) = axis_entities.xy_plane {
                    set_entity_visible_recursive(world, xy_plane, false);
                }
                if let Some(xz_plane) = axis_entities.xz_plane {
                    set_entity_visible_recursive(world, xz_plane, false);
                }
                if let Some(y_axis) = axis_entities.y_axis {
                    set_entity_visible_recursive(world, y_axis, true);
                }
                if let Some(z_axis) = axis_entities.z_axis {
                    set_entity_visible_recursive(world, z_axis, true);
                }
                if let Some(x_axis) = axis_entities.x_axis {
                    set_entity_visible_recursive(world, x_axis, false);
                }
                if let Some(y_line) = axis_entities.y_axis_line {
                    show_entities(world, &[y_line]);
                }
                if let Some(z_line) = axis_entities.z_axis_line {
                    show_entities(world, &[z_line]);
                }
            } else {
                if let Some(xz_plane) = axis_entities.xz_plane {
                    set_entity_visible_recursive(world, xz_plane, true);
                }
                if let Some(xy_plane) = axis_entities.xy_plane {
                    set_entity_visible_recursive(world, xy_plane, false);
                }
                if let Some(yz_plane) = axis_entities.yz_plane {
                    set_entity_visible_recursive(world, yz_plane, false);
                }
                if let Some(x_axis) = axis_entities.x_axis {
                    set_entity_visible_recursive(world, x_axis, true);
                }
                if let Some(z_axis) = axis_entities.z_axis {
                    set_entity_visible_recursive(world, z_axis, true);
                }
                if let Some(y_axis) = axis_entities.y_axis {
                    set_entity_visible_recursive(world, y_axis, false);
                }
                if let Some(x_line) = axis_entities.x_axis_line {
                    show_entities(world, &[x_line]);
                }
                if let Some(z_line) = axis_entities.z_axis_line {
                    show_entities(world, &[z_line]);
                }
            }
        }
        GizmoDragMode::Free => {
            if let Some(x_line) = axis_entities.x_axis_line {
                hide_entities(world, &[x_line]);
            }
            if let Some(y_line) = axis_entities.y_axis_line {
                hide_entities(world, &[y_line]);
            }
            if let Some(z_line) = axis_entities.z_axis_line {
                hide_entities(world, &[z_line]);
            }

            if let Some(x_axis) = axis_entities.x_axis {
                set_entity_visible_recursive(world, x_axis, false);
            }
            if let Some(y_axis) = axis_entities.y_axis {
                set_entity_visible_recursive(world, y_axis, false);
            }
            if let Some(z_axis) = axis_entities.z_axis {
                set_entity_visible_recursive(world, z_axis, false);
            }
            if let Some(xy_plane) = axis_entities.xy_plane {
                set_entity_visible_recursive(world, xy_plane, false);
            }
            if let Some(yz_plane) = axis_entities.yz_plane {
                set_entity_visible_recursive(world, yz_plane, false);
            }
            if let Some(xz_plane) = axis_entities.xz_plane {
                set_entity_visible_recursive(world, xz_plane, false);
            }

            if let Some(center_cube) = axis_entities.center_cube {
                set_entity_visible_recursive(world, center_cube, true);
            }
        }
    }
}
