use crate::command::command::Command;
use crate::command::transform;
use crate::error::EditorError;
use crate::id::{IdGenerator, UuidGenerator};
use crate::model::block::{Block, BlockId};
use crate::model::document::Document;
use crate::model::registry::{BlockTypeDef, MarkTypeDef, TypeRegistry};
use crate::model::selection::Selection;
use crate::op::apply::apply_op;
use crate::op::inverse::compute_inverse;

use super::history::UndoManager;
use super::transaction::Transaction;

fn reverse_inverse_groups(
    inverse_op_groups: Vec<Vec<crate::op::operation::Operation>>,
) -> Vec<crate::op::operation::Operation> {
    inverse_op_groups.into_iter().rev().flatten().collect()
}

pub struct EditorEngine {
    document: Document,
    selection: Option<Selection>,
    stored_marks: Option<Vec<crate::model::inline::Mark>>,
    history: UndoManager,
    id_gen: Box<dyn IdGenerator>,
    registry: TypeRegistry,
}

impl EditorEngine {
    pub fn new(id_gen: Box<dyn IdGenerator>) -> Self {
        let mut doc = Document::new();
        let block_id = BlockId::new("initial");
        let block = Block::new_paragraph(block_id.clone());
        doc.insert_block(block, 0);
        doc.rebuild_tree_metadata();

        Self {
            document: doc,
            selection: Some(Selection::collapsed(block_id, 0)),
            stored_marks: None,
            history: UndoManager::new(),
            id_gen,
            registry: TypeRegistry::new(),
        }
    }

    pub fn new_default() -> Self {
        Self::new(Box::new(UuidGenerator))
    }

    pub fn from_document(doc: Document, id_gen: Box<dyn IdGenerator>) -> Self {
        let mut document = doc;
        document.rebuild_tree_metadata();

        let selection = document
            .root_block_ids()
            .first()
            .map(|id| Selection::collapsed(id.clone(), 0));

        Self {
            document,
            selection,
            stored_marks: None,
            history: UndoManager::new(),
            id_gen,
            registry: TypeRegistry::new(),
        }
    }

    // --- Registry ---

    pub fn registry(&self) -> &TypeRegistry {
        &self.registry
    }

    pub fn register_block_type(&mut self, def: BlockTypeDef) {
        self.registry.register_block_type(def);
    }

    pub fn register_mark_type(&mut self, def: MarkTypeDef) {
        self.registry.register_mark_type(def);
    }

    // --- Command execution ---

    pub fn execute(&mut self, cmd: Command) -> Result<(), EditorError> {
        self.execute_batch(vec![cmd])
    }

    pub fn execute_batch(&mut self, commands: Vec<Command>) -> Result<(), EditorError> {
        if commands.is_empty() {
            return Ok(());
        }

        let selection_before = self.selection.clone();
        let mut staged_document = self.document.clone();
        let mut staged_selection = self.selection.clone();
        let mut staged_stored_marks = self.stored_marks.clone();
        let mut forward_ops = Vec::new();
        let mut inverse_op_groups = Vec::new();

        for cmd in commands {
            let ops = self.stage_command(
                &mut staged_document,
                &mut staged_selection,
                &mut staged_stored_marks,
                cmd,
                &mut inverse_op_groups,
            )?;
            forward_ops.extend(ops);
        }

        self.document = staged_document;
        self.selection = staged_selection;
        self.stored_marks = staged_stored_marks;

        if forward_ops.is_empty() {
            return Ok(());
        }

        self.document.rebuild_tree_metadata();

        let inverse_ops = reverse_inverse_groups(inverse_op_groups);

        let tx = Transaction {
            ops: forward_ops,
            inverse_ops,
            selection_before,
            selection_after: self.selection.clone(),
        };

        self.history.push(tx);
        Ok(())
    }

