//! BufferStore — 运行时 DocumentBuffer 管理器
//!
//! 职责：
//! - 持有所有已打开文档的 `DocumentBuffer` 实例
//! - 以 `ObjectWorkspacePath` 的稳定复合身份作为 buffer key
//! - tiptap 保存链、AI 写入、外部 watcher 均通过此模块仲裁状态
//!
//! 不是持久化结构，纯内存运行时。

use core_protocol::{
    DocBufferDirtyState, DocumentBuffer, KernelObjectIdentity, KernelObjectKind,
    ObjectWorkspacePath, WorkspaceSourceKind,
};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::time::{SystemTime, UNIX_EPOCH};

/// BufferKey — 从 ObjectWorkspacePath 导出的稳定复合 key。
///
/// 由 workspace_id + source_kind + root_uri + relative_path + object_identity.document_id 组成。
/// 同一 .md 文件由 tiptap、AI、外部 watcher 访问时落到同一 key。
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct BufferKey {
    pub workspace_id: String,
    pub source_kind: String,
    pub root_uri: String,
    pub relative_path: String,
    pub document_id: Option<String>,
}

impl BufferKey {
    /// 从 ObjectWorkspacePath 构建 BufferKey。
    pub fn from_workspace_path(path: &ObjectWorkspacePath) -> Self {
        Self {
            workspace_id: path.workspace_id.clone(),
            source_kind: format!("{:?}", path.source_kind),
            root_uri: path.root_uri.clone(),
            relative_path: path.relative_path.clone(),
            document_id: path.object_identity.document_id.clone(),
        }
    }

    /// 从文档基本属性构建 BufferKey。
    pub fn from_parts(
        workspace_id: &str,
        source_kind: &str,
        root_uri: &str,
        relative_path: &str,
        document_id: Option<String>,
    ) -> Self {
        Self {
            workspace_id: workspace_id.to_string(),
            source_kind: source_kind.to_string(),
            root_uri: root_uri.to_string(),
            relative_path: relative_path.to_string(),
            document_id,
        }
    }
}

/// BufferStore — 运行时文档缓冲区管理器。
///
/// 每个 `AppState` 有一个实例，所有文档打开/保存/冲突检测共享同一状态模型。
#[derive(Debug, Clone)]
pub struct BufferStore {
    inner: Arc<RwLock<BufferStoreInner>>,
}

#[derive(Debug)]
struct BufferStoreInner {
    buffers: HashMap<BufferKey, DocumentBuffer>,
}

#[derive(Debug, Clone)]
pub struct BufferExternalModificationOutcome {
    pub buffer: DocumentBuffer,
    pub self_write_echo: bool,
}

impl BufferStore {
    pub fn new() -> Self {
        Self {
            inner: Arc::new(RwLock::new(BufferStoreInner {
                buffers: HashMap::new(),
            })),
        }
    }

    /// 获取或创建 buffer。
    ///
    /// 如果该 key 已有 buffer，直接返回。否则用 `ObjectWorkspacePath` 创建一个 Clean buffer。
    pub fn get_or_create(&self, path: &ObjectWorkspacePath) -> DocumentBuffer {
        let key = BufferKey::from_workspace_path(path);
        let mut inner = self.inner.write().expect("BufferStore lock");
        if let Some(buf) = inner.buffers.get(&key) {
            return buf.clone();
        }
        let now_ms = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap_or_default()
            .as_millis() as i64;
        let buf = DocumentBuffer {
            workspace_path: path.clone(),
            file_version: None,
            base_content_hash: None,
            current_content_hash: None,
            dirty_state: DocBufferDirtyState::Clean,
            last_loaded_at: Some(now_ms),
            last_saved_at: Some(now_ms),
            external_actor: None,
            last_write_intent_id: None,
            last_save_operation_id: None,
        };
        inner.buffers.insert(key, buf.clone());
        buf
    }

    /// 按 key 获取 buffer（不创建）。
    pub fn get(&self, key: &BufferKey) -> Option<DocumentBuffer> {
        let inner = self.inner.read().expect("BufferStore lock");
        inner.buffers.get(key).cloned()
    }

    /// 更新 buffer。
    pub fn update(&self, key: &BufferKey, buffer: DocumentBuffer) {
        let mut inner = self.inner.write().expect("BufferStore lock");
        inner.buffers.insert(key.clone(), buffer);
    }

