//! Developer console for runtime debugging and commands.
//!
//! An in-game terminal for executing commands, inspecting entities, and tweaking settings:
//!
//! - [`ShellState`]: Console state (visibility, input, history, commands)
//! - [`Command`]: Trait for custom commands
//! - [`CommandRegistry`]: Built-in and app-specific command storage
//! - [`shell_immediate_ui`]: Render console with immediate mode UI
//! - [`shell_ui`]: Render console with egui (requires `egui` feature)
//!
//! # Opening the Console
//!
//! Toggle with `Alt+C`. Close with `Escape` or `Alt+C` again.
//!
//! # Basic Setup
//!
//! ```ignore
//! struct MyGame {
//!     shell: ShellState<()>,
//! }
//!
//! impl State for MyGame {
//!     fn on_start(&mut self, world: &mut World) {
//!         self.shell.register_builtin_commands();  // spawn_cube, grid, fov, etc.
//!     }
//!
//!     fn on_key_input(&mut self, world: &mut World, key: KeyCode, state: KeyState) {
//!         if self.shell.visible {
//!             self.shell.handle_key(key, state == KeyState::Pressed);
//!         }
//!         if state == KeyState::Pressed && key == KeyCode::AltLeft {
//!             // Track alt for toggle
//!         }
//!     }
//!
//!     fn on_char_input(&mut self, world: &mut World, ch: char) {
//!         if self.shell.visible && ch.is_ascii_graphic() || ch == ' ' {
//!             self.shell.input_buffer.push(ch);
//!         }
//!     }
//!
//!     fn run_systems(&mut self, world: &mut World) {
//!         self.shell.update_animation(world.resources.window.timing.delta_time);
//!         shell_immediate_ui(&mut self.shell, &mut world.resources.immediate_ui, world);
//!     }
//! }
//! ```
//!
//! # Creating Custom Commands
//!
//! Implement the [`Command`] trait:
//!
//! ```ignore
//! struct DebugCommand;
//!
//! impl Command<()> for DebugCommand {
//!     fn name(&self) -> &str { "debug" }
//!     fn description(&self) -> &str { "Toggle debug mode" }
//!     fn usage(&self) -> &str { "debug [on|off]" }
//!
//!     fn execute(&self, args: &[&str], world: &mut World, _ctx: &mut ()) -> String {
//!         if args.is_empty() {
//!             return format!("Debug: {}", world.resources.graphics.debug_mode);
//!         }
//!         match args[0] {
//!             "on" => world.resources.graphics.debug_mode = true,
//!             "off" => world.resources.graphics.debug_mode = false,
//!             _ => return "Usage: debug [on|off]".to_string(),
//!         }
//!         "OK".to_string()
//!     }
//! }
//!
//! // Register in on_start
//! shell.register_command(Box::new(DebugCommand));
//! ```
//!
//! # Using Context for State
//!
//! Pass game-specific state via the generic `C` type:
//!
//! ```ignore
//! struct GameContext {
//!     score: i32,
//!     paused: bool,
//! }
//!
//! struct ScoreCommand;
//!
//! impl Command<GameContext> for ScoreCommand {
//!     fn name(&self) -> &str { "score" }
//!     fn description(&self) -> &str { "Get or set score" }
//!     fn usage(&self) -> &str { "score [value]" }
//!
//!     fn execute(&self, args: &[&str], _world: &mut World, ctx: &mut GameContext) -> String {
//!         if let Some(value) = args.first() {
//!             ctx.score = value.parse().unwrap_or(0);
//!         }
//!         format!("Score: {}", ctx.score)
//!     }
//! }
//!
//! let mut shell: ShellState<GameContext> = ShellState::new(GameContext { score: 0, paused: false });
//! ```
//!
//! # Built-in Commands
//!
//! Register with [`ShellState::register_builtin_commands`]:
//!
//! | Command | Description |
//! |---------|-------------|
//! | `help [cmd]` | List commands or show command help |
//! | `clear` | Clear console output |
//! | `spawn_cube <x> <y> <z>` | Spawn a cube at position |
//! | `spawn_sphere <x> <y> <z>` | Spawn a sphere at position |
//! | `spawn_cylinder <x> <y> <z>` | Spawn a cylinder at position |
//! | `spawn_cone <x> <y> <z>` | Spawn a cone at position |
//! | `spawn_torus <x> <y> <z>` | Spawn a torus at position |
//! | `spawn_plane <x> <y> <z>` | Spawn a plane at position |
//! | `grid [on\|off]` | Toggle grid visibility |
//! | `atmosphere <type>` | Set atmosphere (none, sky, cloudy, space, nebula, sunset) |
//! | `fov [degrees]` | Get or set camera FOV |
//! | `timescale [value]` | Get or set time speed (1.0 = normal) |
//! | `fps` | Show current framerate |
//! | `pos` | Show camera position |
//! | `tp <x> <y> <z>` | Teleport camera to position |
//! | `unlit [on\|off]` | Toggle unlit rendering |
//! | `bloom [on\|off\|intensity]` | Toggle or set bloom |
//! | `rotate <entity> <x> <y> <z>` | Rotate entity by degrees |
//! | `scale <entity> <factor>` | Scale entity uniformly |
//! | `move <entity> <x> <y> <z>` | Move entity to position |
//! | `delete <entity>` | Delete an entity |
//! | `inspect <entity>` | Show entity information |
//! | `quit` | Exit the application |
//!
//! # Entity References
//!
//! Entities are referenced as `entity_<id>_<generation>`:
//!
//! ```ignore
//! > spawn_cube 0 1 0
//! Spawned cube at (0, 1, 0)
//!
//! > inspect entity_5_0
//! Entity: entity_5_0
//! Position: (0.00, 1.00, 0.00)
//! Scale: (1.00, 1.00, 1.00)
//!
//! > rotate entity_5_0 0 45 0
//! Rotated entity_5_0 by (0, 45, 0) degrees
//! ```
//!
//! # Inserting Entity References
//!
//! Insert entities from picking results:
//!
//! ```ignore
//! if let Some(result) = pick_closest_entity(world, &ray, &options) {
//!     shell.insert_entity(result.entity);  // Appends "entity_X_Y" to input
//! }
//! ```
//!
//! # Console Features
//!
//! - Command history: `Up`/`Down` arrows cycle through history
//! - Resizable: Drag the bottom edge to resize
//! - Scrollable: Output area scrolls automatically
//! - Smooth animation: Slides in/out when toggling
//!
//! [`ShellState`]: ShellState
//! [`Command`]: Command
//! [`CommandRegistry`]: CommandRegistry
//! [`shell_immediate_ui`]: shell_immediate_ui
//! [`shell_ui`]: shell_ui

