use crate::editor_runtime::persistence::{persist_document_state, PersistedDocumentIdentity};
use leptos::prelude::{GetUntracked, ReadSignal, Set, Update, WriteSignal};
use leptos_tiptap::{TiptapContent, TiptapEditorHandle};
use serde_json::{json, Value};

pub(crate) fn read_editor_snapshot(editor: TiptapEditorHandle) -> (String, Value, String) {
    let html = editor
        .get_html()
        .unwrap_or_else(|err| format!("读取 HTML 失败：{err}"));
    let json_value = editor.get_json().unwrap_or_else(|err| {
        json!({
            "type": "error",
            "message": format!("读取 JSON 失败：{err}"),
        })
    });
    let json_text = serde_json::to_string_pretty(&json_value)
        .unwrap_or_else(|err| format!("格式化 JSON 失败：{err}"));
    (html, json_value, json_text)
}

pub(crate) fn sync_editor_outputs(
    editor: TiptapEditorHandle,
    set_html_output: WriteSignal<String>,
    set_document_json: WriteSignal<Value>,
    set_json_output: WriteSignal<String>,
) -> Value {
    let (html, json_value, json_text) = read_editor_snapshot(editor);
    set_html_output.set(html);
    set_document_json.set(json_value.clone());
    set_json_output.set(json_text);
    json_value
}

pub(crate) fn apply_document_update(
    editor: TiptapEditorHandle,
    persisted_identity: &PersistedDocumentIdentity,
    next_document: Value,
    set_dirty_count: WriteSignal<u32>,
    set_html_output: WriteSignal<String>,
    set_document_json: WriteSignal<Value>,
    set_json_output: WriteSignal<String>,
    title: ReadSignal<String>,
    set_command_feedback: WriteSignal<String>,
    success_message: impl Into<String>,
) {
    let success_message = success_message.into();
    let next_payload = next_document
        .get("content")
        .and_then(Value::as_array)
        .cloned()
        .map(Value::Array)
        .unwrap_or_else(|| next_document.clone());
    match editor.set_content(TiptapContent::json(next_payload)) {
        Ok(()) => {
            persist_current_editor_snapshot(
                editor,
                persisted_identity,
                set_dirty_count,
                set_html_output,
                set_document_json,
                set_json_output,
                title,
                set_command_feedback,
                success_message,
            );
        }
        Err(err) => {
            set_command_feedback.set(format!("更新文档失败：{err}"));
        }
    }
}

pub(crate) fn sync_persisted_editor_command(
    editor: TiptapEditorHandle,
    persisted_identity: &PersistedDocumentIdentity,
    set_dirty_count: WriteSignal<u32>,
    set_html_output: WriteSignal<String>,
    set_document_json: WriteSignal<Value>,
    set_json_output: WriteSignal<String>,
    title: ReadSignal<String>,
    set_command_feedback: WriteSignal<String>,
    success_message: impl Into<String>,
) {
    persist_current_editor_snapshot(
        editor,
        persisted_identity,
        set_dirty_count,
        set_html_output,
        set_document_json,
        set_json_output,
        title,
        set_command_feedback,
        success_message.into(),
    );
}

fn persist_current_editor_snapshot(
    editor: TiptapEditorHandle,
    persisted_identity: &PersistedDocumentIdentity,
    set_dirty_count: WriteSignal<u32>,
    set_html_output: WriteSignal<String>,
    set_document_json: WriteSignal<Value>,
    set_json_output: WriteSignal<String>,
    title: ReadSignal<String>,
    set_command_feedback: WriteSignal<String>,
    success_message: String,
) {
    set_dirty_count.update(|count| *count += 1);
    let (html, snapshot, json_text) = read_editor_snapshot(editor);
    set_html_output.set(html.clone());
    set_document_json.set(snapshot.clone());
    set_json_output.set(json_text);
    match persist_document_state(
        persisted_identity,
        &title.get_untracked(),
        &snapshot,
        Some(html),
    ) {
        Ok(()) => set_command_feedback.set(format!("{success_message}，并已写入本地草稿")),
        Err(err) => set_command_feedback.set(format!("{success_message}，但本地保存失败：{err}")),
    }
}