    /// 通过 ObjectWorkspacePath 获取 buffer。不存在则返回 None。
    pub fn get_by_path(&self, path: &ObjectWorkspacePath) -> Option<DocumentBuffer> {
        let key = BufferKey::from_workspace_path(path);
        self.get(&key)
    }

    /// 标记保存成功：更新 file_version、base_content_hash、清除 dirty 状态。
    pub fn mark_saved(
        &self,
        path: &ObjectWorkspacePath,
        file_version: String,
        content_hash: String,
    ) -> Option<DocumentBuffer> {
        self.mark_saved_with_operation(path, file_version, content_hash, None, None)
    }

    pub fn mark_saved_with_operation(
        &self,
        path: &ObjectWorkspacePath,
        file_version: String,
        content_hash: String,
        write_intent_id: Option<String>,
        save_operation_id: Option<String>,
    ) -> Option<DocumentBuffer> {
        let key = BufferKey::from_workspace_path(path);
        let mut inner = self.inner.write().expect("BufferStore lock");
        if let Some(buf) = inner.buffers.get_mut(&key) {
            buf.mark_saved_with_operation(
                file_version,
                content_hash,
                write_intent_id,
                save_operation_id,
            );
            Some(buf.clone())
        } else {
            None
        }
    }

    /// 标记外部修改：如果 buffer 是 Clean → ExternalModified；是 Dirty → Stale。
    pub fn mark_external_modified(
        &self,
        path: &ObjectWorkspacePath,
        actor: Option<String>,
    ) -> Option<DocumentBuffer> {
        let key = BufferKey::from_workspace_path(path);
        let mut inner = self.inner.write().expect("BufferStore lock");
        if let Some(buf) = inner.buffers.get_mut(&key) {
            buf.mark_external_modified(actor);
            Some(buf.clone())
        } else {
            None
        }
    }

    /// 带磁盘版本的外部修改标记。
    ///
    /// watcher 会看到 MNote 自己保存产生的文件系统回声；如果 watcher 观测到的
    /// `observed_file_version` 等于最近保存/加载的 `file_version`，说明这不是新的外部修改，
    /// 不应把 Clean 变成 ExternalModified，也不应把当前浏览器正在输入的 Dirty 变成 Stale。
    pub fn mark_external_modified_if_version_changed(
        &self,
        path: &ObjectWorkspacePath,
        observed_file_version: Option<String>,
        actor: Option<String>,
    ) -> Option<DocumentBuffer> {
        self.mark_external_modified_if_version_changed_with_outcome(
            path,
            observed_file_version,
            actor,
        )
        .map(|outcome| outcome.buffer)
    }

    pub fn mark_external_modified_if_version_changed_with_outcome(
        &self,
        path: &ObjectWorkspacePath,
        observed_file_version: Option<String>,
        actor: Option<String>,
    ) -> Option<BufferExternalModificationOutcome> {
        let key = BufferKey::from_workspace_path(path);
        let mut inner = self.inner.write().expect("BufferStore lock");
        let buf = inner.buffers.get_mut(&key)?;
        if let Some(observed) = observed_file_version
            .as_deref()
            .map(str::trim)
            .filter(|value| !value.is_empty())
        {
            if buf.file_version.as_deref() == Some(observed) {
                return Some(BufferExternalModificationOutcome {
                    buffer: buf.clone(),
                    self_write_echo: true,
                });
            }
            buf.file_version = Some(observed.to_string());
        }
        buf.mark_external_modified(actor);
        Some(BufferExternalModificationOutcome {
            buffer: buf.clone(),
            self_write_echo: false,
        })
    }

    /// 标记 buffer 为 dirty（编辑器内容已修改）。
    pub fn mark_dirty(
        &self,
        path: &ObjectWorkspacePath,
        content_hash: String,
    ) -> Option<DocumentBuffer> {
        let key = BufferKey::from_workspace_path(path);
        let mut inner = self.inner.write().expect("BufferStore lock");
        if let Some(buf) = inner.buffers.get_mut(&key) {
            buf.mark_dirty(content_hash);
            Some(buf.clone())
        } else {
            None
        }
    }