use crate::ecs::camera::components::Projection;
use crate::ecs::graphics::resources::Atmosphere;
use crate::ecs::transform::commands::mark_local_transform_dirty;
use crate::ecs::ui::immediate::ImmediateUi;
use crate::ecs::ui::types::Rect;
use crate::ecs::world::commands::{
    spawn_cone_at, spawn_cube_at, spawn_cylinder_at, spawn_plane_at, spawn_sphere_at,
    spawn_torus_at,
};
use crate::ecs::world::{Entity, World};
use crate::render::wgpu::passes::geometry::UiLayer;
use nalgebra_glm::{Vec2, Vec3, Vec4};
use std::collections::HashMap;
use winit::keyboard::KeyCode;

pub fn format_entity(entity: Entity) -> String {
    format!("entity_{}_{}", entity.id, entity.generation)
}

pub fn parse_entity(s: &str) -> Option<Entity> {
    let parts: Vec<&str> = s.split('_').collect();
    if parts.len() != 3 || parts[0] != "entity" {
        return None;
    }
    let id: u32 = parts[1].parse().ok()?;
    let generation: u32 = parts[2].parse().ok()?;
    Some(Entity { id, generation })
}

pub trait Command<C>: Send + Sync {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn usage(&self) -> &str;
    fn execute(&self, args: &[&str], world: &mut World, context: &mut C) -> String;
}

pub struct CommandRegistry<C> {
    builtin_commands: HashMap<String, Box<dyn Command<C>>>,
    app_commands: HashMap<String, Box<dyn Command<C>>>,
}

impl<C> Default for CommandRegistry<C> {
    fn default() -> Self {
        Self::new()
    }
}

impl<C> CommandRegistry<C> {
    pub fn new() -> Self {
        Self {
            builtin_commands: HashMap::new(),
            app_commands: HashMap::new(),
        }
    }

    pub fn register(&mut self, command: Box<dyn Command<C>>) {
        self.app_commands
            .insert(command.name().to_string(), command);
    }

    pub fn register_builtin(&mut self, command: Box<dyn Command<C>>) {
        self.builtin_commands
            .insert(command.name().to_string(), command);
    }

    pub fn get(&self, name: &str) -> Option<&dyn Command<C>> {
        self.builtin_commands
            .get(name)
            .or_else(|| self.app_commands.get(name))
            .map(|c| c.as_ref())
    }

    pub fn contains(&self, name: &str) -> bool {
        self.builtin_commands.contains_key(name) || self.app_commands.contains_key(name)
    }

    pub fn builtin_command_names(&self) -> Vec<&str> {
        let mut names: Vec<_> = self.builtin_commands.keys().map(|s| s.as_str()).collect();
        names.sort();
        names
    }

    pub fn app_command_names(&self) -> Vec<&str> {
        let mut names: Vec<_> = self.app_commands.keys().map(|s| s.as_str()).collect();
        names.sort();
        names
    }
}

pub struct ShellState<C> {
    pub context: C,
    pub visible: bool,
    pub input_buffer: String,
    pub history: Vec<String>,
    pub output: Vec<OutputLine>,
    pub history_index: Option<usize>,
    pub registry: CommandRegistry<C>,
    pub animation_progress: f32,
    pub scroll_to_bottom: bool,
    pub height: f32,
    pub scroll_offset: f32,
    pub dragging_resize: bool,
    pub drag_start_y: f32,
    pub drag_start_height: f32,
    pending_enter: bool,
    pending_up: bool,
    pending_down: bool,
    pending_escape: bool,
}

pub struct OutputLine {
    pub text: String,
    pub is_command: bool,
}

impl<C: Default> Default for ShellState<C> {
    fn default() -> Self {
        Self::new(C::default())
    }
}

impl<C> ShellState<C> {
    pub fn new(context: C) -> Self {
        Self {
            context,
            visible: false,
            input_buffer: String::new(),
            history: Vec::new(),
            output: vec![OutputLine {
                text: "Type 'help' for available commands.".to_string(),
                is_command: false,
            }],
            history_index: None,
            registry: CommandRegistry::new(),
            animation_progress: 0.0,
            scroll_to_bottom: true,
            height: 100.0,
            scroll_offset: 0.0,
            dragging_resize: false,
            drag_start_y: 0.0,
            drag_start_height: 0.0,
            pending_enter: false,
            pending_up: false,
            pending_down: false,
            pending_escape: false,
        }
    }

    pub fn toggle(&mut self) {
        self.visible = !self.visible;
    }

    pub fn register_command(&mut self, command: Box<dyn Command<C>>) {
        self.registry.register(command);
    }

    pub fn insert_text(&mut self, text: &str) {
        if !self.input_buffer.is_empty() && !self.input_buffer.ends_with(' ') {
            self.input_buffer.push(' ');
        }
        self.input_buffer.push_str(text);
    }

    pub fn insert_entity(&mut self, entity: Entity) {
        self.insert_text(&format_entity(entity));
    }

    pub fn handle_key(&mut self, key: KeyCode, pressed: bool) {
        if pressed {
            match key {
                KeyCode::Enter => self.pending_enter = true,
                KeyCode::ArrowUp => self.pending_up = true,
                KeyCode::ArrowDown => self.pending_down = true,
                KeyCode::Escape => self.pending_escape = true,
                KeyCode::Backspace => {
                    self.input_buffer.pop();
                }
                _ => {}
            }
        }
    }