    /// Move a block to a new position. Operates at the operation level.
    pub fn move_block(&mut self, block_id: &str, new_index: usize) -> Result<(), EditorError> {
        let bid = crate::model::block::BlockId::new(block_id);
        let op = crate::op::operation::Operation::MoveBlock {
            block_id: bid.clone(),
            new_index,
        };
        let selection_before = self.selection.clone();
        let inverse = compute_inverse(&self.document, &self.selection, &op)?;
        apply_op(&mut self.document, &mut self.selection, &op)?;
        self.document.rebuild_tree_metadata();

        let tx = super::transaction::Transaction {
            ops: vec![op],
            inverse_ops: inverse,
            selection_before,
            selection_after: self.selection.clone(),
        };
        self.history.push(tx);
        Ok(())
    }

    /// Move multiple root blocks together as a single undoable transaction.
    /// `new_index` is interpreted against the root list AFTER the dragged
    /// blocks are removed, so callers can compute the insertion point from a
    /// filtered root array and preserve relative order.
    pub fn move_blocks(
        &mut self,
        block_ids: &[BlockId],
        new_index: usize,
    ) -> Result<(), EditorError> {
        if block_ids.is_empty() {
            return Ok(());
        }

        for block_id in block_ids {
            if self.document.block_root_index(block_id).is_none() {
                return Err(EditorError::BlockNotFound(block_id.clone()));
            }
        }

        let root_ids = self.document.root_block_ids().to_vec();
        let ordered_block_ids: Vec<BlockId> = root_ids
            .iter()
            .filter(|block_id| block_ids.contains(block_id))
            .cloned()
            .collect();

        if ordered_block_ids.is_empty() {
            return Ok(());
        }

        let insert_at = new_index.min(root_ids.len().saturating_sub(ordered_block_ids.len()));
        let first_selected_index = root_ids
            .iter()
            .position(|block_id| block_id == &ordered_block_ids[0])
            .ok_or_else(|| EditorError::BlockNotFound(ordered_block_ids[0].clone()))?;
        let moving_down = insert_at > first_selected_index;

        let selection_before = self.selection.clone();
        let mut ops = Vec::new();
        let mut inverse_op_groups = Vec::new();

        if moving_down {
            for (reverse_idx, block_id) in ordered_block_ids.iter().rev().enumerate() {
                let offset = ordered_block_ids.len() - 1 - reverse_idx;
                let op = crate::op::operation::Operation::MoveBlock {
                    block_id: block_id.clone(),
                    new_index: insert_at + offset,
                };
                let inverse = compute_inverse(&self.document, &self.selection, &op)?;
                apply_op(&mut self.document, &mut self.selection, &op)?;
                ops.push(op);
                inverse_op_groups.push(inverse);
            }
        } else {
            for (offset, block_id) in ordered_block_ids.iter().enumerate() {
                let op = crate::op::operation::Operation::MoveBlock {
                    block_id: block_id.clone(),
                    new_index: insert_at + offset,
                };
                let inverse = compute_inverse(&self.document, &self.selection, &op)?;
                apply_op(&mut self.document, &mut self.selection, &op)?;
                ops.push(op);
                inverse_op_groups.push(inverse);
            }
        }

        self.document.rebuild_tree_metadata();
        let inverse_ops = reverse_inverse_groups(inverse_op_groups);

        let tx = Transaction {
            ops,
            inverse_ops,
            selection_before,
            selection_after: self.selection.clone(),
        };
        self.history.push(tx);
        Ok(())
    }