    /// 本地文件 rename/move 后重绑打开的 Markdown buffer。
    pub fn rekey_local_folder_markdown(
        &self,
        workspace_id: &str,
        root_uri: &str,
        previous_relative_path: &str,
        previous_document_id: &str,
        next_relative_path: &str,
        next_document_id: &str,
    ) -> Option<DocumentBuffer> {
        let previous_path = build_local_folder_workspace_path(
            workspace_id,
            root_uri,
            previous_relative_path,
            previous_document_id,
        );
        let next_path = build_local_folder_workspace_path(
            workspace_id,
            root_uri,
            next_relative_path,
            next_document_id,
        );
        let previous_key = BufferKey::from_workspace_path(&previous_path);
        let next_key = BufferKey::from_workspace_path(&next_path);
        let mut inner = self.inner.write().expect("BufferStore lock");
        let mut buffer = inner.buffers.remove(&previous_key)?;
        buffer.workspace_path = next_path;
        inner.buffers.insert(next_key, buffer.clone());
        Some(buffer)
    }

    /// 本地文件 delete/archive/purge 后标记打开的 Markdown buffer 已删除。
    pub fn mark_local_folder_markdown_deleted(
        &self,
        workspace_id: &str,
        root_uri: &str,
        relative_path: &str,
        document_id: &str,
    ) -> Option<DocumentBuffer> {
        let path =
            build_local_folder_workspace_path(workspace_id, root_uri, relative_path, document_id);
        let key = BufferKey::from_workspace_path(&path);
        let mut inner = self.inner.write().expect("BufferStore lock");
        if let Some(buf) = inner.buffers.get_mut(&key) {
            buf.mark_deleted();
            Some(buf.clone())
        } else {
            None
        }
    }

    /// 获取或创建 buffer 时设置 file_version 和 base_content_hash（从 aggregate 加载后调用）。
    pub fn init_buffer(
        &self,
        path: &ObjectWorkspacePath,
        file_version: Option<String>,
        base_content_hash: Option<String>,
    ) -> DocumentBuffer {
        let mut buf = self.get_or_create(path);
        if file_version.is_some() || base_content_hash.is_some() {
            if let Some(fv) = file_version {
                buf.file_version = Some(fv);
            }
            if let Some(ch) = base_content_hash {
                buf.base_content_hash = Some(ch);
            }
            let key = BufferKey::from_workspace_path(path);
            self.update(&key, buf.clone());
        }
        buf
    }

    /// 返回当前 buffer 数量（仅用于测试和监控）。
    pub fn buffer_count(&self) -> usize {
        let inner = self.inner.read().expect("BufferStore lock");
        inner.buffers.len()
    }

    /// 清除所有 buffers（测试用途）。
    pub fn clear(&self) {
        let mut inner = self.inner.write().expect("BufferStore lock");
        inner.buffers.clear();
    }

    /// 返回当前所有 buffers 的拷贝（测试/监控用途）。
    pub fn all_buffers(&self) -> Vec<DocumentBuffer> {
        let inner = self.inner.read().expect("BufferStore lock");
        inner.buffers.values().cloned().collect()
    }
}

impl Default for BufferStore {
    fn default() -> Self {
        Self::new()
    }
}

// ── helpers ──────────────────────────────────────────────────────────

/// 从 ObjectWorkspacePath 构建 buffer key 字符串（用于日志/调试）。
pub fn buffer_key_string(path: &ObjectWorkspacePath) -> String {
    format!(
        "{}:{}:{}:{}",
        path.workspace_id,
        path.root_uri,
        path.relative_path,
        path.object_identity.document_id.as_deref().unwrap_or("_")
    )
}

/// 从本地文件夹写入上下文的参数构建 ObjectWorkspacePath。
///
/// 在 save_local_markdown_page、watcher event 和 Hermes 写入链中统一使用此函数构造路径。
pub fn build_local_folder_workspace_path(
    workspace_id: &str,
    root_uri: &str,
    relative_path: &str,
    document_id: &str,
) -> ObjectWorkspacePath {
    ObjectWorkspacePath {
        workspace_id: workspace_id.to_string(),
        source_kind: WorkspaceSourceKind::LocalFolder,
        root_uri: root_uri.to_string(),
        relative_path: relative_path.to_string(),
        object_identity: KernelObjectIdentity {
            object_kind: KernelObjectKind::Page,
            document_id: Some(document_id.to_string()),
            block_id: None,
            asset_id: None,
        },
        resource_kind: Some("document".to_string()),
    }
}

// ── tests ────────────────────────────────────────────────────────────

