use serde::{Deserialize, Serialize};
use serde_json::Value;
use web_sys::{window, Storage};

const SPIKE_STORAGE_KEY: &str = "mnote.leptos-tiptap-spike.document";

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PersistedSpikeDocument {
    #[serde(default)]
    pub(crate) document_id: Option<String>,
    #[serde(default)]
    pub(crate) workspace_id: Option<String>,
    pub(crate) title: String,
    pub(crate) content: Value,
    #[serde(default)]
    pub(crate) html: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct PersistedDocumentIdentity {
    pub(crate) document_id: Option<String>,
    pub(crate) workspace_id: Option<String>,
}

fn local_storage() -> Option<Storage> {
    window().and_then(|win| win.local_storage().ok().flatten())
}

pub(crate) fn normalize_identity_value(value: Option<String>) -> Option<String> {
    value.and_then(|value| {
        let trimmed = value.trim();
        if trimmed.is_empty() {
            None
        } else {
            Some(trimmed.to_string())
        }
    })
}

pub(crate) fn persisted_document_identity(
    document_id: Option<String>,
    workspace_id: Option<String>,
) -> PersistedDocumentIdentity {
    PersistedDocumentIdentity {
        document_id: normalize_identity_value(document_id),
        workspace_id: normalize_identity_value(workspace_id),
    }
}

pub(crate) fn persisted_document_storage_key(identity: &PersistedDocumentIdentity) -> String {
    match (&identity.workspace_id, &identity.document_id) {
        (Some(workspace_id), Some(document_id)) => {
            format!("{SPIKE_STORAGE_KEY}:{workspace_id}:{document_id}")
        }
        (None, Some(document_id)) => format!("{SPIKE_STORAGE_KEY}:{document_id}"),
        _ => SPIKE_STORAGE_KEY.to_string(),
    }
}

pub(crate) fn persist_document_state(
    identity: &PersistedDocumentIdentity,
    title: &str,
    content: &Value,
    html: Option<String>,
) -> Result<(), String> {
    let payload = PersistedSpikeDocument {
        document_id: identity.document_id.clone(),
        workspace_id: identity.workspace_id.clone(),
        title: title.to_string(),
        content: content.clone(),
        html,
    };
    let raw = serde_json::to_string(&payload).map_err(|err| format!("序列化草稿失败：{err}"))?;
    local_storage()
        .ok_or_else(|| "浏览器 localStorage 不可用".to_string())?
        .set_item(&persisted_document_storage_key(identity), &raw)
        .map_err(|err| format!("写入 localStorage 失败：{err:?}"))
}

pub(crate) fn load_persisted_document(
    identity: &PersistedDocumentIdentity,
) -> Option<PersistedSpikeDocument> {
    let raw = local_storage()?
        .get_item(&persisted_document_storage_key(identity))
        .ok()
        .flatten()?;
    let document = serde_json::from_str::<PersistedSpikeDocument>(&raw).ok()?;
    let stored_identity =
        persisted_document_identity(document.document_id.clone(), document.workspace_id.clone());
    if stored_identity == *identity || identity.document_id.is_none() {
        Some(document)
    } else {
        None
    }
}
