/*
 * Copyright (c) 2025, Modern Notes Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.micode.notes.data;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;

import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.model.Note;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;

/**
 * 笔记数据仓库
 * <p>
 * 负责数据访问逻辑，统一管理Content Provider和缓存
 * 提供笔记的增删改查、搜索、统计等功能
 * </p>
 * <p>
 * 使用Executor进行后台线程数据访问，避免阻塞UI线程
 * </p>
 *
 * @see Note
 * @see Notes
 */
public class NotesRepository {

    /**
     * 笔记信息类
     * <p>
     * 存储从数据库查询的笔记基本信息
     * </p>
     */
    public static class NoteInfo {
        public long id;
        public String title;
        public String snippet;
        public long parentId;
        public long createdDate;
        public long modifiedDate;
        public int type;
        public int localModified;
        public int bgColorId;
        public boolean isPinned; // 新增置顶字段

        public NoteInfo() {}

        public long getId() {
            return id;
        }

        public long getParentId() {
            return parentId;
        }

        public String getNoteDataValue() {
            return snippet;
        }
    }
    private static final String TAG = "NotesRepository";

    private final ContentResolver contentResolver;
    private final ExecutorService executor;

    // 选择条件常量
    private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + " = ?";
    private static final String ROOT_FOLDER_SELECTION = "(" +
        NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " +
        NoteColumns.PARENT_ID + "=?) OR (" +
        NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " +
        NoteColumns.NOTES_COUNT + ">0)";

    /**
     * 数据访问回调接口
     * <p>
     * 统一的数据访问结果回调机制
     * </p>
     *
     * @param <T> 返回数据类型
     */
    public interface Callback<T> {
        /**
         * 成功回调
         *
         * @param result 返回的结果数据
         */
        void onSuccess(T result);

        /**
         * 失败回调
         *
         * @param error 异常对象
         */
        void onError(Exception error);
    }

    /**
     * 从 Cursor 创建 NoteInfo 对象
     *
     * @param cursor 数据库游标
     * @return NoteInfo 对象
     */
    private NoteInfo noteFromCursor(Cursor cursor) {
        NoteInfo noteInfo = new NoteInfo();
        noteInfo.id = cursor.getLong(cursor.getColumnIndexOrThrow(NoteColumns.ID));
        noteInfo.title = cursor.getString(cursor.getColumnIndexOrThrow(NoteColumns.SNIPPET));
        noteInfo.snippet = cursor.getString(cursor.getColumnIndexOrThrow(NoteColumns.SNIPPET));
        noteInfo.parentId = cursor.getLong(cursor.getColumnIndexOrThrow(NoteColumns.PARENT_ID));
        noteInfo.createdDate = cursor.getLong(cursor.getColumnIndexOrThrow(NoteColumns.CREATED_DATE));
        noteInfo.modifiedDate = cursor.getLong(cursor.getColumnIndexOrThrow(NoteColumns.MODIFIED_DATE));
        noteInfo.type = cursor.getInt(cursor.getColumnIndexOrThrow(NoteColumns.TYPE));
        noteInfo.localModified = cursor.getInt(cursor.getColumnIndexOrThrow(NoteColumns.LOCAL_MODIFIED));
        
        int bgColorIdIndex = cursor.getColumnIndex(NoteColumns.BG_COLOR_ID);
        if (bgColorIdIndex != -1 && !cursor.isNull(bgColorIdIndex)) {
            noteInfo.bgColorId = cursor.getInt(bgColorIdIndex);
        } else {
            noteInfo.bgColorId = 0;
        }

        int topIndex = cursor.getColumnIndex(NoteColumns.TOP);
        if (topIndex != -1) {
            noteInfo.isPinned = cursor.getInt(topIndex) > 0;
        }
        
        return noteInfo;
    }

    /**
     * 构造函数
     * <p>
     * 初始化ContentResolver和线程池
     * </p>
     *
     * @param contentResolver Content解析器
     */
    public NotesRepository(ContentResolver contentResolver) {
        this.contentResolver = contentResolver;
        // 使用单线程Executor确保数据访问的顺序性
        this.executor = java.util.concurrent.Executors.newSingleThreadExecutor();
        Log.d(TAG, "NotesRepository initialized");
    }