#[cfg(test)]
mod tests {
    use super::*;
    use core_protocol::{
        DocBufferDirtyState, DocumentBuffer, KernelObjectIdentity, KernelObjectKind,
        ObjectWorkspacePath, WorkspaceSourceKind,
    };

    fn make_test_workspace_path(
        workspace_id: &str,
        relative_path: &str,
        document_id: &str,
    ) -> ObjectWorkspacePath {
        ObjectWorkspacePath {
            workspace_id: workspace_id.to_string(),
            source_kind: WorkspaceSourceKind::LocalFolder,
            root_uri: "file:///tmp/test-root".to_string(),
            relative_path: relative_path.to_string(),
            object_identity: KernelObjectIdentity {
                object_kind: KernelObjectKind::Page,
                document_id: Some(document_id.to_string()),
                block_id: None,
                asset_id: None,
            },
            resource_kind: Some("document".to_string()),
        }
    }

    fn make_fresh_buffer(path: &ObjectWorkspacePath) -> DocumentBuffer {
        DocumentBuffer {
            workspace_path: path.clone(),
            file_version: Some("v1".to_string()),
            base_content_hash: Some("sha256:base".to_string()),
            current_content_hash: None,
            dirty_state: DocBufferDirtyState::Clean,
            last_loaded_at: Some(1_700_000_000_000i64),
            last_saved_at: Some(1_700_000_000_001i64),
            external_actor: None,
            last_write_intent_id: None,
            last_save_operation_id: None,
        }
    }

    #[test]
    fn document_buffer_reuses_workspace_path_key() {
        let store = BufferStore::new();
        let path_a = make_test_workspace_path("ws_1", "doc.md", "local-md:doc.md");
        let path_b = make_test_workspace_path("ws_1", "doc.md", "local-md:doc.md");

        // 同一个路径应得同一个 buffer
        let buf1 = store.get_or_create(&path_a);
        let buf2 = store.get_or_create(&path_b);

        assert_eq!(
            buf1.workspace_path.relative_path,
            buf2.workspace_path.relative_path
        );
        assert_eq!(
            BufferKey::from_workspace_path(&path_a),
            BufferKey::from_workspace_path(&path_b)
        );
    }

    #[test]
    fn document_buffer_marks_external_modified_for_clean_buffer() {
        let store = BufferStore::new();
        let path = make_test_workspace_path("ws_1", "doc.md", "local-md:doc.md");
        let mut buf = make_fresh_buffer(&path);
        let key = BufferKey::from_workspace_path(&path);
        store.update(&key, buf.clone());

        buf.mark_external_modified(Some("external-editor".into()));
        store.update(&key, buf);

        let result = store.get(&key).expect("buffer should exist");
        assert_eq!(result.dirty_state, DocBufferDirtyState::ExternalModified);
        assert_eq!(result.external_actor.as_deref(), Some("external-editor"));
    }

    #[test]
    fn document_buffer_marks_stale_for_dirty_buffer() {
        let store = BufferStore::new();
        let path = make_test_workspace_path("ws_1", "doc.md", "local-md:doc.md");
        let mut buf = make_fresh_buffer(&path);
        let key = BufferKey::from_workspace_path(&path);

        // 先 mark_dirty
        buf.mark_dirty("sha256:dirty".into());
        store.update(&key, buf);

        // 外部修改 → 应变为 Stale
        let result = store.mark_external_modified(&path, Some("external-editor".into()));
        assert!(result.is_some());
        assert_eq!(
            result.as_ref().unwrap().dirty_state,
            DocBufferDirtyState::Stale
        );
        assert_eq!(
            result.as_ref().unwrap().external_actor.as_deref(),
            Some("external-editor")
        );
    }

    #[test]
    fn document_buffer_ignores_watcher_echo_for_last_saved_version() {
        let store = BufferStore::new();
        let path = make_test_workspace_path("ws_1", "doc.md", "local-md:doc.md");
        let key = BufferKey::from_workspace_path(&path);
        store.update(&key, make_fresh_buffer(&path));

        let clean_echo = store
            .mark_external_modified_if_version_changed(
                &path,
                Some("v1".into()),
                Some("external-editor".into()),
            )
            .expect("buffer");
        assert_eq!(clean_echo.dirty_state, DocBufferDirtyState::Clean);
        assert_eq!(clean_echo.external_actor, None);

        let dirty = store
            .mark_dirty(&path, "sha256:dirty".into())
            .expect("dirty");
        assert_eq!(dirty.dirty_state, DocBufferDirtyState::Dirty);
        let dirty_echo = store
            .mark_external_modified_if_version_changed(
                &path,
                Some("v1".into()),
                Some("external-editor".into()),
            )
            .expect("buffer");
        assert_eq!(dirty_echo.dirty_state, DocBufferDirtyState::Dirty);
        assert_eq!(dirty_echo.external_actor, None);
    }

