use std::fmt;

pub trait Command {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn execute(&self, context: &mut CommandContext) -> CommandResult;
    fn undo(&self, context: &mut CommandContext) -> CommandResult;
    fn preview(&self, context: &CommandContext) -> Option<super::super::data_structure::Entity>;
    fn requires_selection(&self) -> bool;
    fn get_required_entity_types(&self) -> &[&'static str];
    fn is_undoable(&self) -> bool;
}

#[derive(Debug, Clone, PartialEq)]
pub enum CommandResult {
    Success,
    Failed(String),
    Canceled,
    RequireInput(String),
}

impl CommandResult {
    pub fn is_success(&self) -> bool {
        matches!(self, CommandResult::Success)
    }

    pub fn is_failed(&self) -> bool {
        matches!(self, CommandResult::Failed(_))
    }

    pub fn is_canceled(&self) -> bool {
        matches!(self, CommandResult::Canceled)
    }

    pub fn error_message(&self) -> Option<&str> {
        match self {
            CommandResult::Failed(msg) => Some(msg),
            _ => None,
        }
    }
}

impl fmt::Display for CommandResult {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CommandResult::Success => write!(f, "Success"),
            CommandResult::Failed(msg) => write!(f, "Failed: {}", msg),
            CommandResult::Canceled => write!(f, "Canceled"),
            CommandResult::RequireInput(prompt) => write!(f, "Require Input: {}", prompt),
        }
    }
}

pub struct CommandContext {
    pub document: *mut super::super::data_structure::Document,
    pub active_layer: String,
    pub current_ucs: crate::math::Matrix4,
    pub selection_set: super::selection::SelectionSet,
    pub active_block: String,
    pub viewport: Option<super::render::viewport::Viewport>,
    pub current_point: Option<crate::geometry::Point>,
    pub user_data: std::collections::HashMap<String, Box<dyn std::any::Any>>,
}

impl Default for CommandContext {
    fn default() -> Self {
        Self {
            document: std::ptr::null_mut(),
            active_layer: "0".to_string(),
            current_ucs: crate::math::Matrix4::identity(),
            selection_set: super::selection::SelectionSet::new(),
            active_block: "ModelSpace".to_string(),
            viewport: None,
            current_point: None,
            user_data: std::collections::HashMap::new(),
        }
    }
}

impl CommandContext {
    pub fn new(document: &mut super::super::data_structure::Document) -> Self {
        Self {
            document: document as *mut _ as *mut _,
            active_layer: document.active_layer.clone(),
            current_ucs: crate::math::Matrix4::identity(),
            selection_set: super::selection::SelectionSet::new(),
            active_block: document.active_block.clone(),
            viewport: None,
            current_point: None,
            user_data: std::collections::HashMap::new(),
        }
    }

    pub fn get_document(&self) -> Option<&super::super::data_structure::Document> {
        if self.document.is_null() {
            None
        } else {
            unsafe { Some(&*self.document) }
        }
    }

    pub fn get_document_mut(&mut self) -> Option<&mut super::super::data_structure::Document> {
        if self.document.is_null() {
            None
        } else {
            unsafe { Some(&mut *self.document) }
        }
    }

    pub fn set_user_data<T: 'static>(&mut self, key: impl Into<String>, value: T) {
        self.user_data.insert(key.into(), Box::new(value));
    }

    pub fn get_user_data<T: 'static>(&self, key: &str) -> Option<&T> {
        self.user_data.get(key).and_then(|boxed| {
            boxed.downcast_ref::<T>()
        })
    }

    pub fn take_user_data<T: 'static>(&mut self, key: &str) -> Option<Box<T>> {
        self.user_data.remove(key).and_then(|boxed| {
            boxed.downcast::<T>().ok()
        })
    }

    pub fn add_selection(&mut self, entity_id: super::super::data_structure::ObjectId) {
        self.selection_set.add(entity_id);
    }

    pub fn clear_selection(&mut self) {
        self.selection_set.clear();
    }

    pub fn get_selected_entities(&self) -> Vec<super::super::data_structure::ObjectId> {
        self.selection_set.get_selected().to_vec()
    }
}