    /**
     * 获取指定文件夹的笔记列表
     * <p>
     * 支持根文件夹（显示所有笔记）和子文件夹两种模式
     * </p>
     *
     * @param folderId 文件夹ID，{@link Notes#ID_ROOT_FOLDER} 表示根文件夹
     * @param callback 回调接口
     */
    public void getNotes(long folderId, Callback<List<NoteInfo>> callback) {
        executor.execute(() -> {
            try {
                List<NoteInfo> notes = queryNotes(folderId);
                callback.onSuccess(notes);
                Log.d(TAG, "Successfully loaded notes for folder: " + folderId);
            } catch (Exception e) {
                Log.e(TAG, "Failed to load notes for folder: " + folderId, e);
                callback.onError(e);
            }
        });
    }

    /**
     * 查询笔记列表（内部方法）
     * <p>
     * 同时返回文件夹和便签，文件夹显示在便签之前
     * </p>
     *
     * @param folderId 文件夹ID
     * @return 笔记列表（包含文件夹和便签）
     */
    private List<NoteInfo> queryNotes(long folderId) {
        List<NoteInfo> notes = new ArrayList<>();
        List<NoteInfo> folders = new ArrayList<>();
        List<NoteInfo> normalNotes = new ArrayList<>();

        String selection;
        String[] selectionArgs;

        if (folderId == Notes.ID_ROOT_FOLDER) {
            // 根文件夹：显示所有文件夹和便签
            selection = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?) OR (" +
                        NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)";
            selectionArgs = new String[]{String.valueOf(Notes.ID_ROOT_FOLDER)};
        } else {
            // 子文件夹：显示该文件夹下的文件夹和便签
            selection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM;
            selectionArgs = new String[]{String.valueOf(folderId)};
        }

        Cursor cursor = contentResolver.query(
            Notes.CONTENT_NOTE_URI,
            null,
            selection,
            selectionArgs,
            NoteColumns.MODIFIED_DATE + " DESC"
        );

        if (cursor != null) {
            try {
                while (cursor.moveToNext()) {
                    NoteInfo note = noteFromCursor(cursor);
                    if (note.type == Notes.TYPE_FOLDER) {
                        // 文件夹单独收集
                        folders.add(note);
                    } else if (note.type == Notes.TYPE_NOTE) {
                        // 便签收集
                        normalNotes.add(note);
                    } else if (note.type == Notes.TYPE_SYSTEM && note.id == Notes.ID_CALL_RECORD_FOLDER) {
                        // 通话记录文件夹
                        folders.add(note);
                    }
                }
                Log.d(TAG, "Query returned " + folders.size() + " folders and " + normalNotes.size() + " notes");
            } finally {
                cursor.close();
            }
        }

        // 文件夹按修改时间倒序排列
        folders.sort((a, b) -> Long.compare(b.modifiedDate, a.modifiedDate));
        // 便签按修改时间倒序排列
        normalNotes.sort((a, b) -> {
            // 首先按置顶状态排序（置顶在前）
            if (a.isPinned != b.isPinned) {
                return a.isPinned ? -1 : 1;
            }
            // 其次按修改时间倒序排列
            return Long.compare(b.modifiedDate, a.modifiedDate);
        });

        // 合并：文件夹在前，便签在后
        notes.addAll(folders);
        notes.addAll(normalNotes);