    #[test]
    fn document_buffer_marks_stale_when_dirty_buffer_sees_new_version() {
        let store = BufferStore::new();
        let path = make_test_workspace_path("ws_1", "doc.md", "local-md:doc.md");
        let key = BufferKey::from_workspace_path(&path);
        let mut buf = make_fresh_buffer(&path);
        buf.mark_dirty("sha256:dirty".into());
        store.update(&key, buf);

        let result = store
            .mark_external_modified_if_version_changed(
                &path,
                Some("v2".into()),
                Some("external-editor".into()),
            )
            .expect("buffer");
        assert_eq!(result.dirty_state, DocBufferDirtyState::Stale);
        assert_eq!(result.file_version.as_deref(), Some("v2"));
        assert_eq!(result.external_actor.as_deref(), Some("external-editor"));
    }

    #[test]
    fn document_buffer_records_save_operation_and_reports_self_write_echo() {
        let store = BufferStore::new();
        let path = make_test_workspace_path("ws_1", "doc.md", "local-md:doc.md");
        let key = BufferKey::from_workspace_path(&path);
        let mut buf = make_fresh_buffer(&path);
        buf.mark_dirty("sha256:dirty".into());
        store.update(&key, buf);

        let saved = store
            .mark_saved_with_operation(
                &path,
                "v2".into(),
                "sha256:v2".into(),
                Some("intent:editor:abc".into()),
                Some("save:op:abc".into()),
            )
            .expect("保存后应更新 buffer");
        assert_eq!(
            saved.last_write_intent_id.as_deref(),
            Some("intent:editor:abc")
        );
        assert_eq!(saved.last_save_operation_id.as_deref(), Some("save:op:abc"));

        let outcome = store
            .mark_external_modified_if_version_changed_with_outcome(
                &path,
                Some("v2".into()),
                Some("external-editor".into()),
            )
            .expect("watcher 回声应返回 outcome");
        assert!(
            outcome.self_write_echo,
            "同版本 watcher 回声应标记为自写回声"
        );
        assert_eq!(
            outcome.buffer.last_write_intent_id.as_deref(),
            Some("intent:editor:abc")
        );
        assert_eq!(
            outcome.buffer.last_save_operation_id.as_deref(),
            Some("save:op:abc")
        );
        assert_eq!(outcome.buffer.dirty_state, DocBufferDirtyState::Clean);
    }

    #[test]
    fn document_buffer_mark_saved_after_page_body_write() {
        let store = BufferStore::new();
        let path = make_test_workspace_path("ws_1", "doc.md", "local-md:doc.md");
        let mut buf = make_fresh_buffer(&path);
        let key = BufferKey::from_workspace_path(&path);
        buf.mark_dirty("sha256:dirty".into());
        store.update(&key, buf);

        // 保存后应变为 Clean，更新 file_version
        let result = store.mark_saved(&path, "v2".into(), "sha256:saved".into());
        assert!(result.is_some());
        let buf = result.unwrap();
        assert_eq!(buf.dirty_state, DocBufferDirtyState::Clean);
        assert_eq!(buf.file_version.as_deref(), Some("v2"));
        assert_eq!(buf.base_content_hash.as_deref(), Some("sha256:saved"));
        assert!(!buf.is_dirty());
    }

    #[test]
    fn document_buffer_different_paths_different_keys() {
        let store = BufferStore::new();
        let path_a = make_test_workspace_path("ws_1", "doc-a.md", "local-md:doc-a.md");
        let path_b = make_test_workspace_path("ws_2", "doc-b.md", "local-md:doc-b.md");

        let key_a = BufferKey::from_workspace_path(&path_a);
        let key_b = BufferKey::from_workspace_path(&path_b);

        assert_ne!(key_a, key_b);

        let buf1 = store.get_or_create(&path_a);
        let buf2 = store.get_or_create(&path_b);

        assert_ne!(
            buf1.workspace_path.workspace_id,
            buf2.workspace_path.workspace_id
        );
    }

