use super::protocol;
use std::collections::{BTreeMap, BTreeSet};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FileTreeRenderRow {
    pub row_id: String,
    pub row_kind: String,
    pub node_id: String,
    pub parent_node_id: Option<String>,
    pub title: String,
    pub depth: u32,
    pub expandable: bool,
    pub expanded: bool,
    pub icon_kind: String,
    pub document_id: Option<String>,
    pub asset_id: Option<String>,
    pub object_identity: Option<String>,
    pub selected: bool,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FileTreeInitialRenderInput {
    pub rows: Vec<FileTreeRenderRow>,
}

pub fn build_filetree_testids(rows: &[FileTreeRenderRow]) -> Vec<(&'static str, String)> {
    rows.iter()
        .map(|row| {
            let test_id = match row.row_kind.as_str() {
                "document" => protocol::TEST_ID_FILETREE_DOC_ROW,
                "index" => protocol::TEST_ID_FILETREE_INDEX_ROW,
                _ => protocol::TEST_ID_FILETREE_ASSET_ROW,
            };
            (test_id, row.row_id.clone())
        })
        .collect()
}

fn escape_html(input: &str) -> String {
    input
        .replace('&', "&amp;")
        .replace('<', "&lt;")
        .replace('>', "&gt;")
        .replace('"', "&quot;")
        .replace('\'', "&#39;")
}

fn row_test_id(row_kind: &str) -> &'static str {
    match row_kind {
        "document" => protocol::TEST_ID_FILETREE_DOC_ROW,
        "index" => protocol::TEST_ID_FILETREE_INDEX_ROW,
        _ => protocol::TEST_ID_FILETREE_ASSET_ROW,
    }
}

