use std::fmt;

use super::command::CommandClone;

pub struct CommandRegistry {
    commands: std::collections::HashMap<String, Box<dyn super::Command>>,
    aliases: std::collections::HashMap<String, String>,
}

impl Default for CommandRegistry {
    fn default() -> Self {
        Self {
            commands: std::collections::HashMap::new(),
            aliases: std::collections::HashMap::new(),
        }
    }
}

impl CommandRegistry {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn register<C: super::Command + 'static>(&mut self, command: C) -> bool {
        let name = command.name().to_lowercase();
        if self.commands.contains_key(&name) {
            return false;
        }
        self.commands.insert(name, Box::new(command));
        true
    }

    pub fn register_with_alias<C: super::Command + 'static>(
        &mut self,
        command: C,
        alias: &str,
    ) -> bool {
        let name = command.name().to_lowercase();
        if self.commands.contains_key(&name) {
            return false;
        }
        self.commands.insert(name.clone(), Box::new(command));
        self.aliases.insert(alias.to_lowercase(), name);
        true
    }

    pub fn unregister(&mut self, name: &str) -> bool {
        let name = name.to_lowercase();
        self.aliases.retain(|_, v| *v != name);
        self.commands.remove(&name).is_some()
    }

    pub fn get(&self, name: &str) -> Option<&dyn super::Command> {
        let name = name.to_lowercase();
        if let Some(cmd) = self.commands.get(&name) {
            Some(cmd.as_ref())
        } else if let Some(real_name) = self.aliases.get(&name) {
            self.commands.get(real_name).map(|c| c.as_ref())
        } else {
            None
        }
    }

    pub fn get_mut(&mut self, name: &str) -> Option<&mut (dyn super::Command + '_)> {
        let name = name.to_lowercase();
        let key = if self.commands.contains_key(&name) {
            name
        } else {
            self.aliases.get(&name)?.clone()
        };
        match self.commands.get_mut(&key) {
            Some(cmd) => Some(cmd.as_mut()),
            None => None,
        }
    }

    pub fn command_exists(&self, name: &str) -> bool {
        let name = name.to_lowercase();
        self.commands.contains_key(&name) || self.aliases.contains_key(&name)
    }

    pub fn get_command_names(&self) -> Vec<&str> {
        self.commands.keys().map(|s| s.as_str()).collect()
    }

    pub fn add_alias(&mut self, alias: &str, command_name: &str) -> bool {
        if self.commands.contains_key(&command_name.to_lowercase()) {
            self.aliases.insert(alias.to_lowercase(), command_name.to_lowercase());
            true
        } else {
            false
        }
    }

    pub fn remove_alias(&mut self, alias: &str) -> bool {
        self.aliases.remove(&alias.to_lowercase()).is_some()
    }

    pub fn get_aliases(&self) -> Vec<(&str, &str)> {
        self.aliases
            .iter()
            .map(|(alias, cmd)| (alias.as_str(), cmd.as_str()))
            .collect()
    }
}

impl fmt::Display for CommandRegistry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "CommandRegistry(commands={}, aliases={})", self.commands.len(), self.aliases.len())
    }
}

pub struct CommandManager {
    registry: CommandRegistry,
    current_command: Option<Box<dyn super::Command>>,
    context: super::CommandContext<'static>,
    command_stack: Vec<Box<dyn super::Command>>,
    is_recording_macro: bool,
    macro_commands: Vec<Box<dyn super::Command>>,
}

impl Default for CommandManager {
    fn default() -> Self {
        Self {
            registry: CommandRegistry::new(),
            current_command: None,
            context: super::CommandContext::default(),
            command_stack: Vec::new(),
            is_recording_macro: false,
            macro_commands: Vec::new(),
        }
    }
}