    /// Remove multiple blocks in a single undoable transaction. Operates at the
    /// operation level.
    pub fn remove_blocks(&mut self, block_ids: &[BlockId]) -> Result<(), EditorError> {
        if block_ids.is_empty() {
            return Ok(());
        }

        for block_id in block_ids {
            if self.document.block_root_index(block_id).is_none() {
                return Err(EditorError::BlockNotFound(block_id.clone()));
            }
        }

        let selection_before = self.selection.clone();
        let mut ops = Vec::new();
        let mut inverse_op_groups = Vec::new();

        if self
            .document
            .root_block_ids()
            .iter()
            .all(|block_id| block_ids.contains(block_id))
        {
            let op = crate::op::operation::Operation::InsertBlock {
                block: Block::new_paragraph(self.id_gen.next_id()),
                index: 0,
            };
            let inverse = compute_inverse(&self.document, &self.selection, &op)?;
            apply_op(&mut self.document, &mut self.selection, &op)?;
            ops.push(op);
            inverse_op_groups.push(inverse);
        }

        let root_ids = self.document.root_block_ids().to_vec();
        let first_removed_index = root_ids
            .iter()
            .position(|block_id| block_ids.contains(block_id))
            .ok_or_else(|| EditorError::BlockNotFound(block_ids[0].clone()))?;
        let selection_target = root_ids[..first_removed_index]
            .iter()
            .rev()
            .find(|block_id| !block_ids.contains(block_id))
            .or_else(|| {
                root_ids[first_removed_index + 1..]
                    .iter()
                    .find(|block_id| !block_ids.contains(block_id))
            })
            .cloned()
            .ok_or_else(|| {
                EditorError::InvalidOperation("remove_blocks requires a remaining block".into())
            })?;

        for block_id in block_ids {
            let op = crate::op::operation::Operation::RemoveBlock {
                block_id: block_id.clone(),
            };
            let inverse = compute_inverse(&self.document, &self.selection, &op)?;
            apply_op(&mut self.document, &mut self.selection, &op)?;
            ops.push(op);
            inverse_op_groups.push(inverse);
        }

        self.document.rebuild_tree_metadata();
        self.selection = Some(Selection::collapsed(selection_target, 0));
        let inverse_ops = reverse_inverse_groups(inverse_op_groups);

        let tx = Transaction {
            ops,
            inverse_ops,
            selection_before,
            selection_after: self.selection.clone(),
        };
        self.history.push(tx);
        Ok(())
    }

    fn validate_command(&self, cmd: &Command) -> Result<(), EditorError> {
        match cmd {
            Command::SetBlockType { block_type, .. }
                if !self.registry.is_valid_block_type(block_type) =>
            {
                return Err(EditorError::UnknownBlockType(block_type.clone()));
            }
            Command::ToggleMark { mark_type } if !self.registry.is_valid_mark_type(mark_type) => {
                return Err(EditorError::UnknownMarkType(mark_type.clone()));
            }
            Command::SetMark { mark_type, .. } | Command::ClearMark { mark_type }
                if !self.registry.is_valid_mark_type(mark_type) =>
            {
                return Err(EditorError::UnknownMarkType(mark_type.clone()));
            }
            Command::ToggleTodo
            | Command::InsertTable { .. }
            | Command::InsertTableRow { .. }
            | Command::DeleteTableRow { .. }
            | Command::InsertTableCol { .. }
            | Command::DeleteTableCol { .. }
            | Command::MergeCells { .. }
            | Command::UnmergeCells { .. }
            | Command::SplitCell { .. }
            | Command::SetCellProps { .. }
            | Command::SetTableProps { .. }
            | Command::SetRowProps { .. }
            | Command::ToggleCellMark { .. }
            | Command::ClearCells { .. }
            | Command::TableNavigate { .. } => {
                // Table commands use built-in types, always valid.
            }
            _ => {}
        }
        Ok(())
    }

