//! 基于 HTML 字符串的 block menu legacy helper。
//!
//! 这些函数通过浏览器 DOM API 操作编辑器完整 HTML 字符串：
//! `window.document.createElement("div") + innerHTML 解析 -> DOM 修改 ->
//! innerHTML 回读`。这是 history-safe Tiptap command 之前的过渡路径，
//! 新代码应优先使用 command-based mutation，不要继续扩展整文 HTML 替换。
//!
//! # History 风险
//!
//! HTML 字符串往返会丢失 ProseMirror 内部状态，包括 marks、decorations、
//! selection 和 undo history。调用方随后必须通过 `apply_html_update` 用原始
//! HTML 重置编辑器，因此也会破坏撤销历史。本模块只用于隔离待替换的 legacy
//! helper。

use wasm_bindgen::JsCast;
use web_sys::{window, Element};

use crate::editor_runtime::block_hover_state::DropPlacement;

/// 复制第 `index` 个顶层 HTML 块并插入其后。
///
/// 纯 DOM 操作：解析 `current_html` 为 `<div>` 子元素，克隆目标块，
/// 插入到下一个同级节点前（或末尾），返回序列化 HTML。
pub(crate) fn duplicate_top_level_block_html(
    current_html: &str,
    index: usize,
) -> Result<String, String> {
    let document = window()
        .and_then(|win| win.document())
        .ok_or_else(|| "浏览器 document 不可用".to_string())?;
    let container = document
        .create_element("div")
        .map_err(|err| format!("创建 HTML 容器失败：{err:?}"))?;
    container.set_inner_html(current_html);

    let children = container.children();
    let current = children
        .item(index as u32)
        .ok_or_else(|| format!("找不到第 {index} 个 HTML 顶层块"))?;
    let cloned = current
        .clone_node_with_deep(true)
        .map_err(|err| format!("复制 HTML 块失败：{err:?}"))?
        .dyn_into::<Element>()
        .map_err(|_| "复制出的节点不是 Element".to_string())?;

    if let Some(next_sibling) = children.item(index as u32 + 1) {
        container
            .insert_before(&cloned, Some(&next_sibling))
            .map_err(|err| format!("插入复制块失败：{err:?}"))?;
    } else {
        container
            .append_child(&cloned)
            .map_err(|err| format!("追加复制块失败：{err:?}"))?;
    }

    Ok(container.inner_html())
}

/// 在 HTML 层将第 `source` 个顶层块移动到第 `target` 个块的前/后。
///
/// 纯 DOM 操作：解析 `current_html`，克隆拖拽块，移除原始块，
/// 在调整后的位置插入克隆，返回序列化 HTML。
pub(crate) fn reorder_top_level_block_html(
    current_html: &str,
    source: usize,
    target: usize,
    placement: DropPlacement,
) -> Result<String, String> {
    let document = window()
        .and_then(|win| win.document())
        .ok_or_else(|| "浏览器 document 不可用".to_string())?;
    let container = document
        .create_element("div")
        .map_err(|err| format!("创建 HTML 容器失败：{err:?}"))?;
    container.set_inner_html(current_html);

    let initial_children = container.children();
    let block_count = initial_children.length() as usize;
    if source >= block_count || target >= block_count {
        return Err("拖拽目标超出当前顶层块范围".to_string());
    }

    let current = initial_children
        .item(source as u32)
        .ok_or_else(|| format!("找不到第 {source} 个 HTML 顶层块"))?;
    let moving = current
        .clone_node_with_deep(true)
        .map_err(|err| format!("复制拖拽块失败：{err:?}"))?
        .dyn_into::<Element>()
        .map_err(|_| "复制出的拖拽块不是 Element".to_string())?;

    container
        .remove_child(&current)
        .map_err(|err| format!("移除原始拖拽块失败：{err:?}"))?;

    let mut insert_at = match placement {
        DropPlacement::Before => target,
        DropPlacement::After => target + 1,
    };
    if source < insert_at {
        insert_at = insert_at.saturating_sub(1);
    }

    let current_children = container.children();
    let current_len = current_children.length() as usize;
    if insert_at >= current_len {
        container
            .append_child(&moving)
            .map_err(|err| format!("追加拖拽块失败：{err:?}"))?;
    } else {
        let next_sibling = current_children
            .item(insert_at as u32)
            .ok_or_else(|| format!("找不到第 {insert_at} 个 HTML 插入位置"))?;
        container
            .insert_before(&moving, Some(&next_sibling))
            .map_err(|err| format!("插入拖拽块失败：{err:?}"))?;
    }

    Ok(container.inner_html())
}