        return notes;
    }

    /**
     * 查询单个文件夹信息
     *
     * @param folderId 文件夹ID
     * @return 文件夹信息，如果不存在返回null
     */
    public NoteInfo getFolderInfo(long folderId) {
        if (folderId == Notes.ID_ROOT_FOLDER) {
            NoteInfo root = new NoteInfo();
            root.id = Notes.ID_ROOT_FOLDER;
            root.title = "我的便签";
            root.snippet = "我的便签";
            root.type = Notes.TYPE_FOLDER;
            return root;
        }

        String selection = NoteColumns.ID + "=?";
        String[] selectionArgs = new String[]{String.valueOf(folderId)};

        Cursor cursor = contentResolver.query(
            Notes.CONTENT_NOTE_URI,
            null,
            selection,
            selectionArgs,
            null
        );

        if (cursor != null) {
            try {
                if (cursor.moveToFirst()) {
                    return noteFromCursor(cursor);
                }
            } finally {
                cursor.close();
            }
        }
        return null;
    }

    /**
     * 查询文件夹的父文件夹ID（异步版本）
     *
     * @param folderId 文件夹ID
     * @param callback 回调接口，返回父文件夹ID
     */
    public void getParentFolderId(long folderId, Callback<Long> callback) {
        executor.execute(() -> {
            try {
                long parentId = getParentFolderId(folderId);
                callback.onSuccess(parentId);
            } catch (Exception e) {
                callback.onError(e);
            }
        });
    }

    /**
     * 查询文件夹的父文件夹ID
     *
     * @param folderId 文件夹ID
     * @return 父文件夹ID，如果不存在返回根文件夹ID
     */
    public long getParentFolderId(long folderId) {
        if (folderId == Notes.ID_ROOT_FOLDER || folderId == Notes.ID_CALL_RECORD_FOLDER) {
            return Notes.ID_ROOT_FOLDER;
        }

        NoteInfo folder = getFolderInfo(folderId);
        if (folder != null) {
            return folder.parentId;
        }
        return Notes.ID_ROOT_FOLDER;
    }

    /**
     * 获取文件夹路径（从根到当前）
     *
     * @param folderId 当前文件夹ID
     * @return 文件夹路径列表（从根到当前）
     */
    public List<NoteInfo> getFolderPath(long folderId) {
        List<NoteInfo> path = new ArrayList<>();
        long currentId = folderId;

        while (currentId != Notes.ID_ROOT_FOLDER) {
            NoteInfo folder = getFolderInfo(currentId);
            if (folder == null) {
                break;
            }
            path.add(0, folder); // 添加到列表头部
            currentId = folder.parentId;
        }

        // 添加根文件夹
        NoteInfo root = new NoteInfo();
        root.id = Notes.ID_ROOT_FOLDER;
        root.title = "我的便签";
        root.snippet = "我的便签";
        root.type = Notes.TYPE_FOLDER;
        path.add(0, root);

        return path;
    }

    /**
     * 获取文件夹路径（异步版本）
     *
     * @param folderId 当前文件夹ID
     * @param callback 回调接口，返回文件夹路径列表
     */
    public void getFolderPath(long folderId, Callback<List<NoteInfo>> callback) {
        executor.execute(() -> {
            try {
                List<NoteInfo> path = getFolderPath(folderId);
                callback.onSuccess(path);
            } catch (Exception e) {
                callback.onError(e);
            }
        });
    }

    /**
     * 创建新文件夹
     *
     * @param parentId 父文件夹ID
     * @param name 文件夹名称
     * @param callback 回调接口，返回新文件夹的ID
     */
    public void createFolder(long parentId, String name, Callback<Long> callback) {
        executor.execute(() -> {
            try {
                ContentValues values = new ContentValues();
                long currentTime = System.currentTimeMillis();

                values.put(NoteColumns.PARENT_ID, parentId);
                values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
                values.put(NoteColumns.SNIPPET, name);
                values.put(NoteColumns.CREATED_DATE, currentTime);
                values.put(NoteColumns.MODIFIED_DATE, currentTime);
                values.put(NoteColumns.LOCAL_MODIFIED, 1);
                values.put(NoteColumns.NOTES_COUNT, 0);

                Uri uri = contentResolver.insert(Notes.CONTENT_NOTE_URI, values);

                Long folderId = 0L;
                if (uri != null) {
                    try {
                        folderId = ContentUris.parseId(uri);
                    } catch (Exception e) {
                        Log.e(TAG, "Failed to parse folder ID from URI", e);
                    }
                }

                callback.onSuccess(folderId);
                Log.d(TAG, "Successfully created folder: " + name + " with ID: " + folderId);
            } catch (Exception e) {
                Log.e(TAG, "Failed to create folder: " + name, e);
                callback.onError(e);
            }
        });
    }

    /**
     * 创建新笔记
     * <p>
     * 在指定文件夹下创建一个空笔记
     * </p>
     *
     * @param folderId 父文件夹ID
     * @param callback 回调接口，返回新笔记的ID
     */
    public void createNote(long folderId, Callback<Long> callback) {
        executor.execute(() -> {
            try {
                ContentValues values = new ContentValues();
                long currentTime = System.currentTimeMillis();

                values.put(NoteColumns.PARENT_ID, folderId);
                values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
                values.put(NoteColumns.CREATED_DATE, currentTime);
                values.put(NoteColumns.MODIFIED_DATE, currentTime);
                values.put(NoteColumns.LOCAL_MODIFIED, 1);
                values.put(NoteColumns.SNIPPET, "");

                Uri uri = contentResolver.insert(Notes.CONTENT_NOTE_URI, values);

                Long noteId = 0L;
                if (uri != null) {
                    try {
                        noteId = ContentUris.parseId(uri);
                    } catch (Exception e) {
                        Log.e(TAG, "Failed to parse note ID from URI", e);
                    }
                }

                if (noteId > 0) {
                    callback.onSuccess(noteId);
                    Log.d(TAG, "Successfully created note with ID: " + noteId);
                } else {
                    callback.onError(new IllegalStateException("Failed to create note, invalid ID returned"));
                }
            } catch (Exception e) {
                Log.e(TAG, "Failed to create note", e);
                callback.onError(e);
            }
        });
    }

    /**
     * 更新笔记内容
     * <p>
     * 更新笔记的标题和内容，自动更新修改时间和本地修改标志
     * </p>
     *
     * @param noteId 笔记ID
     * @param content 笔记内容
     * @param callback 回调接口，返回影响的行数
     */
    public void updateNote(long noteId, String content, Callback<Integer> callback) {
        executor.execute(() -> {
            try {
                ContentValues values = new ContentValues();
                long currentTime = System.currentTimeMillis();

                values.put(NoteColumns.SNIPPET, extractSnippet(content));
                values.put(NoteColumns.MODIFIED_DATE, currentTime);
                values.put(NoteColumns.LOCAL_MODIFIED, 1);

                Uri uri = ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
                int rows = contentResolver.update(uri, values, null, null);

                if (rows > 0) {
                    // 查询现有的文本数据记录
                    Cursor cursor = contentResolver.query(
                        Notes.CONTENT_DATA_URI,
                        new String[]{DataColumns.ID},
                        DataColumns.NOTE_ID + " = ? AND " + DataColumns.MIME_TYPE + " = ?",
                        new String[]{String.valueOf(noteId), TextNote.CONTENT_ITEM_TYPE},
                        null
                    );

                    long dataId = 0;
                    if (cursor != null) {
                        try {
                            if (cursor.moveToFirst()) {
                                dataId = cursor.getLong(cursor.getColumnIndexOrThrow(DataColumns.ID));
                            }
                        } finally {
                            cursor.close();
                        }
                    }

                    // 更新或插入文本数据
                    ContentValues dataValues = new ContentValues();
                    dataValues.put(DataColumns.CONTENT, content);

                    if (dataId > 0) {
                        // 更新现有记录
                        Uri dataUri = ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId);
                        int dataRows = contentResolver.update(dataUri, dataValues, null, null);
                        if (dataRows > 0) {
                            callback.onSuccess(rows);
                            Log.d(TAG, "Successfully updated note: " + noteId);
                        } else {
                            callback.onError(new RuntimeException("Failed to update note data"));
                        }
                    } else {
                        // 插入新记录
                        dataValues.put(DataColumns.NOTE_ID, noteId);
                        dataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
                        Uri dataUri = contentResolver.insert(Notes.CONTENT_DATA_URI, dataValues);
                        if (dataUri != null) {
                            callback.onSuccess(rows);
                            Log.d(TAG, "Successfully updated note: " + noteId);
                        } else {
                            callback.onError(new RuntimeException("Failed to insert note data"));
                        }
                    }
                } else {
                    callback.onError(new RuntimeException("No note found with ID: " + noteId));
                }
            } catch (Exception e) {
                Log.e(TAG, "Failed to update note: " + noteId, e);
                callback.onError(e);
            }
        });
    }

    /**
     * 删除笔记
     * <p>
     * 将笔记移动到回收站文件夹
     * </p>
     *
     * @param noteId 笔记ID
     * @param callback 回调接口，返回影响的行数
     */
    public void deleteNote(long noteId, Callback<Integer> callback) {
        executor.execute(() -> {
            try {
                ContentValues values = new ContentValues();
                values.put(NoteColumns.PARENT_ID, Notes.ID_TRASH_FOLER);
                values.put(NoteColumns.LOCAL_MODIFIED, 1);

                Uri uri = ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
                int rows = contentResolver.update(uri, values, null, null);

                if (rows > 0) {
                    callback.onSuccess(rows);
                    Log.d(TAG, "Successfully moved note to trash: " + noteId);
                } else {
                    callback.onError(new RuntimeException("No note found with ID: " + noteId));
                }
            } catch (Exception e) {
                Log.e(TAG, "Failed to delete note: " + noteId, e);
                callback.onError(e);
            }
        });
    }

    /**
     * 批量删除笔记
     * <p>
     * 将多个笔记移动到回收站文件夹
     * </p>
     *
     * @param noteIds 笔记ID列表
     * @param callback 回调接口，返回影响的行数
     */
    public void deleteNotes(List<Long> noteIds, Callback<Integer> callback) {
        executor.execute(() -> {
            try {
                if (noteIds == null || noteIds.isEmpty()) {
                    callback.onError(new IllegalArgumentException("Note IDs list is empty"));
                    return;
                }

                int totalRows = 0;
                for (Long noteId : noteIds) {
                    ContentValues values = new ContentValues();
                    values.put(NoteColumns.PARENT_ID, Notes.ID_TRASH_FOLER);
                    values.put(NoteColumns.LOCAL_MODIFIED, 1);

                    Uri uri = ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
                    int rows = contentResolver.update(uri, values, null, null);
                    totalRows += rows;
                }

                if (totalRows > 0) {
                    callback.onSuccess(totalRows);
                    Log.d(TAG, "Successfully moved " + totalRows + " notes to trash");
                } else {
                    callback.onError(new RuntimeException("No notes were deleted"));
                }
            } catch (Exception e) {
                Log.e(TAG, "Failed to batch delete notes", e);
                callback.onError(e);
            }
        });
    }

    /**
     * 搜索笔记
     * <p>
     * 根据关键字在标题和内容中搜索笔记
     * </p>
     *
     * @param keyword 搜索关键字
     * @param callback 回调接口
     */
    public void searchNotes(String keyword, Callback<List<NoteInfo>> callback) {
        executor.execute(() -> {
            try {
                if (keyword == null || keyword.trim().isEmpty()) {
                    callback.onSuccess(new ArrayList<>());
                    return;
                }

                String selection = "(" + NoteColumns.TYPE + " = ?) AND (" +
                    NoteColumns.SNIPPET + " LIKE ? OR " +
                    NoteColumns.ID + " IN (SELECT " + DataColumns.NOTE_ID +
                    " FROM data WHERE " + DataColumns.CONTENT + " LIKE ?))";

                String[] selectionArgs = new String[]{
                    String.valueOf(Notes.TYPE_NOTE),
                    "%" + keyword + "%",
                    "%" + keyword + "%"
                };

                Cursor cursor = contentResolver.query(
                    Notes.CONTENT_NOTE_URI,
                    null,
                    selection,
                    selectionArgs,
                    NoteColumns.MODIFIED_DATE + " DESC"
                );

                List<NoteInfo> notes = new ArrayList<>();
                if (cursor != null) {
                    try {
                        while (cursor.moveToNext()) {
                            notes.add(noteFromCursor(cursor));
                        }
                        Log.d(TAG, "Search returned " + cursor.getCount() + " results for: " + keyword);
                    } finally {
                        cursor.close();
                    }
                }

                callback.onSuccess(notes);
            } catch (Exception e) {
                Log.e(TAG, "Failed to search notes: " + keyword, e);
                callback.onError(e);
            }
        });
    }

    /**
     * 获取笔记统计信息
     * <p>
     * 统计指定文件夹下的笔记数量
     * </p>
     *
     * @param folderId 文件夹ID
     * @param callback 回调接口
     */
    public void countNotes(long folderId, Callback<Integer> callback) {
        executor.execute(() -> {
            try {
                String selection;
                String[] selectionArgs;

                if (folderId == Notes.ID_ROOT_FOLDER) {
                    selection = NoteColumns.TYPE + " != ?";
                    selectionArgs = new String[]{String.valueOf(Notes.TYPE_FOLDER)};
                } else {
                    selection = NoteColumns.PARENT_ID + " = ?";
                    selectionArgs = new String[]{String.valueOf(folderId)};
                }

                Cursor cursor = contentResolver.query(
                    Notes.CONTENT_NOTE_URI,
                    new String[]{"COUNT(*) AS count"},
                    selection,
                    selectionArgs,
                    null
                );

                int count = 0;
                if (cursor != null) {
                    try {
                        if (cursor.moveToFirst()) {
                            count = cursor.getInt(0);
                        }
                    } finally {
                        cursor.close();
                    }
                }

                callback.onSuccess(count);
                Log.d(TAG, "Counted " + count + " notes in folder: " + folderId);
            } catch (Exception e) {
                Log.e(TAG, "Failed to count notes in folder: " + folderId, e);
                callback.onError(e);
            }
        });
    }

    /**
     * 获取文件夹列表
     * <p>
     * 查询所有文件夹类型的笔记
     * </p>
     *
     * @param callback 回调接口
     */
    public void getFolders(Callback<List<NoteInfo>> callback) {
        executor.execute(() -> {
            try {
                String selection = NoteColumns.TYPE + " = ?";
                String[] selectionArgs = new String[]{
                    String.valueOf(Notes.TYPE_FOLDER)
                };

                Cursor cursor = contentResolver.query(
                    Notes.CONTENT_NOTE_URI,
                    null,
                    selection,
                    selectionArgs,
                    NoteColumns.MODIFIED_DATE + " DESC"
                );

                List<NoteInfo> folders = new ArrayList<>();
                if (cursor != null) {
                    try {
                        while (cursor.moveToNext()) {
                            folders.add(noteFromCursor(cursor));
                        }
                        Log.d(TAG, "Found " + cursor.getCount() + " folders");
                    } finally {
                        cursor.close();
                    }
                }

                callback.onSuccess(folders);
            } catch (Exception e) {
                Log.e(TAG, "Failed to load folders", e);
                callback.onError(e);
            }
        });
    }

    /**
     * 批量移动笔记到指定文件夹
     * <p>
     * 将笔记从当前文件夹移动到目标文件夹
     * </p>
     *
     * @param noteIds 要移动的笔记ID列表
     * @param targetFolderId 目标文件夹ID
     * @param callback 回调接口
     */
    public void moveNotes(List<Long> noteIds, long targetFolderId, Callback<Integer> callback) {
        executor.execute(() -> {
            try {
                if (noteIds == null || noteIds.isEmpty()) {
                    callback.onError(new IllegalArgumentException("Note IDs list is empty"));
                    return;
                }

                int totalRows = 0;
                for (Long noteId : noteIds) {
                    ContentValues values = new ContentValues();
                    values.put(NoteColumns.PARENT_ID, targetFolderId);
                    values.put(NoteColumns.LOCAL_MODIFIED, 1);

                    Uri uri = ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
                    int rows = contentResolver.update(uri, values, null, null);
                    totalRows += rows;
                }

                if (totalRows > 0) {
                    callback.onSuccess(totalRows);
                    Log.d(TAG, "Successfully moved " + totalRows + " notes to folder: " + targetFolderId);
                } else {
                    callback.onError(new RuntimeException("No notes were moved"));
                }
            } catch (Exception e) {
                Log.e(TAG, "Failed to move notes", e);
                callback.onError(e);
            }
        });
    }

    /**
     * 批量更新笔记置顶状态
     *
     * @param noteIds 笔记ID列表
     * @param isPinned 是否置顶
     * @param callback 回调接口
     */
    public void batchTogglePin(List<Long> noteIds, boolean isPinned, Callback<Integer> callback) {
        executor.execute(() -> {
            try {
                if (noteIds == null || noteIds.isEmpty()) {
                    callback.onError(new IllegalArgumentException("Note IDs list is empty"));
                    return;
                }

                int totalRows = 0;
                ContentValues values = new ContentValues();
                values.put(NoteColumns.TOP, isPinned ? 1 : 0);
                values.put(NoteColumns.LOCAL_MODIFIED, 1);

                for (Long noteId : noteIds) {
                    Uri uri = ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
                    int rows = contentResolver.update(uri, values, null, null);
                    totalRows += rows;
                }

                if (totalRows > 0) {
                    callback.onSuccess(totalRows);
                    Log.d(TAG, "Successfully updated pin state for " + totalRows + " notes");
                } else {
                    callback.onError(new RuntimeException("No notes were updated"));
                }
            } catch (Exception e) {
                Log.e(TAG, "Failed to update pin state", e);
                callback.onError(e);
            }
        });
    }

    /**
     * 从内容中提取摘要
     *
     * @param content 笔记内容
     * @return 摘要文本（最多100个字符）
     */
    private String extractSnippet(String content) {
        if (content == null || content.isEmpty()) {
            return "";
        }
        int maxLength = 100;
        return content.length() > maxLength
            ? content.substring(0, maxLength)
            : content;
    }

    /**
     * 关闭Executor
     * <p>
     * 在不再需要数据访问时调用，释放线程池资源
     * </p>
     */
    public void shutdown() {
        if (executor != null && !executor.isShutdown()) {
            executor.shutdown();
            Log.d(TAG, "Executor shutdown");
        }
    }
}