impl CommandManager {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn register_command<C: super::Command + 'static>(&mut self, command: C) -> bool {
        self.registry.register(command)
    }

    pub fn unregister_command(&mut self, name: &str) -> bool {
        self.registry.unregister(name)
    }

    pub fn execute_command(
        &mut self,
        name: &str,
        context: &mut super::CommandContext,
    ) -> super::CommandResult {
        if let Some(cmd) = self.registry.get(name) {
            self.current_command = Some(self.registry.get(name).unwrap().clone_command());
            let result = cmd.execute(context);
            if result.is_success() && self.is_recording_macro {
                if let Some(current) = &self.current_command {
                    self.macro_commands.push(current.clone_command());
                }
            }
            result
        } else {
            super::CommandResult::Failed(format!("Command '{}' not found", name))
        }
    }

    pub fn start_command(&mut self, name: &str) -> super::CommandResult {
        if let Some(cmd) = self.registry.get(name) {
            self.current_command = Some(cmd.clone_command());
            super::CommandResult::Success
        } else {
            super::CommandResult::Failed(format!("Command '{}' not found", name))
        }
    }

    pub fn continue_command(
        &mut self,
        input: &str,
    ) -> super::CommandResult {
        if let Some(ref mut cmd) = self.current_command {
            cmd.receive_input(input)
        } else {
            super::CommandResult::Failed("No active command".to_string())
        }
    }

    pub fn end_command(&mut self) -> super::CommandResult {
        self.current_command = None;
        super::CommandResult::Success
    }

    pub fn cancel_command(&mut self) {
        self.current_command = None;
        self.command_stack.clear();
    }

    pub fn push_command(&mut self, name: &str) -> super::CommandResult {
        if let Some(cmd) = self.registry.get(name) {
            self.command_stack.push(cmd.clone_command());
            if self.current_command.is_none() {
                self.current_command = Some(self.command_stack.last().unwrap().clone_command());
            }
            super::CommandResult::Success
        } else {
            super::CommandResult::Failed(format!("Command '{}' not found", name))
        }
    }

    pub fn pop_command(&mut self) -> Option<Box<dyn super::Command>> {
        let cmd = self.command_stack.pop();
        self.current_command = self.command_stack.last().cloned();
        cmd
    }

    pub fn get_current_command(&self) -> Option<&dyn super::Command> {
        self.current_command.as_deref()
    }

    pub fn start_macro_recording(&mut self) {
        self.is_recording_macro = true;
        self.macro_commands.clear();
    }

    pub fn stop_macro_recording(&mut self) -> Vec<Box<dyn super::Command>> {
        self.is_recording_macro = false;
        self.macro_commands.clone()
    }

    pub fn is_recording_macro(&self) -> bool {
        self.is_recording_macro
    }

    pub fn get_registered_commands(&self) -> Vec<&str> {
        self.registry.get_command_names()
    }

    pub fn command_exists(&self, name: &str) -> bool {
        self.registry.command_exists(name)
    }
}

impl super::CommandClone for Box<dyn super::Command> {
    fn clone_command(&self) -> Box<dyn super::Command> {
        (**self).clone_command()
    }
}

impl super::Command for Box<dyn super::Command> {
    fn name(&self) -> &str {
        (**self).name()
    }

    fn description(&self) -> &str {
        (**self).description()
    }

    fn execute(&self, context: &mut super::CommandContext) -> super::CommandResult {
        (**self).execute(context)
    }

    fn undo(&self, context: &mut super::CommandContext) -> super::CommandResult {
        (**self).undo(context)
    }

    fn preview(&self, context: &super::CommandContext) -> Option<super::super::data_structure::Entity> {
        (**self).preview(context)
    }

    fn requires_selection(&self) -> bool {
        (**self).requires_selection()
    }

    fn get_required_entity_types(&self) -> &[&'static str] {
        (**self).get_required_entity_types()
    }

    fn is_undoable(&self) -> bool {
        (**self).is_undoable()
    }
}

impl Clone for Box<dyn super::Command> {
    fn clone(&self) -> Self {
        self.clone_command()
    }
}

impl fmt::Display for CommandManager {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "CommandManager(commands={}, current={})",
            self.registry.get_command_names().len(),
            self.current_command.as_ref().map(|c| c.name()).unwrap_or("None")
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_command_registry() {
        let mut registry = CommandRegistry::new();

        struct TestCommand;
        impl super::super::CommandClone for TestCommand {
            fn clone_command(&self) -> Box<dyn super::super::Command> { Box::new(TestCommand) }
        }

        impl super::super::Command for TestCommand {
            fn name(&self) -> &str { "test" }
            fn description(&self) -> &str { "Test command" }
            fn execute(&self, _: &mut super::super::CommandContext) -> super::super::CommandResult {
                super::super::CommandResult::Success
            }
            fn undo(&self, _: &mut super::super::CommandContext) -> super::super::CommandResult {
                super::super::CommandResult::Success
            }
            fn preview(&self, _: &super::super::CommandContext) -> Option<super::super::super::data_structure::Entity> { None }
            fn requires_selection(&self) -> bool { false }
            fn get_required_entity_types(&self) -> &[&'static str] { &[] }
            fn is_undoable(&self) -> bool { true }
        }

        assert!(registry.register(TestCommand));
        assert!(!registry.register(TestCommand));

        assert!(registry.command_exists("test"));
        assert!(registry.get("test").is_some());

        assert_eq!(registry.get_command_names(), vec!["test"]);
    }

    #[test]
    fn test_alias() {
        let mut registry = CommandRegistry::new();

        struct TestCommand;
        impl super::super::CommandClone for TestCommand {
            fn clone_command(&self) -> Box<dyn super::super::Command> { Box::new(TestCommand) }
        }

        impl super::super::Command for TestCommand {
            fn name(&self) -> &str { "test" }
            fn description(&self) -> &str { "Test command" }
            fn execute(&self, _: &mut super::super::CommandContext) -> super::super::CommandResult {
                super::super::CommandResult::Success
            }
            fn undo(&self, _: &mut super::super::CommandContext) -> super::super::CommandResult {
                super::super::CommandResult::Success
            }
            fn preview(&self, _: &super::super::CommandContext) -> Option<super::super::super::data_structure::Entity> { None }
            fn requires_selection(&self) -> bool { false }
            fn get_required_entity_types(&self) -> &[&'static str] { &[] }
            fn is_undoable(&self) -> bool { true }
        }

        registry.register_with_alias(TestCommand, "t");
        assert!(registry.command_exists("t"));
        assert!(registry.get("t").is_some());
    }
}