    pub fn execute_command(&mut self, world: &mut World) {
        let input = self.input_buffer.trim().to_string();
        if input.is_empty() {
            return;
        }

        self.output.push(OutputLine {
            text: format!("> {}", input),
            is_command: true,
        });

        self.history.push(input.clone());
        self.history_index = None;

        let parts: Vec<&str> = input.split_whitespace().collect();
        if parts.is_empty() {
            self.input_buffer.clear();
            return;
        }

        let command_name = parts[0];
        let args = &parts[1..];

        if command_name == "clear" {
            self.output.clear();
        } else if command_name == "help" {
            if args.is_empty() {
                let mut help_text = String::from("Commands:\n");
                help_text.push_str("  clear - Clear the console output\n");
                help_text.push_str("  help - List all available commands\n");

                let builtin_names = self.registry.builtin_command_names();
                if !builtin_names.is_empty() {
                    help_text.push_str("\nBuilt-in:\n");
                    for name in builtin_names {
                        if let Some(cmd) = self.registry.get(name) {
                            help_text.push_str(&format!("  {} - {}\n", name, cmd.description()));
                        }
                    }
                }

                let app_names = self.registry.app_command_names();
                if !app_names.is_empty() {
                    help_text.push_str("\nApp:\n");
                    for name in app_names {
                        if let Some(cmd) = self.registry.get(name) {
                            help_text.push_str(&format!("  {} - {}\n", name, cmd.description()));
                        }
                    }
                }

                self.output.push(OutputLine {
                    text: help_text,
                    is_command: false,
                });
            } else if let Some(cmd) = self.registry.get(args[0]) {
                self.output.push(OutputLine {
                    text: format!(
                        "{}\nUsage: {}\n{}",
                        cmd.name(),
                        cmd.usage(),
                        cmd.description()
                    ),
                    is_command: false,
                });
            } else {
                self.output.push(OutputLine {
                    text: format!("Unknown command: {}", args[0]),
                    is_command: false,
                });
            }
        } else if self.registry.contains(command_name) {
            if let Some(cmd) = self.registry.get(command_name) {
                let result = cmd.execute(args, world, &mut self.context);
                if !result.is_empty() {
                    self.output.push(OutputLine {
                        text: result,
                        is_command: false,
                    });
                }
            }
        } else {
            self.output.push(OutputLine {
                text: format!("Unknown command: {}", command_name),
                is_command: false,
            });
        }

        self.input_buffer.clear();
        self.scroll_to_bottom = true;
    }

    pub fn history_up(&mut self) {
        if self.history.is_empty() {
            return;
        }

        match self.history_index {
            None => {
                self.history_index = Some(self.history.len() - 1);
            }
            Some(index) if index > 0 => {
                self.history_index = Some(index - 1);
            }
            _ => {}
        }

        if let Some(index) = self.history_index {
            self.input_buffer = self.history[index].clone();
        }
    }

    pub fn history_down(&mut self) {
        match self.history_index {
            Some(index) if index < self.history.len() - 1 => {
                self.history_index = Some(index + 1);
                self.input_buffer = self.history[index + 1].clone();
            }
            Some(_) => {
                self.history_index = None;
                self.input_buffer.clear();
            }
            None => {}
        }
    }

    pub fn update_animation(&mut self, delta_time: f32) {
        let target = if self.visible { 1.0 } else { 0.0 };
        let speed = 8.0;
        self.animation_progress += (target - self.animation_progress) * speed * delta_time;
        self.animation_progress = self.animation_progress.clamp(0.0, 1.0);
    }

    pub fn should_render(&self) -> bool {
        self.animation_progress > 0.001
    }

    pub fn register_builtin_commands(&mut self) {
        self.registry.register_builtin(Box::new(SpawnCubeCmd));
        self.registry.register_builtin(Box::new(SpawnSphereCmd));
        self.registry.register_builtin(Box::new(SpawnCylinderCmd));
        self.registry.register_builtin(Box::new(SpawnConeCmd));
        self.registry.register_builtin(Box::new(SpawnTorusCmd));
        self.registry.register_builtin(Box::new(SpawnPlaneCmd));
        self.registry.register_builtin(Box::new(GridCmd));
        self.registry.register_builtin(Box::new(AtmosphereCmd));
        self.registry.register_builtin(Box::new(FovCmd));
        self.registry.register_builtin(Box::new(TimescaleCmd));
        self.registry.register_builtin(Box::new(FpsCmd));
        self.registry.register_builtin(Box::new(QuitCmd));
        self.registry.register_builtin(Box::new(PosCmd));
        self.registry.register_builtin(Box::new(TeleportCmd));
        self.registry.register_builtin(Box::new(UnlitCmd));
        self.registry.register_builtin(Box::new(BloomCmd));
        self.registry.register_builtin(Box::new(RotateCmd));
        self.registry.register_builtin(Box::new(ScaleCmd));
        self.registry.register_builtin(Box::new(MoveCmd));
        self.registry.register_builtin(Box::new(DeleteCmd));
        self.registry.register_builtin(Box::new(InspectCmd));
    }
}

struct SpawnCubeCmd;

impl<C> Command<C> for SpawnCubeCmd {
    fn name(&self) -> &str {
        "spawn_cube"
    }

    fn description(&self) -> &str {
        "Spawn a cube at the specified position"
    }

    fn usage(&self) -> &str {
        "spawn_cube <x> <y> <z>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.len() != 3 {
            return "Usage: spawn_cube <x> <y> <z>".to_string();
        }

        let x: f32 = match args[0].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid x coordinate".to_string(),
        };
        let y: f32 = match args[1].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid y coordinate".to_string(),
        };
        let z: f32 = match args[2].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid z coordinate".to_string(),
        };

        spawn_cube_at(world, Vec3::new(x, y, z));
        format!("Spawned cube at ({}, {}, {})", x, y, z)
    }
}

struct SpawnSphereCmd;

impl<C> Command<C> for SpawnSphereCmd {
    fn name(&self) -> &str {
        "spawn_sphere"
    }

    fn description(&self) -> &str {
        "Spawn a sphere at the specified position"
    }

    fn usage(&self) -> &str {
        "spawn_sphere <x> <y> <z>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.len() != 3 {
            return "Usage: spawn_sphere <x> <y> <z>".to_string();
        }

        let x: f32 = match args[0].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid x coordinate".to_string(),
        };
        let y: f32 = match args[1].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid y coordinate".to_string(),
        };
        let z: f32 = match args[2].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid z coordinate".to_string(),
        };

        spawn_sphere_at(world, Vec3::new(x, y, z));
        format!("Spawned sphere at ({}, {}, {})", x, y, z)
    }
}

struct SpawnCylinderCmd;

