use crate::editor_runtime::{
    block_hover_state::HoveredBlockState,
    block_transform::run_slash_action,
    bridge_events::{ChangeMetaPayload, ChangePayload},
    command_sync::read_editor_snapshot,
    overlays::toolbar_overlay_locked,
    persistence::{persist_document_state, persisted_document_identity, PersistedDocumentIdentity},
    runtime_bridge::dispatch_change_event_to_target,
    slash_actions::{SlashActionKind, SLASH_ACTIONS},
};
use leptos::prelude::*;
use leptos_tiptap::TiptapEditorHandle;
use serde_json::Value;
use std::sync::Arc;
use web_sys::EventTarget;

pub(crate) struct SlashMenuViewProps {
    pub(crate) editor: TiptapEditorHandle,
    pub(crate) signals: SlashMenuSignals,
    pub(crate) runtime: SlashMenuRuntimeContext,
    pub(crate) anchor_style: Arc<dyn Fn() -> String + Send + Sync>,
}

#[derive(Clone, Copy)]
pub(crate) struct SlashMenuSignals {
    pub(crate) slash_open: ReadSignal<bool>,
    pub(crate) set_slash_open: WriteSignal<bool>,
    pub(crate) slash_index: ReadSignal<usize>,
    pub(crate) slash_query: ReadSignal<String>,
    pub(crate) dirty_count: ReadSignal<u32>,
    pub(crate) set_dirty_count: WriteSignal<u32>,
    pub(crate) document_json: WriteSignal<Value>,
    pub(crate) html_output: WriteSignal<String>,
    pub(crate) json_output: WriteSignal<String>,
    pub(crate) document_id: ReadSignal<Option<String>>,
    pub(crate) workspace_id: ReadSignal<Option<String>>,
    pub(crate) title: ReadSignal<String>,
    pub(crate) editor_focused: ReadSignal<bool>,
    pub(crate) hovered_block: ReadSignal<Option<HoveredBlockState>>,
    pub(crate) turn_into_open: ReadSignal<bool>,
    pub(crate) color_menu_open: ReadSignal<bool>,
    pub(crate) more_menu_open: ReadSignal<bool>,
    pub(crate) revision: ReadSignal<Option<i64>>,
    pub(crate) conflict_detection_key: ReadSignal<Option<String>>,
    pub(crate) read_only: ReadSignal<bool>,
    pub(crate) command_feedback: WriteSignal<String>,
}

#[derive(Clone)]
pub(crate) struct SlashMenuRuntimeContext {
    pub(crate) change_event_target: Option<EventTarget>,
    pub(crate) dispatch_runtime_state: Arc<dyn Fn(&EventTarget) + Send + Sync>,
}

fn runtime_persisted_identity(
    document_id: ReadSignal<Option<String>>,
    workspace_id: ReadSignal<Option<String>>,
) -> PersistedDocumentIdentity {
    persisted_document_identity(document_id.get_untracked(), workspace_id.get_untracked())
}

fn filtered_slash_intro_view(
    query: &str,
    command_feedback: WriteSignal<String>,
) -> impl IntoView {
    let show_turn_into = !query.is_empty()
        && ("zhw".contains(query) || "转换为".contains(query));
    let show_page = !query.is_empty() && ("ym".contains(query) || "页面".contains(query));

    if show_turn_into || show_page {
        view! {
            <>
                {if show_turn_into {
                    view! {
                        <>
                            <div class="slash-section-label">"转换为"</div>
                            <button
                                class="slash-item"
                                data-active="false"
                                data-testid="slash-item-turn-into"
                                on:click=move |_| {
                                    command_feedback.set("转换为入口请通过块菜单或选区工具栏执行".to_string());
                                }
                            >
                                <span class="slash-item-row">
                                    <span class="slash-item-icon">"↻"</span>
                                    <span class="slash-item-copy">
                                        <strong>"转换为"</strong>
                                        <span>"打开块类型转换入口"</span>
                                    </span>
                                    <span class="slash-item-shortcut">"/zhw"</span>
                                </span>
                            </button>
                        </>
                    }.into_any()
                } else {
                    ().into_any()
                }}
                {if show_page {
                    view! {
                        <>
                            <div class="slash-section-label">"基础块列表"</div>
                            <button
                                class="slash-item"
                                data-active="false"
                                data-testid="slash-item-page"
                                on:click=move |_| {
                                    command_feedback.set("页面入口请通过转换为页面真源链路执行".to_string());
                                }
                            >
                                <span class="slash-item-row">
                                    <span class="slash-item-icon">"▣"</span>
                                    <span class="slash-item-copy">
                                        <strong>"页面"</strong>
                                        <span>"创建或转换为页面块"</span>
                                    </span>
                                    <span class="slash-item-shortcut">"/ym"</span>
                                </span>
                            </button>
                        </>
                    }.into_any()
                } else {
                    ().into_any()
                }}
            </>
        }
        .into_any()
    } else {
        ().into_any()
    }
}