    fn stage_command(
        &mut self,
        document: &mut Document,
        selection: &mut Option<Selection>,
        stored_marks: &mut Option<Vec<crate::model::inline::Mark>>,
        cmd: Command,
        inverse_op_groups: &mut Vec<Vec<crate::op::operation::Operation>>,
    ) -> Result<Vec<crate::op::operation::Operation>, EditorError> {
        let current_selection = selection.as_ref().ok_or(EditorError::NoSelection)?.clone();

        self.validate_command(&cmd)?;

        if let Command::ToggleMark { ref mark_type } = cmd
            && let Some((start, end)) = current_selection.ordered_offsets()
            && start == end
        {
            let current = stored_marks.take().unwrap_or_else(|| {
                crate::command::transform::active_marks_at_pub(
                    document,
                    &current_selection.anchor.id,
                    start,
                )
            });
            let has = current.iter().any(|m| &m.ty == mark_type);
            *stored_marks = Some(if has {
                current.into_iter().filter(|m| &m.ty != mark_type).collect()
            } else {
                let mut marks = current;
                marks.push(crate::model::inline::Mark::new(mark_type));
                marks
            });
            return Ok(Vec::new());
        }

        if let Command::SetMark {
            ref mark_type,
            ref attrs,
        } = cmd
            && let Some((start, end)) = current_selection.ordered_offsets()
            && start == end
        {
            let mut current = stored_marks.take().unwrap_or_else(|| {
                crate::command::transform::active_marks_at_pub(
                    document,
                    &current_selection.anchor.id,
                    start,
                )
            });
            current.retain(|m| &m.ty != mark_type);
            current.push(crate::model::inline::Mark::with_attrs(
                mark_type.clone(),
                attrs.clone(),
            ));
            *stored_marks = Some(current);
            return Ok(Vec::new());
        }

        if let Command::ClearMark { ref mark_type } = cmd
            && let Some((start, end)) = current_selection.ordered_offsets()
            && start == end
        {
            let current = stored_marks.take().unwrap_or_else(|| {
                crate::command::transform::active_marks_at_pub(
                    document,
                    &current_selection.anchor.id,
                    start,
                )
            });
            *stored_marks = Some(current.into_iter().filter(|m| &m.ty != mark_type).collect());
            return Ok(Vec::new());
        }

        let ops = transform::transform(
            document,
            &current_selection,
            &cmd,
            &mut *self.id_gen,
            stored_marks.take(),
        )?;

        for op in &ops {
            let inverse = compute_inverse(document, selection, op)?;
            inverse_op_groups.push(inverse);
            apply_op(document, selection, op)?;
        }

        Ok(ops)
    }

    // --- Undo/Redo ---

    pub fn undo(&mut self) -> Result<bool, EditorError> {
        let tx = match self.history.undo() {
            Some(tx) => tx,
            None => return Ok(false),
        };

        for op in &tx.inverse_ops {
            apply_op(&mut self.document, &mut self.selection, op)?;
        }

        self.document.rebuild_tree_metadata();
        self.selection = tx.selection_before.clone();
        Ok(true)
    }

    pub fn redo(&mut self) -> Result<bool, EditorError> {
        let tx = match self.history.redo() {
            Some(tx) => tx,
            None => return Ok(false),
        };

        for op in &tx.ops {
            apply_op(&mut self.document, &mut self.selection, op)?;
        }

        self.document.rebuild_tree_metadata();
        self.selection = tx.selection_after.clone();
        Ok(true)
    }

    // --- Queries ---

    pub fn document(&self) -> &Document {
        &self.document
    }

    pub fn selection(&self) -> &Option<Selection> {
        &self.selection
    }

    pub fn set_selection(&mut self, selection: Option<Selection>) {
        if self.selection != selection {
            self.stored_marks = None;
        }
        self.selection = selection;
    }

    pub fn stored_marks(&self) -> &Option<Vec<crate::model::inline::Mark>> {
        &self.stored_marks
    }

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

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

    // --- Serialization ---

    pub fn to_json(&self) -> Result<String, EditorError> {
        serde_json::to_string(&self.document)
            .map_err(|e| EditorError::SerializationError(e.to_string()))
    }

    pub fn to_json_pretty(&self) -> Result<String, EditorError> {
        serde_json::to_string_pretty(&self.document)
            .map_err(|e| EditorError::SerializationError(e.to_string()))
    }

    pub fn from_json(json: &str, id_gen: Box<dyn IdGenerator>) -> Result<Self, EditorError> {
        let doc: Document = serde_json::from_str(json)
            .map_err(|e| EditorError::SerializationError(e.to_string()))?;
        Ok(Self::from_document(doc, id_gen))
    }
}