impl<C> Command<C> for SpawnCylinderCmd {
    fn name(&self) -> &str {
        "spawn_cylinder"
    }

    fn description(&self) -> &str {
        "Spawn a cylinder at the specified position"
    }

    fn usage(&self) -> &str {
        "spawn_cylinder <x> <y> <z>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.len() != 3 {
            return "Usage: spawn_cylinder <x> <y> <z>".to_string();
        }

        let x: f32 = match args[0].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid x coordinate".to_string(),
        };
        let y: f32 = match args[1].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid y coordinate".to_string(),
        };
        let z: f32 = match args[2].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid z coordinate".to_string(),
        };

        spawn_cylinder_at(world, Vec3::new(x, y, z));
        format!("Spawned cylinder at ({}, {}, {})", x, y, z)
    }
}

struct SpawnConeCmd;

impl<C> Command<C> for SpawnConeCmd {
    fn name(&self) -> &str {
        "spawn_cone"
    }

    fn description(&self) -> &str {
        "Spawn a cone at the specified position"
    }

    fn usage(&self) -> &str {
        "spawn_cone <x> <y> <z>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.len() != 3 {
            return "Usage: spawn_cone <x> <y> <z>".to_string();
        }

        let x: f32 = match args[0].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid x coordinate".to_string(),
        };
        let y: f32 = match args[1].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid y coordinate".to_string(),
        };
        let z: f32 = match args[2].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid z coordinate".to_string(),
        };

        spawn_cone_at(world, Vec3::new(x, y, z));
        format!("Spawned cone at ({}, {}, {})", x, y, z)
    }
}

struct SpawnTorusCmd;

impl<C> Command<C> for SpawnTorusCmd {
    fn name(&self) -> &str {
        "spawn_torus"
    }

    fn description(&self) -> &str {
        "Spawn a torus at the specified position"
    }

    fn usage(&self) -> &str {
        "spawn_torus <x> <y> <z>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.len() != 3 {
            return "Usage: spawn_torus <x> <y> <z>".to_string();
        }

        let x: f32 = match args[0].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid x coordinate".to_string(),
        };
        let y: f32 = match args[1].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid y coordinate".to_string(),
        };
        let z: f32 = match args[2].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid z coordinate".to_string(),
        };

        spawn_torus_at(world, Vec3::new(x, y, z));
        format!("Spawned torus at ({}, {}, {})", x, y, z)
    }
}

struct SpawnPlaneCmd;

impl<C> Command<C> for SpawnPlaneCmd {
    fn name(&self) -> &str {
        "spawn_plane"
    }

    fn description(&self) -> &str {
        "Spawn a plane at the specified position"
    }

    fn usage(&self) -> &str {
        "spawn_plane <x> <y> <z>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.len() != 3 {
            return "Usage: spawn_plane <x> <y> <z>".to_string();
        }

        let x: f32 = match args[0].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid x coordinate".to_string(),
        };
        let y: f32 = match args[1].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid y coordinate".to_string(),
        };
        let z: f32 = match args[2].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid z coordinate".to_string(),
        };

        spawn_plane_at(world, Vec3::new(x, y, z));
        format!("Spawned plane at ({}, {}, {})", x, y, z)
    }
}

struct GridCmd;

impl<C> Command<C> for GridCmd {
    fn name(&self) -> &str {
        "grid"
    }

    fn description(&self) -> &str {
        "Toggle grid visibility (on/off)"
    }

    fn usage(&self) -> &str {
        "grid <on|off>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.is_empty() {
            let state = if world.resources.graphics.show_grid {
                "on"
            } else {
                "off"
            };
            return format!("Grid is currently {}", state);
        }

        match args[0].to_lowercase().as_str() {
            "on" | "true" | "1" => {
                world.resources.graphics.show_grid = true;
                "Grid enabled".to_string()
            }
            "off" | "false" | "0" => {
                world.resources.graphics.show_grid = false;
                "Grid disabled".to_string()
            }
            _ => "Usage: grid <on|off>".to_string(),
        }
    }
}

struct AtmosphereCmd;

impl<C> Command<C> for AtmosphereCmd {
    fn name(&self) -> &str {
        "atmosphere"
    }

    fn description(&self) -> &str {
        "Set the atmosphere type"
    }

    fn usage(&self) -> &str {
        "atmosphere <none|sky|cloudy|space|nebula|sunset>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.is_empty() {
            let current = match world.resources.graphics.atmosphere {
                Atmosphere::None => "none",
                Atmosphere::Sky => "sky",
                Atmosphere::CloudySky => "cloudy",
                Atmosphere::Space => "space",
                Atmosphere::Nebula => "nebula",
                Atmosphere::Sunset => "sunset",
                _ => "other",
            };
            return format!("Current atmosphere: {}", current);
        }

        let atmosphere = match args[0].to_lowercase().as_str() {
            "none" => Atmosphere::None,
            "sky" => Atmosphere::Sky,
            "cloudy" | "cloudysky" => Atmosphere::CloudySky,
            "space" => Atmosphere::Space,
            "nebula" => Atmosphere::Nebula,
            "sunset" => Atmosphere::Sunset,
            _ => {
                return "Unknown atmosphere type. Usage: atmosphere <none|sky|cloudy|space|nebula|sunset>".to_string();
            }
        };

        world.resources.graphics.atmosphere = atmosphere;
        format!("Atmosphere set to {}", args[0].to_lowercase())
    }
}

struct FovCmd;

impl<C> Command<C> for FovCmd {
    fn name(&self) -> &str {
        "fov"
    }

    fn description(&self) -> &str {
        "Get or set camera field of view in degrees"
    }

    fn usage(&self) -> &str {
        "fov [degrees]"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        let Some(camera_entity) = world.resources.active_camera else {
            return "No active camera".to_string();
        };

        let Some(camera) = world.get_camera_mut(camera_entity) else {
            return "Camera component not found".to_string();
        };

        let Projection::Perspective(perspective) = &mut camera.projection else {
            return "Camera is not using perspective projection".to_string();
        };

        if args.is_empty() {
            let fov_degrees = perspective.y_fov_rad.to_degrees();
            return format!("Current FOV: {:.1} degrees", fov_degrees);
        }

        let fov: f32 = match args[0].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid FOV value".to_string(),
        };

        if !(1.0..=179.0).contains(&fov) {
            return "FOV must be between 1 and 179 degrees".to_string();
        }

