use super::protocol;
use std::collections::BTreeMap;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PageTreeRenderRow {
    pub node_id: String,
    pub parent_node_id: Option<String>,
    pub title: String,
    pub depth: u32,
    pub expandable: bool,
    pub expanded: bool,
    pub openable: bool,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PageTreeDomRow {
    pub test_id: &'static str,
    pub action_create_test_id: &'static str,
    pub action_rename_test_id: &'static str,
    pub action_move_up_test_id: &'static str,
    pub node_id: String,
    pub depth: u32,
    pub title: String,
    pub expandable: bool,
    pub openable: bool,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PageTreeInitialRenderInput {
    pub rows: Vec<PageTreeRenderRow>,
    pub active_node_id: Option<String>,
    pub focused_node_id: Option<String>,
}

pub fn build_page_tree_dom_rows(rows: &[PageTreeRenderRow]) -> Vec<PageTreeDomRow> {
    rows.iter()
        .map(|row| PageTreeDomRow {
            test_id: protocol::TEST_ID_TREE_NODE_OPEN,
            action_create_test_id: protocol::TEST_ID_TREE_ACTION_CREATE,
            action_rename_test_id: protocol::TEST_ID_TREE_ACTION_RENAME,
            action_move_up_test_id: protocol::TEST_ID_TREE_ACTION_MOVE_UP,
            node_id: row.node_id.clone(),
            depth: row.depth,
            title: row.title.clone(),
            expandable: row.expandable,
            openable: row.openable,
        })
        .collect()
}

const PAGE_TREE_CHEVRON_SVG: &str = r#"<svg class="tree-toggle-icon" viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false"><path d="M7.84 14.955c.206 0 .37-.07.505-.21l4.277-4.179a.79.79 0 0 0 .264-.574.78.78 0 0 0-.258-.574L8.35 5.24a.7.7 0 0 0-.51-.21.721.721 0 0 0-.498 1.247l3.814 3.721-3.814 3.709a.721.721 0 0 0 .498 1.248"></path></svg>"#;

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

fn render_depth_for_node(rows: &[PageTreeRenderRow], node_id: &str, fallback_depth: u32) -> u32 {
    if fallback_depth > 0 {
        return fallback_depth;
    }
    let parent_by_id = rows
        .iter()
        .map(|row| (row.node_id.as_str(), row.parent_node_id.as_deref()))
        .collect::<BTreeMap<_, _>>();
    let mut depth = 0_u32;
    let mut cursor = parent_by_id.get(node_id).and_then(|parent| *parent);
    while let Some(parent_id) = cursor {
        depth += 1;
        cursor = parent_by_id.get(parent_id).and_then(|parent| *parent);
        if depth > 32 {
            break;
        }
    }
    depth
}

fn render_page_row(
    html: &mut String,
    row: &PageTreeRenderRow,
    children_by_parent: &BTreeMap<Option<String>, Vec<PageTreeRenderRow>>,
    input: &PageTreeInitialRenderInput,
) {
    let dom_rows = build_page_tree_dom_rows(&[row.clone()]);
    if let Some(row) = dom_rows.first() {
        let active = input
            .active_node_id
            .as_deref()
            .map(|active_node_id| active_node_id == row.node_id)
            .unwrap_or(false);
        let focused = input
            .focused_node_id
            .as_deref()
            .map(|focused_node_id| focused_node_id == row.node_id)
            .unwrap_or(false);
        let expanded = input
            .rows
            .iter()
            .find(|source| source.node_id == row.node_id)
            .map(|source| source.expanded)
            .unwrap_or(false);
        let toggle_html = if row.expandable {
            format!(
                r#"<button type="button" class="tree-toggle" data-testid="tree-node-toggle" data-rust-action="toggle" data-node-id="{node_id}" aria-label="{label} {title}" aria-expanded="{expanded_state}">{marker}</button>"#,
                node_id = escape_html(&row.node_id),
                label = if expanded { "折叠" } else { "展开" },
                title = escape_html(&row.title),
                expanded_state = if expanded { "true" } else { "false" },
                marker = PAGE_TREE_CHEVRON_SVG,
            )
        } else {
            r#"<span class="tree-spacer" aria-hidden="true"></span>"#.to_string()
        };
        let parent_attr = input
            .rows
            .iter()
            .find(|source| source.node_id == row.node_id)
            .and_then(|source| source.parent_node_id.as_deref())
            .map(|parent_id| format!(r#" data-parent-id="{}""#, escape_html(parent_id)))
            .unwrap_or_default();
        let render_depth = render_depth_for_node(&input.rows, &row.node_id, row.depth);
        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="page" data-testid="wolai-sidebar-row" data-tree-testid="{test_id}" data-node-id="{node_id}"{parent_attr} data-depth="{depth}" data-shell-mode="page" data-active="{active}" aria-selected="{selected}"{current_attr} data-focused="{focused}" data-page-openable="{openable}" data-draggable="true" draggable="true" tabindex="{tab_index}">{toggle_html}<button type="button" class="tree-link" data-testid="tree-node-open" data-rust-action="open" data-node-id="{node_id}" data-page-openable="{openable}"><span class="tree-link-title">{title}</span></button><div class="tree-actions"><button type="button" class="tree-action" data-testid="tree-action-menu" data-rust-action="menu" data-node-id="{node_id}" aria-label="更多操作">…</button><button type="button" class="tree-action" data-testid="tree-action-create" data-rust-action="create" data-node-id="{node_id}" aria-label="新建子页面">+</button></div></div>"#,
            node_id = escape_html(&row.node_id),
            aria_level = render_depth + 1,
            expanded_attr = if row.expandable && expanded { "true" } else { "false" },
            test_id = row.test_id,
            parent_attr = parent_attr,
            depth = render_depth,
            active = active,
            selected = active,
            current_attr = if active { r#" aria-current="page""# } else { "" },
            focused = focused,
            openable = row.openable,
            tab_index = if focused { "0" } else { "-1" },
            toggle_html = toggle_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 expanded {
                    r#"<ul class="tree-children">"#
                } else {
                    r#"<ul class="tree-children tree-children--collapsed">"#
                });
                for child in children {
                    render_page_row(html, child, children_by_parent, input);
                }
                html.push_str("</ul>");
            }
        }
        html.push_str("</li>");
    }
}

pub fn render_initial_page_tree_html(input: &PageTreeInitialRenderInput) -> String {
    let mut html =
        String::from(r#"<ul class="tree-root" role="tree" data-rust-page-renderer="initial_v1">"#);
    if input.rows.is_empty() {
        html.push_str("</ul>");
        return html;
    }

    let ids = input
        .rows
        .iter()
        .map(|row| row.node_id.clone())
        .collect::<std::collections::BTreeSet<_>>();
    let mut children_by_parent = BTreeMap::<Option<String>, Vec<PageTreeRenderRow>>::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_page_row(&mut html, root, &children_by_parent, input);
        }
    }
    html.push_str("</ul>");
    html
}

#[cfg(test)]
mod tests {
    use super::{
        build_page_tree_dom_rows, render_initial_page_tree_html, PageTreeInitialRenderInput,
        PageTreeRenderRow,
    };

    #[test]
    fn tree_shell_page_renderer_builds_rows_with_stable_testids() {
        let rows = build_page_tree_dom_rows(&[
            PageTreeRenderRow {
                node_id: "page_root".into(),
                parent_node_id: None,
                title: "首页".into(),
                depth: 0,
                expandable: true,
                expanded: false,
                openable: true,
            },
            PageTreeRenderRow {
                node_id: "page_child".into(),
                parent_node_id: Some("page_root".into()),
                title: "子页".into(),
                depth: 1,
                expandable: false,
                expanded: false,
                openable: true,
            },
        ]);

        assert_eq!(rows.len(), 2);
        assert_eq!(rows[0].test_id, "tree-node-open");
        assert_eq!(rows[0].action_create_test_id, "tree-action-create");
        assert_eq!(rows[0].action_rename_test_id, "tree-action-rename");
        assert_eq!(rows[0].action_move_up_test_id, "tree-action-move-up");
        assert_eq!(rows[1].depth, 1);
    }

    #[test]
    fn tree_shell_page_renderer_outputs_initial_html_contract() {
        let html = render_initial_page_tree_html(&PageTreeInitialRenderInput {
            rows: vec![
                PageTreeRenderRow {
                    node_id: "page_root".into(),
                    parent_node_id: None,
                    title: "首页 <安全>".into(),
                    depth: 0,
                    expandable: true,
                    expanded: true,
                    openable: true,
                },
                PageTreeRenderRow {
                    node_id: "page_child".into(),
                    parent_node_id: Some("page_root".into()),
                    title: "子页".into(),
                    depth: 1,
                    expandable: false,
                    expanded: false,
                    openable: true,
                },
            ],
            active_node_id: Some("page_root".into()),
            focused_node_id: Some("page_root".into()),
        });

        assert!(html.contains("data-rust-page-renderer=\"initial_v1\""));
        assert!(html.contains("data-rust-rendered-row=\"page\""));
        assert!(html.contains("data-shell-mode=\"page\""));
        assert!(html.contains("data-rust-action=\"open\""));
        assert!(html.contains("data-rust-action=\"toggle\""));
        assert!(html.contains("data-testid=\"tree-node-toggle\""));
        assert!(html.contains("data-rust-action=\"create\""));
        assert!(html.contains("draggable=\"true\""));
        assert!(html.contains("tree-children"));
        assert!(html.contains("子页"));
        assert!(html.contains("首页 &lt;安全&gt;"));
        assert!(html.contains("data-active=\"true\""));
        assert!(html.contains("data-focused=\"true\""));
    }
}