fn handle_slash_item_click(
    editor: TiptapEditorHandle,
    kind: SlashActionKind,
    signals: SlashMenuSignals,
    runtime: &SlashMenuRuntimeContext,
) {
    match run_slash_action(editor, kind) {
        Ok(message) => {
            signals.set_slash_open.set(false);
            signals.set_dirty_count.update(|count| *count += 1);
            let (html, snapshot, json_text) = read_editor_snapshot(editor);
            signals.html_output.set(html.clone());
            signals.document_json.set(snapshot.clone());
            signals.json_output.set(json_text);
            if let Some(target) = runtime.change_event_target.as_ref() {
                dispatch_change_event_to_target(
                    target,
                    &ChangePayload {
                        document_id: signals.document_id.get_untracked(),
                        workspace_id: signals.workspace_id.get_untracked(),
                        title: signals.title.get_untracked(),
                        content: snapshot.clone(),
                        meta: ChangeMetaPayload {
                            dirty_count: signals.dirty_count.get_untracked(),
                            editor_focused: signals.editor_focused.get_untracked(),
                            slash_open: signals.slash_open.get_untracked(),
                            toolbar_open: toolbar_overlay_locked(
                                signals.turn_into_open.get_untracked(),
                                signals.color_menu_open.get_untracked(),
                                signals.more_menu_open.get_untracked(),
                            ),
                            selected_block_index: signals
                                .hovered_block
                                .get_untracked()
                                .map(|block| block.index),
                            revision: signals.revision.get_untracked(),
                            conflict_detection_key: signals.conflict_detection_key.get_untracked(),
                            read_only: signals.read_only.get_untracked(),
                        },
                    },
                );
                (runtime.dispatch_runtime_state)(target);
            }
            match persist_document_state(
                &runtime_persisted_identity(signals.document_id, signals.workspace_id),
                &signals.title.get_untracked(),
                &snapshot,
                Some(html),
            ) {
                Ok(()) => signals
                    .command_feedback
                    .set(format!("{message}，并已写入本地草稿")),
                Err(err) => signals
                    .command_feedback
                    .set(format!("{message}，但本地保存失败：{err}")),
            }
        }
        Err(err) => {
            signals.set_slash_open.set(false);
            signals
                .command_feedback
                .set(format!("命令执行失败：{err}"));
        }
    }
}

pub(crate) fn slash_menu_view(props: SlashMenuViewProps) -> impl IntoView {
    let SlashMenuViewProps {
        editor,
        signals,
        runtime,
        anchor_style,
    } = props;

    view! {
        {move || {
            if signals.slash_open.get() {
                let anchor_style = anchor_style.clone();
                let runtime = runtime.clone();
                view! {
                    <div
                        class="slash-menu"
                        data-testid="mnote-leptos-tiptap-slash-menu"
                        style=anchor_style()
                    >
                        <div class="slash-list">
                            {move || {
                                let query = signals.slash_query.get().trim().to_ascii_lowercase();
                                filtered_slash_intro_view(&query, signals.command_feedback)
                            }}
                            {SLASH_ACTIONS.iter().enumerate().map(|(index, action)| {
                                let is_selected = move || signals.slash_index.get() == index;
                                let kind = action.kind;
                                let label = action.label;
                                let description = action.description;
                                let shortcut = action.shortcut;
                                let icon = action.icon;
                                let category = action.category;
                                let show_category = index == 0
                                    || SLASH_ACTIONS[index - 1].category != category;
                                let testid = format!("slash-item-{}", action.id);
                                let runtime = runtime.clone();
                                view! {
                                    <>
                                        {if show_category {
                                            view! { <div class="slash-section-label">{category}</div> }.into_any()
                                        } else {
                                            ().into_any()
                                        }}
                                        <button
                                            class="slash-item"
                                            data-active=move || is_selected().to_string()
                                            data-testid=testid
                                            on:click=move |_| {
                                                handle_slash_item_click(editor, kind, signals, &runtime);
                                            }
                                        >
                                            <span class="slash-item-row">
                                                <span class="slash-item-icon">{icon}</span>
                                                <span class="slash-item-copy">
                                                    <strong>{label}</strong>
                                                    <span>{description}</span>
                                                </span>
                                                <span class="slash-item-shortcut">{shortcut}</span>
                                            </span>
                                        </button>
                                    </>
                                }
                            }).collect_view()}
                        </div>
                    </div>
                }
                    .into_any()
            } else {
                ().into_any()
            }
        }}
    }
}