        perspective.y_fov_rad = fov.to_radians();
        format!("FOV set to {:.1} degrees", fov)
    }
}

struct TimescaleCmd;

impl<C> Command<C> for TimescaleCmd {
    fn name(&self) -> &str {
        "timescale"
    }

    fn description(&self) -> &str {
        "Get or set time scale (1.0 = normal, 0.5 = slow-mo, 2.0 = fast)"
    }

    fn usage(&self) -> &str {
        "timescale [value]"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.is_empty() {
            return format!(
                "Current timescale: {:.2}",
                world.resources.window.timing.time_speed
            );
        }

        let scale: f32 = match args[0].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid timescale value".to_string(),
        };

        if !(0.0..=10.0).contains(&scale) {
            return "Timescale must be between 0.0 and 10.0".to_string();
        }

        world.resources.window.timing.time_speed = scale;
        format!("Timescale set to {:.2}", scale)
    }
}

struct FpsCmd;

impl<C> Command<C> for FpsCmd {
    fn name(&self) -> &str {
        "fps"
    }

    fn description(&self) -> &str {
        "Show current frames per second"
    }

    fn usage(&self) -> &str {
        "fps"
    }

    fn execute(&self, _args: &[&str], world: &mut World, _context: &mut C) -> String {
        let timing = &world.resources.window.timing;
        format!(
            "FPS: {:.1} | Frame time: {:.2}ms | Uptime: {:.1}s",
            timing.frames_per_second,
            timing.delta_time * 1000.0,
            timing.uptime_milliseconds as f32 / 1000.0
        )
    }
}

struct QuitCmd;

impl<C> Command<C> for QuitCmd {
    fn name(&self) -> &str {
        "quit"
    }

    fn description(&self) -> &str {
        "Exit the application"
    }

    fn usage(&self) -> &str {
        "quit"
    }

    fn execute(&self, _args: &[&str], world: &mut World, _context: &mut C) -> String {
        world.resources.window.should_exit = true;
        "Exiting...".to_string()
    }
}

struct PosCmd;

impl<C> Command<C> for PosCmd {
    fn name(&self) -> &str {
        "pos"
    }

    fn description(&self) -> &str {
        "Show current camera position"
    }

    fn usage(&self) -> &str {
        "pos"
    }

    fn execute(&self, _args: &[&str], world: &mut World, _context: &mut C) -> String {
        let Some(camera_entity) = world.resources.active_camera else {
            return "No active camera".to_string();
        };

        let Some(transform) = world.get_local_transform(camera_entity) else {
            return "Camera transform not found".to_string();
        };

        format!(
            "Position: ({:.2}, {:.2}, {:.2})",
            transform.translation.x, transform.translation.y, transform.translation.z
        )
    }
}

struct TeleportCmd;

impl<C> Command<C> for TeleportCmd {
    fn name(&self) -> &str {
        "tp"
    }

    fn description(&self) -> &str {
        "Teleport camera to specified position"
    }

    fn usage(&self) -> &str {
        "tp <x> <y> <z>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.len() != 3 {
            return "Usage: tp <x> <y> <z>".to_string();
        }

        let Some(camera_entity) = world.resources.active_camera else {
            return "No active camera".to_string();
        };

        let x: f32 = match args[0].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid x coordinate".to_string(),
        };
        let y: f32 = match args[1].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid y coordinate".to_string(),
        };
        let z: f32 = match args[2].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid z coordinate".to_string(),
        };

        let Some(transform) = world.get_local_transform_mut(camera_entity) else {
            return "Camera transform not found".to_string();
        };

        transform.translation = Vec3::new(x, y, z);
        mark_local_transform_dirty(world, camera_entity);

        format!("Teleported to ({:.2}, {:.2}, {:.2})", x, y, z)
    }
}

struct UnlitCmd;

impl<C> Command<C> for UnlitCmd {
    fn name(&self) -> &str {
        "unlit"
    }

    fn description(&self) -> &str {
        "Toggle unlit rendering mode"
    }

    fn usage(&self) -> &str {
        "unlit [on|off]"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.is_empty() {
            let state = if world.resources.graphics.unlit_mode {
                "on"
            } else {
                "off"
            };
            return format!("Unlit mode is currently {}", state);
        }

        match args[0].to_lowercase().as_str() {
            "on" | "true" | "1" => {
                world.resources.graphics.unlit_mode = true;
                "Unlit mode enabled".to_string()
            }
            "off" | "false" | "0" => {
                world.resources.graphics.unlit_mode = false;
                "Unlit mode disabled".to_string()
            }
            _ => "Usage: unlit [on|off]".to_string(),
        }
    }
}

struct BloomCmd;

impl<C> Command<C> for BloomCmd {
    fn name(&self) -> &str {
        "bloom"
    }

    fn description(&self) -> &str {
        "Toggle bloom effect or set intensity"
    }

    fn usage(&self) -> &str {
        "bloom [on|off|<intensity>]"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.is_empty() {
            let state = if world.resources.graphics.bloom_enabled {
                format!(
                    "on (intensity: {:.2})",
                    world.resources.graphics.bloom_intensity
                )
            } else {
                "off".to_string()
            };
            return format!("Bloom is currently {}", state);
        }

        match args[0].to_lowercase().as_str() {
            "on" | "true" | "1" => {
                world.resources.graphics.bloom_enabled = true;
                "Bloom enabled".to_string()
            }
            "off" | "false" | "0" => {
                world.resources.graphics.bloom_enabled = false;
                "Bloom disabled".to_string()
            }
            _ => {
                if let Ok(intensity) = args[0].parse::<f32>() {
                    if (0.0..=5.0).contains(&intensity) {
                        world.resources.graphics.bloom_enabled = true;
                        world.resources.graphics.bloom_intensity = intensity;
                        return format!("Bloom intensity set to {:.2}", intensity);
                    }
                    return "Bloom intensity must be between 0.0 and 5.0".to_string();
                }
                "Usage: bloom [on|off|<intensity>]".to_string()
            }
        }
    }
}

struct RotateCmd;

impl<C> Command<C> for RotateCmd {
    fn name(&self) -> &str {
        "rotate"
    }

    fn description(&self) -> &str {
        "Rotate an entity by degrees on each axis"
    }