fn render_filetree_row(
    html: &mut String,
    row: &FileTreeRenderRow,
    children_by_parent: &BTreeMap<Option<String>, Vec<FileTreeRenderRow>>,
) {
    let toggle_html = if row.expandable {
        format!(
            r#"<button type="button" class="tree-toggle" data-testid="filetree-toggle" data-rust-action="toggle" data-row-id="{row_id}" aria-label="{label} {title}">{marker}</button>"#,
            row_id = escape_html(&row.row_id),
            label = if row.expanded { "折叠" } else { "展开" },
            title = escape_html(&row.title),
            marker = if row.expanded { "▾" } else { "▸" },
        )
    } else {
        r#"<span class="tree-spacer" aria-hidden="true"></span>"#.to_string()
    };
    let parent_attr = row
        .parent_node_id
        .as_deref()
        .map(|parent_id| format!(r#" data-parent-id="{}""#, escape_html(parent_id)))
        .unwrap_or_default();
    let create_action_html = if row.row_kind == "document" {
        format!(
            r#"<button type="button" class="tree-action" data-testid="filetree-create" data-rust-action="create" data-row-id="{row_id}" data-node-id="{node_id}" aria-label="新建子页面">+</button>"#,
            row_id = escape_html(&row.row_id),
            node_id = escape_html(&row.node_id),
        )
    } else {
        String::new()
    };
    html.push_str(&format!(
        r#"<li class="tree-node" data-node-id="{node_id}"><div class="tree-row" role="treeitem" aria-level="{aria_level}" aria-expanded="{expanded_attr}" data-rust-rendered-row="filetree" data-testid="{test_id}" data-row-id="{row_id}" data-row-kind="{row_kind}" data-node-id="{node_id}"{parent_attr} data-document-id="{document_id}" data-doc-id="{document_id}" data-asset-id="{asset_id}" data-object-identity="{object_identity}" data-shell-mode="filetree" data-selected="{selected}" data-active="false" draggable="true">{toggle_html}<span class="tree-kind-badge" data-kind="{icon_kind}" aria-hidden="true"></span><button type="button" class="tree-link" data-rust-action="open" data-row-id="{row_id}" data-document-id="{document_id}" data-asset-id="{asset_id}"><span class="tree-link-title">{title}</span></button><div class="tree-actions">{create_action_html}<button type="button" class="tree-action" data-testid="filetree-action-menu" data-rust-action="menu" data-row-id="{row_id}" aria-label="更多操作">…</button></div></div>"#,
        node_id = escape_html(&row.node_id),
        aria_level = row.depth + 1,
        expanded_attr = if row.expandable && row.expanded { "true" } else { "false" },
        test_id = row_test_id(&row.row_kind),
        row_id = escape_html(&row.row_id),
        row_kind = escape_html(&row.row_kind),
        parent_attr = parent_attr,
        document_id = escape_html(row.document_id.as_deref().unwrap_or_default()),
        asset_id = escape_html(row.asset_id.as_deref().unwrap_or_default()),
        object_identity = escape_html(row.object_identity.as_deref().unwrap_or_default()),
        selected = row.selected,
        toggle_html = toggle_html,
        icon_kind = escape_html(&row.icon_kind),
        create_action_html = create_action_html,
        title = escape_html(&row.title),
    ));
    if row.expandable {
        if let Some(children) = children_by_parent.get(&Some(row.node_id.clone())) {
            html.push_str(if row.expanded {
                r#"<ul class="tree-children">"#
            } else {
                r#"<ul class="tree-children tree-children--collapsed">"#
            });
            for child in children {
                render_filetree_row(html, child, children_by_parent);
            }
            html.push_str("</ul>");
        }
    }
    html.push_str("</li>");
}

pub fn render_initial_filetree_html(input: &FileTreeInitialRenderInput) -> String {
    let mut html = String::from(
        r#"<ul class="tree-root" role="tree" data-rust-filetree-renderer="initial_v1">"#,
    );
    if input.rows.is_empty() {
        html.push_str(
            r#"<li class="tree-empty" data-rust-rendered-row="filetree-empty">暂无文件或页面</li>"#,
        );
        html.push_str("</ul>");
        return html;
    }

    let ids = input
        .rows
        .iter()
        .map(|row| row.node_id.clone())
        .collect::<BTreeSet<_>>();
    let mut children_by_parent = BTreeMap::<Option<String>, Vec<FileTreeRenderRow>>::new();
    for row in &input.rows {
        let parent_id = row
            .parent_node_id
            .as_ref()
            .filter(|parent_id| ids.contains(*parent_id))
            .cloned();
        children_by_parent
            .entry(parent_id)
            .or_default()
            .push(row.clone());
    }

    if let Some(roots) = children_by_parent.get(&None).cloned() {
        for root in &roots {
            render_filetree_row(&mut html, root, &children_by_parent);
        }
    }
    html.push_str("</ul>");
    html
}

#[cfg(test)]
mod tests {
    use super::{render_initial_filetree_html, FileTreeInitialRenderInput, FileTreeRenderRow};

    #[test]
    fn tree_shell_filetree_renderer_outputs_initial_nested_html_contract() {
        let html = render_initial_filetree_html(&FileTreeInitialRenderInput {
            rows: vec![
                FileTreeRenderRow {
                    row_id: "doc:page_root".into(),
                    row_kind: "document".into(),
                    node_id: "page_root".into(),
                    parent_node_id: None,
                    title: "首页 <安全>".into(),
                    depth: 0,
                    expandable: true,
                    expanded: true,
                    icon_kind: "page".into(),
                    document_id: Some("page_root".into()),
                    asset_id: None,
                    object_identity: Some(
                        r#"{"objectKind":"page","documentId":"page_root","blockId":null,"assetId":null}"#.into(),
                    ),
                    selected: true,
                },
                FileTreeRenderRow {
                    row_id: "index:page_root".into(),
                    row_kind: "index".into(),
                    node_id: "index:page_root".into(),
                    parent_node_id: Some("page_root".into()),
                    title: "index.md".into(),
                    depth: 1,
                    expandable: false,
                    expanded: false,
                    icon_kind: "index".into(),
                    document_id: Some("page_root".into()),
                    asset_id: None,
                    object_identity: Some(
                        r#"{"objectKind":"index","documentId":"page_root","blockId":null,"assetId":null}"#.into(),
                    ),
                    selected: false,
                },
            ],
        });

        assert!(html.contains("data-rust-filetree-renderer=\"initial_v1\""));
        assert!(html.contains("data-rust-rendered-row=\"filetree\""));
        assert!(html.contains("data-testid=\"filetree-doc-row\""));
        assert!(html.contains("data-testid=\"filetree-index-row\""));
        assert!(html.contains("data-doc-id=\"page_root\""));
        assert!(html.contains("data-object-identity=\"{&quot;objectKind&quot;:&quot;index&quot;"));
        assert!(html.contains("tree-children"));
        assert!(html.contains("首页 &lt;安全&gt;"));
        assert!(html.contains("data-selected=\"true\""));
    }
}