    #[test]
    fn document_buffer_reuses_across_multiple_get_or_create_calls() {
        let store = BufferStore::new();
        let path = make_test_workspace_path("ws_1", "doc.md", "local-md:doc.md");

        let buf1 = store.get_or_create(&path);
        let buf2 = store.get_or_create(&path);

        // 同一个路径的 buffer 应该共享同一 key
        assert_eq!(
            BufferKey::from_workspace_path(&buf1.workspace_path),
            BufferKey::from_workspace_path(&buf2.workspace_path)
        );
        assert_eq!(store.buffer_count(), 1);
    }

    #[test]
    fn document_buffer_init_buffer_sets_file_version() {
        let store = BufferStore::new();
        let path = make_test_workspace_path("ws_1", "doc.md", "local-md:doc.md");

        let buf = store.init_buffer(&path, Some("v3".into()), Some("sha256:v3base".into()));
        assert_eq!(buf.file_version.as_deref(), Some("v3"));
        assert_eq!(buf.base_content_hash.as_deref(), Some("sha256:v3base"));
        assert_eq!(buf.dirty_state, DocBufferDirtyState::Clean);

        // 再次获取应保留状态
        let buf2 = store.get_or_create(&path);
        assert_eq!(buf2.file_version.as_deref(), Some("v3"));
        assert_eq!(store.buffer_count(), 1);
    }

    #[test]
    fn document_buffer_mark_dirty_and_saved_round_trip() {
        let store = BufferStore::new();
        let path = make_test_workspace_path("ws_1", "doc-cycle.md", "local-md:doc-cycle.md");

        // init → mark_dirty → mark_saved → 应回到 Clean
        let _ = store.init_buffer(&path, Some("v1".into()), Some("sha256:base".into()));
        let dirty = store.mark_dirty(&path, "sha256:dirty".into());
        assert!(dirty.is_some());
        assert_eq!(dirty.unwrap().dirty_state, DocBufferDirtyState::Dirty);

        let saved = store.mark_saved(&path, "v2".into(), "sha256:saved".into());
        assert!(saved.is_some());
        let buf = saved.unwrap();
        assert_eq!(buf.dirty_state, DocBufferDirtyState::Clean);
        assert_eq!(buf.file_version.as_deref(), Some("v2"));
        assert_eq!(buf.base_content_hash.as_deref(), Some("sha256:saved"));
    }

    #[test]
    fn document_buffer_rekeys_after_local_file_operation_rename() {
        let store = BufferStore::new();
        let root_uri = "file:///tmp/mnote-buffer-rekey";
        let old_path = build_local_folder_workspace_path(
            "local:test",
            root_uri,
            "docs/Old.md",
            "local-md:docs~2FOld.md",
        );
        store.init_buffer(&old_path, Some("v1".into()), Some("sha256:old".into()));

        let rekeyed = store
            .rekey_local_folder_markdown(
                "local:test",
                root_uri,
                "docs/Old.md",
                "local-md:docs~2FOld.md",
                "docs/New.md",
                "local-md:docs~2FNew.md",
            )
            .expect("buffer should be rekeyed");

        assert_eq!(rekeyed.workspace_path.relative_path, "docs/New.md");
        assert_eq!(
            rekeyed
                .workspace_path
                .object_identity
                .document_id
                .as_deref(),
            Some("local-md:docs~2FNew.md")
        );
        assert!(store.get_by_path(&old_path).is_none());
        let next_path = build_local_folder_workspace_path(
            "local:test",
            root_uri,
            "docs/New.md",
            "local-md:docs~2FNew.md",
        );
        assert!(store.get_by_path(&next_path).is_some());
    }

    #[test]
    fn document_buffer_marks_deleted_after_local_file_operation_archive() {
        let store = BufferStore::new();
        let root_uri = "file:///tmp/mnote-buffer-delete";
        let path = build_local_folder_workspace_path(
            "local:test",
            root_uri,
            "docs/Delete.md",
            "local-md:docs~2FDelete.md",
        );
        store.get_or_create(&path);

        let deleted = store
            .mark_local_folder_markdown_deleted(
                "local:test",
                root_uri,
                "docs/Delete.md",
                "local-md:docs~2FDelete.md",
            )
            .expect("buffer should be marked deleted");

        assert_eq!(deleted.dirty_state, DocBufferDirtyState::Deleted);
    }
}