    fn usage(&self) -> &str {
        "rotate <entity> <x> <y> <z>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.len() != 4 {
            return "Usage: rotate <entity> <x> <y> <z>".to_string();
        }

        let Some(entity) = parse_entity(args[0]) else {
            return format!("Invalid entity format: {}", args[0]);
        };

        let x: f32 = match args[1].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid x rotation".to_string(),
        };
        let y: f32 = match args[2].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid y rotation".to_string(),
        };
        let z: f32 = match args[3].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid z rotation".to_string(),
        };

        let Some(transform) = world.get_local_transform_mut(entity) else {
            return "Entity not found or has no transform".to_string();
        };

        let rot_x = nalgebra_glm::quat_angle_axis(x.to_radians(), &Vec3::new(1.0, 0.0, 0.0));
        let rot_y = nalgebra_glm::quat_angle_axis(y.to_radians(), &Vec3::new(0.0, 1.0, 0.0));
        let rot_z = nalgebra_glm::quat_angle_axis(z.to_radians(), &Vec3::new(0.0, 0.0, 1.0));
        let rotation = rot_z * rot_y * rot_x;
        transform.rotation = rotation * transform.rotation;
        mark_local_transform_dirty(world, entity);

        format!("Rotated {} by ({}, {}, {}) degrees", args[0], x, y, z)
    }
}

struct ScaleCmd;

impl<C> Command<C> for ScaleCmd {
    fn name(&self) -> &str {
        "scale"
    }

    fn description(&self) -> &str {
        "Scale an entity uniformly or on each axis"
    }

    fn usage(&self) -> &str {
        "scale <entity> <factor> OR scale <entity> <x> <y> <z>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.len() < 2 {
            return "Usage: scale <entity> <factor> OR scale <entity> <x> <y> <z>".to_string();
        }

        let Some(entity) = parse_entity(args[0]) else {
            return format!("Invalid entity format: {}", args[0]);
        };

        let scale = if args.len() == 2 {
            let factor: f32 = match args[1].parse() {
                Ok(value) => value,
                Err(_) => return "Invalid scale factor".to_string(),
            };
            Vec3::new(factor, factor, factor)
        } else if args.len() == 4 {
            let x: f32 = match args[1].parse() {
                Ok(value) => value,
                Err(_) => return "Invalid x scale".to_string(),
            };
            let y: f32 = match args[2].parse() {
                Ok(value) => value,
                Err(_) => return "Invalid y scale".to_string(),
            };
            let z: f32 = match args[3].parse() {
                Ok(value) => value,
                Err(_) => return "Invalid z scale".to_string(),
            };
            Vec3::new(x, y, z)
        } else {
            return "Usage: scale <entity> <factor> OR scale <entity> <x> <y> <z>".to_string();
        };

        let new_scale = {
            let Some(transform) = world.get_local_transform_mut(entity) else {
                return "Entity not found or has no transform".to_string();
            };
            transform.scale = transform.scale.component_mul(&scale);
            transform.scale
        };
        mark_local_transform_dirty(world, entity);

        format!(
            "Scaled {} to ({:.2}, {:.2}, {:.2})",
            args[0], new_scale.x, new_scale.y, new_scale.z
        )
    }
}

struct MoveCmd;

impl<C> Command<C> for MoveCmd {
    fn name(&self) -> &str {
        "move"
    }

    fn description(&self) -> &str {
        "Move an entity by offset or to absolute position"
    }

    fn usage(&self) -> &str {
        "move <entity> <x> <y> <z>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.len() != 4 {
            return "Usage: move <entity> <x> <y> <z>".to_string();
        }

        let Some(entity) = parse_entity(args[0]) else {
            return format!("Invalid entity format: {}", args[0]);
        };

        let x: f32 = match args[1].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid x position".to_string(),
        };
        let y: f32 = match args[2].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid y position".to_string(),
        };
        let z: f32 = match args[3].parse() {
            Ok(value) => value,
            Err(_) => return "Invalid z position".to_string(),
        };

        let Some(transform) = world.get_local_transform_mut(entity) else {
            return "Entity not found or has no transform".to_string();
        };

        transform.translation = Vec3::new(x, y, z);
        mark_local_transform_dirty(world, entity);

        format!("Moved {} to ({:.2}, {:.2}, {:.2})", args[0], x, y, z)
    }
}

struct DeleteCmd;

impl<C> Command<C> for DeleteCmd {
    fn name(&self) -> &str {
        "delete"
    }

    fn description(&self) -> &str {
        "Delete an entity from the world"
    }

    fn usage(&self) -> &str {
        "delete <entity>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.is_empty() {
            return "Usage: delete <entity>".to_string();
        }

        let Some(entity) = parse_entity(args[0]) else {
            return format!("Invalid entity format: {}", args[0]);
        };

        if world.get_local_transform(entity).is_none() {
            return "Entity not found".to_string();
        }

        world.despawn_entities(&[entity]);
        format!("Deleted {}", args[0])
    }
}

struct InspectCmd;

impl<C> Command<C> for InspectCmd {
    fn name(&self) -> &str {
        "inspect"
    }

    fn description(&self) -> &str {
        "Show information about an entity"
    }

    fn usage(&self) -> &str {
        "inspect <entity>"
    }

    fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
        if args.is_empty() {
            return "Usage: inspect <entity>".to_string();
        }

        let Some(entity) = parse_entity(args[0]) else {
            return format!("Invalid entity format: {}", args[0]);
        };

        let mut info = format!("Entity: {}\n", args[0]);

        if let Some(name) = world.get_name(entity) {
            info.push_str(&format!("Name: {}\n", name.0));
        }

        if let Some(transform) = world.get_local_transform(entity) {
            info.push_str(&format!(
                "Position: ({:.2}, {:.2}, {:.2})\n",
                transform.translation.x, transform.translation.y, transform.translation.z
            ));
            info.push_str(&format!(
                "Scale: ({:.2}, {:.2}, {:.2})\n",
                transform.scale.x, transform.scale.y, transform.scale.z
            ));
        } else {
            return "Entity not found".to_string();
        }

        if let Some(visibility) = world.get_visibility(entity) {
            info.push_str(&format!("Visible: {}\n", visibility.visible));
        }

        info
    }
}