pub struct CommandBuilder {
    name: String,
    description: String,
    command_type: CommandType,
}

impl Default for CommandBuilder {
    fn default() -> Self {
        Self {
            name: String::new(),
            description: String::new(),
            command_type: CommandType::EntityOperation,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CommandType {
    EntityOperation,
    DrawingAid,
    DisplayControl,
    FileOperation,
    LayerControl,
    BlockOperation,
    Dimension,
    Text,
    Selection,
    Unknown,
}

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

    pub fn name(mut self, name: impl Into<String>) -> Self {
        self.name = name.into();
        self
    }

    pub fn description(mut self, desc: impl Into<String>) -> Self {
        self.description = desc.into();
        self
    }

    pub fn command_type(mut self, cmd_type: CommandType) -> Self {
        self.command_type = cmd_type;
        self
    }

    pub fn build(self) -> Result<Box<dyn Command>, crate::error::CadError> {
        use crate::error::CadError;

        if self.name.is_empty() {
            return Err(CadError::command("build", "命令名称不能为空"));
        }

        if self.description.is_empty() {
            return Err(CadError::command(&self.name, "命令描述不能为空"));
        }

        match self.command_type {
            CommandType::EntityOperation => {
                Ok(Box::new(EntityCommand::new(self.name, self.description)))
            }
            CommandType::DrawingAid => {
                Ok(Box::new(DrawingAidCommand::new(self.name, self.description)))
            }
            CommandType::DisplayControl => {
                Ok(Box::new(DisplayControlCommand::new(self.name, self.description)))
            }
            CommandType::FileOperation => {
                Ok(Box::new(FileOperationCommand::new(self.name, self.description)))
            }
            CommandType::LayerControl => {
                Ok(Box::new(LayerControlCommand::new(self.name, self.description)))
            }
            CommandType::BlockOperation => {
                Ok(Box::new(BlockOperationCommand::new(self.name, self.description)))
            }
            CommandType::Dimension => {
                Ok(Box::new(DimensionCommand::new(self.name, self.description)))
            }
            CommandType::Text => {
                Ok(Box::new(TextCommand::new(self.name, self.description)))
            }
            CommandType::Selection => {
                Ok(Box::new(SelectionCommand::new(self.name, self.description)))
            }
            CommandType::Unknown => {
                Ok(Box::new(GenericCommand::new(self.name, self.description,
                    |_| CommandResult::Failed("命令未实现".to_string()),
                    |_| CommandResult::Failed("无法撤销未实现的命令".to_string())
                )))
            }
        }
    }
}

struct EntityCommand {
    name: String,
    description: String,
}

impl EntityCommand {
    fn new(name: String, description: String) -> Self {
        Self { name, description }
    }
}

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

struct DrawingAidCommand {
    name: String,
    description: String,
}

impl DrawingAidCommand {
    fn new(name: String, description: String) -> Self {
        Self { name, description }
    }
}

impl Command for DrawingAidCommand {
    fn name(&self) -> &str { &self.name }
    fn description(&self) -> &str { &self.description }
    fn execute(&self, _: &mut CommandContext) -> CommandResult { CommandResult::Success }
    fn undo(&self, _: &mut CommandContext) -> CommandResult { CommandResult::Success }
    fn preview(&self, _: &CommandContext) -> Option<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 { false }
}

struct DisplayControlCommand {
    name: String,
    description: String,
}

impl DisplayControlCommand {
    fn new(name: String, description: String) -> Self {
        Self { name, description }
    }
}

impl Command for DisplayControlCommand {
    fn name(&self) -> &str { &self.name }
    fn description(&self) -> &str { &self.description }
    fn execute(&self, _: &mut CommandContext) -> CommandResult { CommandResult::Success }
    fn undo(&self, _: &mut CommandContext) -> CommandResult { CommandResult::Success }
    fn preview(&self, _: &CommandContext) -> Option<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 { false }
}

struct FileOperationCommand {
    name: String,
    description: String,
}

impl FileOperationCommand {
    fn new(name: String, description: String) -> Self {
        Self { name, description }
    }
}

impl Command for FileOperationCommand {
    fn name(&self) -> &str { &self.name }
    fn description(&self) -> &str { &self.description }
    fn execute(&self, _: &mut CommandContext) -> CommandResult { CommandResult::Success }
    fn undo(&self, _: &mut CommandContext) -> CommandResult { CommandResult::Success }
    fn preview(&self, _: &CommandContext) -> Option<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 { false }
}

struct LayerControlCommand {
    name: String,
    description: String,
}

impl LayerControlCommand {
    fn new(name: String, description: String) -> Self {
        Self { name, description }
    }
}

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

struct BlockOperationCommand {
    name: String,
    description: String,
}

impl BlockOperationCommand {
    fn new(name: String, description: String) -> Self {
        Self { name, description }
    }
}

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

struct DimensionCommand {
    name: String,
    description: String,
}

impl DimensionCommand {
    fn new(name: String, description: String) -> Self {
        Self { name, description }
    }
}

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

struct TextCommand {
    name: String,
    description: String,
}

impl TextCommand {
    fn new(name: String, description: String) -> Self {
        Self { name, description }
    }
}

impl Command for TextCommand {
    fn name(&self) -> &str { &self.name }
    fn description(&self) -> &str { &self.description }
    fn execute(&self, _: &mut CommandContext) -> CommandResult { CommandResult::Success }
    fn undo(&self, _: &mut CommandContext) -> CommandResult { CommandResult::Success }
    fn preview(&self, _: &CommandContext) -> Option<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 }
}

struct SelectionCommand {
    name: String,
    description: String,
}

impl SelectionCommand {
    fn new(name: String, description: String) -> Self {
        Self { name, description }
    }
}

impl Command for SelectionCommand {
    fn name(&self) -> &str { &self.name }
    fn description(&self) -> &str { &self.description }
    fn execute(&self, _: &mut CommandContext) -> CommandResult { CommandResult::Success }
    fn undo(&self, _: &mut CommandContext) -> CommandResult { CommandResult::Success }
    fn preview(&self, _: &CommandContext) -> Option<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 { false }
}

pub struct GenericCommand {
    name: String,
    description: String,
    execute_fn: Box<dyn Fn(&mut CommandContext) -> CommandResult + Send + Sync>,
    undo_fn: Box<dyn Fn(&mut CommandContext) -> CommandResult + Send + Sync>,
    requires_selection: bool,
    required_entity_types: Vec<&'static str>,
}

impl GenericCommand {
    pub fn new(
        name: impl Into<String>,
        description: impl Into<String>,
        execute_fn: impl Fn(&mut CommandContext) -> CommandResult + 'static + Send + Sync,
        undo_fn: impl Fn(&mut CommandContext) -> CommandResult + 'static + Send + Sync,
    ) -> Self {
        Self {
            name: name.into(),
            description: description.into(),
            execute_fn: Box::new(execute_fn),
            undo_fn: Box::new(undo_fn),
            requires_selection: false,
            required_entity_types: Vec::new(),
        }
    }

    pub fn with_selection_requirement(mut self, required: bool, entity_types: Vec<&'static str>) -> Self {
        self.requires_selection = required;
        self.required_entity_types = entity_types;
        self
    }
}

impl Command for GenericCommand {
    fn name(&self) -> &str {
        &self.name
    }

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

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

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

    fn preview(&self, _context: &CommandContext) -> Option<super::super::data_structure::Entity> {
        None
    }

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

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

    fn is_undoable(&self) -> bool {
        true
    }
}

impl fmt::Display for GenericCommand {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Command({})", self.name)
    }
}