pub fn shell_immediate_ui<C>(shell: &mut ShellState<C>, ui: &mut ImmediateUi, world: &mut World) {
    if !shell.should_render() {
        return;
    }

    let screen_width = ui.screen_size.x;
    let screen_height = ui.screen_size.y;
    let min_height = 100.0;
    let max_height = screen_height * 0.9;
    shell.height = shell.height.clamp(min_height, max_height);
    let shell_height = shell.height;
    let current_height = shell_height * shell.animation_progress;

    let resize_handle_height = 8.0;
    let resize_area = Rect::new(
        0.0,
        current_height - resize_handle_height,
        screen_width,
        resize_handle_height * 2.0,
    );
    let mouse_in_resize = resize_area.contains(ui.mouse_pos) && shell.animation_progress > 0.95;

    if mouse_in_resize && ui.mouse_just_pressed && !shell.dragging_resize {
        shell.dragging_resize = true;
        shell.drag_start_y = ui.mouse_pos.y;
        shell.drag_start_height = shell.height;
    }

    if shell.dragging_resize {
        if ui.mouse_down {
            let delta = ui.mouse_pos.y - shell.drag_start_y;
            shell.height = (shell.drag_start_height + delta).clamp(min_height, max_height);
        } else {
            shell.dragging_resize = false;
        }
    }

    let bg_color = Vec4::new(0.06, 0.06, 0.08, 0.95);
    let header_color = Vec4::new(0.39, 0.78, 0.39, 1.0);
    let hint_color = Vec4::new(0.47, 0.47, 0.47, 1.0);
    let command_color = Vec4::new(0.59, 0.78, 1.0, 1.0);
    let output_color = Vec4::new(0.78, 0.78, 0.78, 1.0);
    let border_color = Vec4::new(0.4, 0.4, 0.45, 1.0);

    ui.set_layer(UiLayer::Tooltips);

    let panel_clip = Rect::new(0.0, 0.0, screen_width, current_height);
    ui.push_clip(panel_clip);

    ui.add_rect_raw(crate::render::wgpu::passes::geometry::UiRect {
        position: Vec2::new(0.0, 0.0),
        size: Vec2::new(screen_width, current_height),
        color: bg_color,
        corner_radius: 0.0,
        border_width: 0.0,
        border_color: Vec4::new(0.0, 0.0, 0.0, 0.0),
        clip_rect: Some(panel_clip),
        layer: UiLayer::Tooltips,
        z_index: 0,
    });

    let padding = 10.0;
    let font_size = 14.0;
    let line_height = font_size + 6.0;
    let header_height = 24.0;
    let input_height = 28.0;
    let separator_height = 1.0;

    let header_y = 4.0;
    ui.add_text_raw(
        "Console",
        Vec2::new(padding, header_y),
        crate::ecs::text::components::TextProperties {
            font_size,
            color: header_color,
            ..Default::default()
        },
        Some(panel_clip),
        UiLayer::Tooltips,
        1,
    );

    let hint_text = "Alt+C to close";
    let hint_width = hint_text.len() as f32 * font_size * 0.5;
    ui.add_text_raw(
        hint_text,
        Vec2::new(screen_width - hint_width - padding, header_y + 2.0),
        crate::ecs::text::components::TextProperties {
            font_size: font_size * 0.85,
            color: hint_color,
            ..Default::default()
        },
        Some(panel_clip),
        UiLayer::Tooltips,
        1,
    );

    let top_separator_y = header_height;
    ui.add_rect_raw(crate::render::wgpu::passes::geometry::UiRect {
        position: Vec2::new(0.0, top_separator_y),
        size: Vec2::new(screen_width, separator_height),
        color: border_color,
        corner_radius: 0.0,
        border_width: 0.0,
        border_color: Vec4::new(0.0, 0.0, 0.0, 0.0),
        clip_rect: Some(panel_clip),
        layer: UiLayer::Tooltips,
        z_index: 1,
    });

    let bottom_separator_y = current_height - input_height - separator_height;
    ui.add_rect_raw(crate::render::wgpu::passes::geometry::UiRect {
        position: Vec2::new(0.0, bottom_separator_y),
        size: Vec2::new(screen_width, separator_height),
        color: border_color,
        corner_radius: 0.0,
        border_width: 0.0,
        border_color: Vec4::new(0.0, 0.0, 0.0, 0.0),
        clip_rect: Some(panel_clip),
        layer: UiLayer::Tooltips,
        z_index: 1,
    });

    let output_top = top_separator_y + separator_height + 2.0;
    let output_bottom = bottom_separator_y - 2.0;
    let output_height = (output_bottom - output_top).max(0.0);
    let output_clip = Rect::new(0.0, output_top, screen_width, output_height);

    let mut total_content_height = 0.0;
    for line in &shell.output {
        let line_count = line.text.lines().count().max(1);
        total_content_height += line_count as f32 * line_height;
    }

    if shell.scroll_to_bottom {
        shell.scroll_offset = (total_content_height - output_height).max(0.0);
        shell.scroll_to_bottom = false;
    }

    shell.scroll_offset = shell
        .scroll_offset
        .clamp(0.0, (total_content_height - output_height).max(0.0));

    if output_height > 0.0 {
        let mut y_pos = output_top - shell.scroll_offset;
        for line in &shell.output {
            let color = if line.is_command {
                command_color
            } else {
                output_color
            };

            for text_line in line.text.lines() {
                if y_pos + line_height > output_top && y_pos < output_bottom {
                    ui.add_text_raw(
                        text_line,
                        Vec2::new(padding, y_pos),
                        crate::ecs::text::components::TextProperties {
                            font_size,
                            color,
                            ..Default::default()
                        },
                        Some(output_clip),
                        UiLayer::Tooltips,
                        2,
                    );
                }
                y_pos += line_height;
            }
        }
    }

    let input_y = current_height - input_height + (input_height - font_size) * 0.5;

    ui.add_text_raw(
        ">",
        Vec2::new(padding, input_y),
        crate::ecs::text::components::TextProperties {
            font_size,
            color: header_color,
            ..Default::default()
        },
        Some(panel_clip),
        UiLayer::Tooltips,
        2,
    );

    let cursor = if shell.visible && shell.animation_progress > 0.9 {
        "_"
    } else {
        ""
    };
    let input_display = format!("{}{}", shell.input_buffer, cursor);
    ui.add_text_raw(
        &input_display,
        Vec2::new(padding + font_size + 4.0, input_y),
        crate::ecs::text::components::TextProperties {
            font_size,
            color: output_color,
            ..Default::default()
        },
        Some(panel_clip),
        UiLayer::Tooltips,
        2,
    );

    ui.add_rect_raw(crate::render::wgpu::passes::geometry::UiRect {
        position: Vec2::new(0.0, current_height - 2.0),
        size: Vec2::new(screen_width, 2.0),
        color: border_color,
        corner_radius: 0.0,
        border_width: 0.0,
        border_color: Vec4::new(0.0, 0.0, 0.0, 0.0),
        clip_rect: Some(panel_clip),
        layer: UiLayer::Tooltips,
        z_index: 3,
    });

    ui.pop_clip();

    if shell.animation_progress > 0.95 {
        let handle_color = if shell.dragging_resize || mouse_in_resize {
            Vec4::new(0.6, 0.8, 1.0, 1.0)
        } else {
            Vec4::new(0.5, 0.5, 0.55, 0.8)
        };
        let handle_width = 40.0;
        let handle_height = 3.0;
        ui.add_rect_raw(crate::render::wgpu::passes::geometry::UiRect {
            position: Vec2::new((screen_width - handle_width) * 0.5, current_height + 4.0),
            size: Vec2::new(handle_width, handle_height),
            color: handle_color,
            corner_radius: 1.5,
            border_width: 0.0,
            border_color: Vec4::new(0.0, 0.0, 0.0, 0.0),
            clip_rect: None,
            layer: UiLayer::Tooltips,
            z_index: 10,
        });
    }

    if shell.visible && shell.animation_progress > 0.9 {
        if shell.pending_enter {
            shell.pending_enter = false;
            shell.execute_command(world);
        }

        if shell.pending_up {
            shell.pending_up = false;
            shell.history_up();
        }

        if shell.pending_down {
            shell.pending_down = false;
            shell.history_down();
        }

        if shell.pending_escape {
            shell.pending_escape = false;
            shell.visible = false;
        }
    }

    ui.set_layer(UiLayer::Background);
}

#[cfg(feature = "egui")]
pub fn shell_ui<C>(shell: &mut ShellState<C>, ui_context: &egui::Context, world: &mut World) {
    if !shell.should_render() {
        return;
    }

    let screen_rect = ui_context.input(|i| i.viewport_rect());
    let shell_height = shell.height.min(screen_rect.height() * 0.9);
    let current_height = shell_height * shell.animation_progress;

    let panel_rect = egui::Rect::from_min_size(
        egui::pos2(0.0, 0.0),
        egui::vec2(screen_rect.width(), current_height),
    );

    egui::Area::new(egui::Id::new("shell_console"))
        .fixed_pos(egui::pos2(0.0, 0.0))
        .order(egui::Order::Foreground)
        .show(ui_context, |ui| {
            ui.set_clip_rect(panel_rect);

            egui::Frame::new()
                .fill(egui::Color32::from_rgba_unmultiplied(15, 15, 20, 230))
                .show(ui, |ui| {
                    ui.set_min_size(egui::vec2(screen_rect.width(), shell_height));
                    ui.set_max_size(egui::vec2(screen_rect.width(), shell_height));

                    ui.vertical(|ui| {
                        ui.add_space(4.0);

                        ui.horizontal(|ui| {
                            ui.add_space(8.0);
                            ui.label(
                                egui::RichText::new("Console")
                                    .color(egui::Color32::from_rgb(100, 200, 100))
                                    .strong(),
                            );
                            ui.with_layout(
                                egui::Layout::right_to_left(egui::Align::Center),
                                |ui| {
                                    ui.add_space(8.0);
                                    ui.label(
                                        egui::RichText::new("Alt+C or Escape to close")
                                            .color(egui::Color32::from_rgb(120, 120, 120))
                                            .small(),
                                    );
                                },
                            );
                        });

                        ui.add_space(2.0);
                        ui.separator();

                        let available_height = shell_height - 70.0;
                        egui::ScrollArea::vertical()
                            .max_height(available_height)
                            .auto_shrink([false, false])
                            .stick_to_bottom(shell.scroll_to_bottom)
                            .show(ui, |ui| {
                                ui.add_space(4.0);
                                for line in &shell.output {
                                    ui.horizontal(|ui| {
                                        ui.add_space(8.0);
                                        let color = if line.is_command {
                                            egui::Color32::from_rgb(150, 200, 255)
                                        } else {
                                            egui::Color32::from_rgb(200, 200, 200)
                                        };
                                        ui.label(
                                            egui::RichText::new(&line.text)
                                                .color(color)
                                                .family(egui::FontFamily::Monospace),
                                        );
                                    });
                                }
                                ui.add_space(4.0);
                            });

                        shell.scroll_to_bottom = false;

                        ui.separator();

                        ui.horizontal(|ui| {
                            ui.add_space(8.0);
                            ui.label(
                                egui::RichText::new(">")
                                    .color(egui::Color32::from_rgb(100, 200, 100))
                                    .family(egui::FontFamily::Monospace),
                            );

                            let response = ui.add(
                                egui::TextEdit::singleline(&mut shell.input_buffer)
                                    .desired_width(screen_rect.width() - 40.0)
                                    .font(egui::FontId::monospace(14.0))
                                    .text_color(egui::Color32::from_rgb(220, 220, 220))
                                    .frame(false),
                            );

                            if shell.visible && shell.animation_progress > 0.9 {
                                response.request_focus();
                            }

                            if response.has_focus() {
                                if ui.input(|i| i.key_pressed(egui::Key::Enter)) {
                                    shell.execute_command(world);
                                }

                                if ui.input(|i| i.key_pressed(egui::Key::ArrowUp)) {
                                    shell.history_up();
                                }

                                if ui.input(|i| i.key_pressed(egui::Key::ArrowDown)) {
                                    shell.history_down();
                                }

                                if ui.input(|i| i.key_pressed(egui::Key::Escape)) {
                                    shell.visible = false;
                                }

                                if ui.input(|i| i.modifiers.alt && i.key_pressed(egui::Key::C)) {
                                    shell.visible = false;
                                }
                            }
                        });

                        ui.add_space(4.0);
                    });
                });
        });
}
