/*
 * 版权所有 (c) 2010-2011，MiCode 开源社区 (www.micode.net)
 * 根据 Apache 许可证 2.0 版本（"许可证"）授权；
 * 除非符合许可证的规定，否则不得使用本文件。
 * 您可以从以下网址获取许可证副本：
 * http://www.apache.org/licenses/LICENSE-2.0
 * 除非适用法律要求或书面同意，本软件按"原样"分发，
 * 没有任何明示或暗示的保证或条件。
 * 详见许可证中规定的权限和限制。
 * （注：这是一份标准的Apache许可证2.0版本的开源声明）
 */

// 定义Google任务远程操作包路径
package net.micode.notes.gtask.remote;
// 导入Android基础组件
import android.app.Activity;                       // 活动组件，提供用户交互界面
import android.content.ContentResolver;           // 内容解析器，用于访问ContentProvider
import android.content.ContentUris;              // 用于操作ContentProvider的URI工具
import android.content.ContentValues;            // 键值对集合，用于数据库操作
import android.content.Context;                  // 应用上下文，提供全局资源访问
import android.database.Cursor;                  // 数据库查询结果集
import android.os.Build;                         // 系统版本信息
import android.util.Log;                         // 日志工具类
// 导入AndroidX兼容库注解
import androidx.annotation.RequiresApi;            // API版本要求注解
// 导入应用资源
import net.micode.notes.R;                       // 资源ID引用
// 导入笔记数据相关类
import net.micode.notes.data.Notes;              // 笔记数据库常量
import net.micode.notes.data.Notes.DataColumns;   // 数据表列名常量
import net.micode.notes.data.Notes.NoteColumns;  // 笔记表列名常量
// 导入Google任务数据模型
import net.micode.notes.gtask.data.MetaData;     // 元数据封装类
import net.micode.notes.gtask.data.Node;         // 节点基类
import net.micode.notes.gtask.data.SqlNote;      // SQLite笔记实体
import net.micode.notes.gtask.data.Task;         // 任务项实现
import net.micode.notes.gtask.data.TaskList;     // 任务列表实现
// 导入自定义异常
import net.micode.notes.gtask.exception.ActionFailureException;  // 操作失败异常
import net.micode.notes.gtask.exception.NetworkFailureException;  // 网络异常
// 导入工具类
import net.micode.notes.tool.DataUtils;           // 数据转换工具
import net.micode.notes.tool.GTaskStringUtils;    // Google任务字符串工具
// 导入JSON处理库
import org.json.JSONArray;                       // JSON数组处理
import org.json.JSONException;                   // JSON解析异常
import org.json.JSONObject;                      // JSON对象处理
// 导入Java集合框架
import java.util.HashMap;                        // 哈希映射表
import java.util.HashSet;                        // 哈希集合
import java.util.Iterator;                      // 集合迭代器
import java.util.Map;                            // 映射接口
import java.util.Objects;                        // 对象工具类
// Google任务同步管理器（实现本地笔记与Google Tasks双向同步）
public class GTaskManager {
    // 日志标签，使用类名作为TAG
    private static final String TAG = GTaskManager.class.getSimpleName();

    // 同步状态常量定义
    public static final int STATE_SUCCESS = 0;           // 同步成功状态码
    public static final int STATE_NETWORK_ERROR = 1;    // 网络错误状态码
    public static final int STATE_INTERNAL_ERROR = 2;   // 内部错误状态码
    public static final int STATE_SYNC_IN_PROGRESS = 3; // 同步进行中状态码
    public static final int STATE_SYNC_CANCELLED = 4;   // 同步已取消状态码

    // 单例实例变量
    private static GTaskManager mInstance = null;

    // 上下文相关变量
    private Activity mActivity;          // 用于获取认证令牌的Activity
    private Context mContext;            // 应用上下文
    private ContentResolver mContentResolver; // 内容解析器

    // 同步状态标志
    private boolean mSyncing;            // 是否正在同步的标志
    private boolean mCancelled;          // 是否已取消同步的标志

    // 数据存储结构
    private final HashMap<String, TaskList> mGTaskListHashMap; // Google任务列表缓存（key: 任务列表ID）
    private final HashMap<String, Node> mGTaskHashMap;         // 所有任务节点缓存（key: 任务ID）
    private final HashMap<String, MetaData> mMetaHashMap;      // 元数据缓存（key: 关联任务ID）
    private TaskList mMetaList;          // 元数据专用任务列表

    // ID映射关系
    private final HashSet<Long> mLocalDeleteIdMap;  // 待删除的本地笔记ID集合
    private final HashMap<String, Long> mGidToNid;  // Google任务ID到本地笔记ID的映射
    private final HashMap<Long, String> mNidToGid;  // 本地笔记ID到Google任务ID的映射

    // 私有构造函数（单例模式）
    private GTaskManager() {
        mSyncing = false;                // 初始化时未在同步状态
        mCancelled = false;              // 初始化时未取消
        mGTaskListHashMap = new HashMap<>();  // 初始化任务列表缓存
        mGTaskHashMap = new HashMap<>();      // 初始化任务节点缓存
        mMetaHashMap = new HashMap<>();       // 初始化元数据缓存
        mMetaList = null;                     // 初始化元数据列表为空
        mLocalDeleteIdMap = new HashSet<>();  // 初始化待删除ID集合
        mGidToNid = new HashMap<>();          // 初始化GoogleID到本地ID映射
        mNidToGid = new HashMap<>();          // 初始化本地ID到GoogleID映射
    }

    // 获取单例实例（线程安全）
    public static synchronized GTaskManager getInstance() {
        if (mInstance == null) {         // 如果实例不存在
            mInstance = new GTaskManager(); // 创建新实例
        }
        return mInstance;                // 返回单例实例
    }

    // 设置Activity上下文（用于认证）
    public synchronized void setActivityContext(Activity activity) {
        mActivity = activity;            // 保存Activity引用
    }

    // 执行同步操作（主入口方法）
    @RequiresApi(api = Build.VERSION_CODES.R)
    public int sync(Context context, GTaskASyncTask asyncTask) {
        // 检查是否已在同步中
        if (mSyncing) {
            Log.d(TAG, "Sync is in progress"); // 记录日志
            return STATE_SYNC_IN_PROGRESS;    // 返回同步中状态
        }

        // 初始化同步环境
        mContext = context;                   // 保存上下文
        mContentResolver = mContext.getContentResolver();  // 获取内容解析器
        mSyncing = true;                      // 设置同步标志
        mCancelled = false;                   // 重置取消标志

        // 清空所有缓存数据
        mGTaskListHashMap.clear();
        mGTaskHashMap.clear();
        mMetaHashMap.clear();
        mLocalDeleteIdMap.clear();
        mGidToNid.clear();
        mNidToGid.clear();

        try {
            // 获取Google任务客户端实例
            GTaskClient client = GTaskClient.getInstance();
            client.resetUpdateArray();          // 重置更新队列

            // 步骤1：登录Google Tasks（如果未取消）
            if (!mCancelled) {
                if (!client.login(mActivity))  { // 尝试登录
                    throw new NetworkFailureException("login google task failed"); // 登录失败抛出异常
                }
            }

            // 步骤2：初始化远程任务列表
            asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list));  // 更新进度
            initGTaskList();                  // 初始化任务列表

            // 步骤3：执行内容同步
            asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing));  // 更新进度
            syncContent();                    // 执行内容同步

        } catch (NetworkFailureException e) {
            Log.e(TAG, e.toString());          // 记录网络错误日志
            return STATE_NETWORK_ERROR;       // 返回网络错误状态
        } catch (ActionFailureException e) {
            Log.e(TAG, e.toString());          // 记录操作失败日志
            return STATE_INTERNAL_ERROR;      // 返回内部错误状态
        } catch (Exception e) {
            Log.e(TAG, e.toString());          // 记录未知异常日志
            e.printStackTrace();               // 打印堆栈轨迹
            return STATE_INTERNAL_ERROR;      // 返回内部错误状态
        } finally {
            // 最终清理工作
            mGTaskListHashMap.clear();         // 清空任务列表缓存
            mGTaskHashMap.clear();             // 清空任务节点缓存
            mMetaHashMap.clear();              // 清空元数据缓存
            mLocalDeleteIdMap.clear();         // 清空待删除ID集合
            mGidToNid.clear();                 // 清空ID映射
            mNidToGid.clear();                 // 清空反向ID映射
            mSyncing = false;                 // 重置同步标志
        }

        // 返回最终状态（根据是否取消）
        return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS;
    }

    // 初始化远程任务列表
    private void initGTaskList() throws NetworkFailureException {
        if (mCancelled) return;              // 如果已取消则直接返回

        GTaskClient client = GTaskClient.getInstance();  // 获取客户端实例
        try {
            // 获取所有任务列表
            JSONArray jsTaskLists = client.getTaskLists();

            // 首先初始化元数据列表
            mMetaList = null;                // 重置元数据列表
            for (int i = 0; i < jsTaskLists.length();  i++) {
                JSONObject object = jsTaskLists.getJSONObject(i);  // 获取单个列表
                String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);  // 获取列表ID
                String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);  // 获取列表名称

                // 检查是否是元数据专用列表
                if (name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX  + GTaskStringUtils.FOLDER_META)) {
                    mMetaList = new TaskList();           // 创建元数据列表
                    mMetaList.setContentByRemoteJSON(object);  // 设置内容

                    // 加载该列表下的所有元数据项
                    JSONArray jsMetas = client.getTaskList(gid);
                    for (int j = 0; j < jsMetas.length();  j++) {
                        object = jsMetas.getJSONObject(j);  // 获取元数据项
                        MetaData metaData = new MetaData(); // 创建元数据对象
                        metaData.setContentByRemoteJSON(object);  // 设置内容
                        if (metaData.isWorthSaving())  {    // 检查是否需要保存
                            mMetaList.addChildTask(metaData);  // 添加到元数据列表
                            if (metaData.getGid()  != null) {  // 如果有有效ID
                                mMetaHashMap.put(metaData.getRelatedGid(),  metaData); // 加入缓存
                            }
                        }
                    }
                }
            }

            // 如果不存在元数据列表则创建
            if (mMetaList == null) {
                mMetaList = new TaskList();  // 创建新列表
                mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX  + GTaskStringUtils.FOLDER_META); // 设置名称
                GTaskClient.getInstance().createTaskList(mMetaList);  // 在服务器创建
            }

            // 初始化普通任务列表
            for (int i = 0; i < jsTaskLists.length();  i++) {
                JSONObject object = jsTaskLists.getJSONObject(i);  // 获取单个列表
                String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);  // 获取列表ID
                String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);  // 获取列表名称

                // 检查是否是MIUI前缀的普通列表（排除元数据列表）
                if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)
                        && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX  + GTaskStringUtils.FOLDER_META)) {
                    TaskList tasklist = new TaskList();    // 创建任务列表
                    tasklist.setContentByRemoteJSON(object);  // 设置内容
                    mGTaskListHashMap.put(gid,  tasklist);  // 加入列表缓存
                    mGTaskHashMap.put(gid,  tasklist);      // 加入节点缓存

                    // 加载该列表下的所有任务
                    JSONArray jsTasks = client.getTaskList(gid);
                    for (int j = 0; j < jsTasks.length();  j++) {
                        object = jsTasks.getJSONObject(j);  // 获取单个任务
                        gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);  // 获取任务ID
                        Task task = new Task();            // 创建任务对象
                        task.setContentByRemoteJSON(object);  // 设置内容
                        if (task.isWorthSaving())  {        // 检查是否需要保存
                            task.setMetaInfo(mMetaHashMap.get(gid));  // 关联元数据
                            tasklist.addChildTask(task);    // 添加到列表
                            mGTaskHashMap.put(gid,  task);  // 加入节点缓存
                        }
                    }
                }
            }
        } catch (JSONException e) {
            Log.e(TAG, e.toString());        // 记录JSON异常
            e.printStackTrace();             // 打印堆栈轨迹
            throw new ActionFailureException("initGTaskList: handing JSONObject failed"); // 抛出操作异常
        }
    }

    // 执行内容同步（核心方法）
    @RequiresApi(api = Build.VERSION_CODES.R)
    private void syncContent() throws NetworkFailureException {
        int syncType;                      // 同步类型标识
        Cursor c = null;                   // 数据库游标
        String gid;                        // Google任务ID
        Node node;                         // 任务节点

        mLocalDeleteIdMap.clear();          // 清空待删除集合

        if (mCancelled) return;            // 检查是否已取消

        /* ===== 阶段1：处理本地已删除的笔记 ===== */
        try {
            // 查询回收站中的笔记（非系统类型且父目录为回收站）
            c = mContentResolver.query(Notes.CONTENT_NOTE_URI,  SqlNote.PROJECTION_NOTE,
                    "(type<>? AND parent_id=?)", new String[] {
                            String.valueOf(Notes.TYPE_SYSTEM),  String.valueOf(Notes.ID_TRASH_FOLER)
                    }, null);
            if (c != null) {
                while (c.moveToNext())  {    // 遍历结果集
                    gid = c.getString(SqlNote.GTASK_ID_COLUMN);  // 获取Google ID
                    node = mGTaskHashMap.get(gid);  // 查找对应节点
                    if (node != null) {     // 如果存在对应节点
                        mGTaskHashMap.remove(gid);  // 从缓存移除
                        doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); // 执行远程删除
                    }
                    mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));  // 记录待删除ID
                }
            } else {
                Log.w(TAG, "failed to query trash folder"); // 记录查询失败
            }
        } finally {
            if (c != null) {
                c.close();                   // 关闭游标
                c = null;
            }
        }

        /* ===== 阶段2：同步文件夹 ===== */
        syncFolder();                      // 同步文件夹结构

        /* ===== 阶段3：处理数据库中的现有笔记 ===== */
        try {
            // 查询所有普通笔记（非回收站）
            c = mContentResolver.query(Notes.CONTENT_NOTE_URI,  SqlNote.PROJECTION_NOTE,
                    "(type=? AND parent_id<>?)", new String[] {
                            String.valueOf(Notes.TYPE_NOTE),  String.valueOf(Notes.ID_TRASH_FOLER)
                    }, NoteColumns.TYPE + " DESC");
            if (c != null) {
                while (c.moveToNext())  {    // 遍历结果集
                    gid = c.getString(SqlNote.GTASK_ID_COLUMN);  // 获取Google ID
                    node = mGTaskHashMap.get(gid);  // 查找对应节点

                    // 确定同步类型
                    if (node != null) {     // 如果存在对应节点
                        mGTaskHashMap.remove(gid);  // 从缓存移除
                        mGidToNid.put(gid,  c.getLong(SqlNote.ID_COLUMN));  // 建立ID映射
                        mNidToGid.put(c.getLong(SqlNote.ID_COLUMN),  gid); // 建立反向映射
                        syncType = node.getSyncAction(c);  // 获取同步类型
                    } else {
                        // 根据是否存在Google ID判断是本地新增还是远程删除
                        syncType = c.getString(SqlNote.GTASK_ID_COLUMN).trim().isEmpty()  ?
                                Node.SYNC_ACTION_ADD_REMOTE : Node.SYNC_ACTION_DEL_LOCAL;
                    }
                    doContentSync(syncType, node, c); // 执行同步操作
                }
            } else {
                Log.w(TAG, "failed to query existing note in database"); // 记录查询失败
            }
        } finally {
            if (c != null) {
                c.close();                   // 关闭游标
                c = null;
            }
        }

        /* ===== 阶段4：处理剩余的远程节点（本地不存在） ===== */
        for (Map.Entry<String, Node> entry : mGTaskHashMap.entrySet())  {
            node = entry.getValue();         // 获取节点
            doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); // 执行本地添加
        }

        /* ===== 阶段5：批量删除本地标记项 ===== */
        if (!mCancelled) {
            if (!DataUtils.batchDeleteNotes(mContentResolver,  mLocalDeleteIdMap)) {
                throw new ActionFailureException("failed to batch-delete local deleted notes"); // 删除失败抛出异常
            }
        }

        /* ===== 阶段6：刷新本地同步ID ===== */
        if (!mCancelled) {
            GTaskClient.getInstance().commitUpdate();  // 提交未完成的更新
            refreshLocalSyncId();           // 刷新本地同步ID
        }
    }
    // 同步文件夹方法（需要Android R及以上版本）
    @RequiresApi(api = Build.VERSION_CODES.R)
    private void syncFolder() throws NetworkFailureException {
        Cursor c = null;                   // 数据库查询游标
        String gid;                       // Google任务ID
        Node node;                        // 任务节点对象
        int syncType;                     // 同步类型标识

        if (mCancelled) return;           // 检查同步是否被取消

        /* ===== 1. 处理根文件夹 ===== */
        try {
            // 查询根文件夹（ID_ROOT_FOLDER）
            c = mContentResolver.query(
                    ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,  Notes.ID_ROOT_FOLDER),
                    SqlNote.PROJECTION_NOTE, null, null, null);

            if (c != null && c.moveToNext())  {
                gid = c.getString(SqlNote.GTASK_ID_COLUMN);  // 获取Google ID
                node = mGTaskHashMap.get(gid);               // 查找对应节点

                if (node != null) {         // 如果远程存在对应节点
                    mGTaskHashMap.remove(gid);               // 从缓存移除
                    // 建立ID映射关系
                    mGidToNid.put(gid,  (long) Notes.ID_ROOT_FOLDER);
                    mNidToGid.put((long)  Notes.ID_ROOT_FOLDER, gid);

                    // 系统文件夹：仅在名称不匹配时更新远程
                    if (!node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX  +
                            GTaskStringUtils.FOLDER_DEFAULT)) {
                        doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c);
                    }
                } else {                    // 如果远程不存在
                    doContentSync(Node.SYNC_ACTION_ADD_REMOTE, null, c); // 在远程创建
                }
            } else {
                Log.w(TAG, "failed to query root folder"); // 记录查询失败
            }
        } finally {
            if (c != null) c.close();        // 确保游标关闭
        }

        /* ===== 2. 处理通话记录文件夹 ===== */
        try {
            // 查询通话记录文件夹（ID_CALL_RECORD_FOLDER）
            c = mContentResolver.query(Notes.CONTENT_NOTE_URI,  SqlNote.PROJECTION_NOTE,
                    "(_id=?)", new String[]{String.valueOf(Notes.ID_CALL_RECORD_FOLDER)},  null);

            if (c != null && c.moveToNext())  {
                gid = c.getString(SqlNote.GTASK_ID_COLUMN);  // 获取Google ID
                node = mGTaskHashMap.get(gid);               // 查找对应节点

                if (node != null) {         // 如果远程存在对应节点
                    mGTaskHashMap.remove(gid);               // 从缓存移除
                    // 建立ID映射关系
                    mGidToNid.put(gid,  (long) Notes.ID_CALL_RECORD_FOLDER);
                    mNidToGid.put((long)  Notes.ID_CALL_RECORD_FOLDER, gid);

                    // 系统文件夹：仅在名称不匹配时更新远程
                    if (!node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX  +
                            GTaskStringUtils.FOLDER_CALL_NOTE)) {
                        doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c);
                    }
                } else {                    // 如果远程不存在
                    doContentSync(Node.SYNC_ACTION_ADD_REMOTE, null, c); // 在远程创建
                }
            } else {
                Log.w(TAG, "failed to query call note folder"); // 记录查询失败
            }
        } finally {
            if (c != null) c.close();        // 确保游标关闭
        }

        /* ===== 3. 处理普通本地文件夹 ===== */
        try {
            // 查询所有普通文件夹（非回收站）
            c = mContentResolver.query(Notes.CONTENT_NOTE_URI,  SqlNote.PROJECTION_NOTE,
                    "(type=? AND parent_id<>?)",
                    new String[]{
                            String.valueOf(Notes.TYPE_FOLDER),
                            String.valueOf(Notes.ID_TRASH_FOLER)
                    },
                    NoteColumns.TYPE + " DESC");

            if (c != null) {
                while (c.moveToNext())  {     // 遍历查询结果
                    gid = c.getString(SqlNote.GTASK_ID_COLUMN);  // 获取Google ID
                    node = mGTaskHashMap.get(gid);               // 查找对应节点

                    // 确定同步类型
                    if (node != null) {      // 存在对应节点
                        mGTaskHashMap.remove(gid);               // 从缓存移除
                        // 建立ID映射关系
                        mGidToNid.put(gid,  c.getLong(SqlNote.ID_COLUMN));
                        mNidToGid.put(c.getLong(SqlNote.ID_COLUMN),  gid);
                        syncType = node.getSyncAction(c);        // 获取同步类型
                    } else {
                        // 根据Google ID是否存在判断是本地新增还是远程删除
                        syncType = c.getString(SqlNote.GTASK_ID_COLUMN).trim().isEmpty()  ?
                                Node.SYNC_ACTION_ADD_REMOTE : Node.SYNC_ACTION_DEL_LOCAL;
                    }
                    doContentSync(syncType, node, c);           // 执行同步操作
                }
            } else {
                Log.w(TAG, "failed to query existing folder"); // 记录查询失败
            }
        } finally {
            if (c != null) c.close();        // 确保游标关闭
        }

        /* ===== 4. 处理远程新增的文件夹 ===== */
        for (Map.Entry<String, TaskList> entry : mGTaskListHashMap.entrySet())  {
            gid = entry.getKey();            // 获取Google ID
            node = entry.getValue();         // 获取节点对象

            // 如果缓存中仍存在该节点（说明本地不存在）
            if (mGTaskHashMap.containsKey(gid))  {
                mGTaskHashMap.remove(gid);   // 从缓存移除
                doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); // 在本地创建
            }
        }

        // 如果未被取消，提交所有未完成的更新
        if (!mCancelled) GTaskClient.getInstance().commitUpdate();
    }

    // 执行具体同步操作（根据同步类型分发）
    @RequiresApi(api = Build.VERSION_CODES.R)
    private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException {
        if (mCancelled) return;            // 检查是否已取消

        MetaData meta;                     // 元数据对象
        switch (syncType) {
            case Node.SYNC_ACTION_ADD_LOCAL:   // 本地添加操作
                addLocalNode(node);        // 在本地添加节点
                break;
            case Node.SYNC_ACTION_ADD_REMOTE:  // 远程添加操作
                addRemoteNode(node, c);    // 在远程添加节点
                break;
            case Node.SYNC_ACTION_DEL_LOCAL:   // 本地删除操作
                // 查找关联的元数据
                meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN));
                if (meta != null) {
                    GTaskClient.getInstance().deleteNode(meta);  // 删除元数据
                }
                mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));  // 记录待删除ID
                break;
            case Node.SYNC_ACTION_DEL_REMOTE:  // 远程删除操作
                // 查找关联的元数据
                meta = mMetaHashMap.get(node.getGid());
                if (meta != null) {
                    GTaskClient.getInstance().deleteNode(meta);  // 删除元数据
                }
                GTaskClient.getInstance().deleteNode(node);      // 删除节点
                break;
            case Node.SYNC_ACTION_UPDATE_LOCAL: // 本地更新操作
                updateLocalNode(node, c);  // 更新本地节点
                break;
            case Node.SYNC_ACTION_UPDATE_REMOTE: // 远程更新操作
                updateRemoteNode(node, c); // 更新远程节点
                break;
            case Node.SYNC_ACTION_UPDATE_CONFLICT: // 冲突处理（目前简单采用远程更新）
                updateRemoteNode(node, c);
                break;
            case Node.SYNC_ACTION_NONE:    // 无操作
                break;
            case Node.SYNC_ACTION_ERROR:    // 错误状态
            default:
                throw new ActionFailureException("unkown sync action type"); // 抛出异常
        }
    }

    // 在本地添加节点
    @RequiresApi(api = Build.VERSION_CODES.R)
    private void addLocalNode(Node node) throws NetworkFailureException {
        if (mCancelled) return;            // 检查是否已取消

        SqlNote sqlNote;                   // 本地数据库笔记对象
        if (node instanceof TaskList) {    // 如果是任务列表（文件夹）
            // 处理系统预置文件夹
            if (node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX  +
                    GTaskStringUtils.FOLDER_DEFAULT)) {
                sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); // 根文件夹
            }
            else if (node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX  +
                    GTaskStringUtils.FOLDER_CALL_NOTE)) {
                sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); // 通话记录文件夹
            }
            else {                         // 普通文件夹
                sqlNote = new SqlNote(mContext);
                sqlNote.setContent(node.getLocalJSONFromContent());  // 设置内容
                sqlNote.setParentId(Notes.ID_ROOT_FOLDER);          // 设置父ID为根文件夹
            }
        }
        else {                             // 普通任务节点
            sqlNote = new SqlNote(mContext);
            JSONObject js = node.getLocalJSONFromContent();       // 获取节点内容

            try {
                // 处理笔记ID冲突
                if (js.has(GTaskStringUtils.META_HEAD_NOTE))  {
                    JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
                    if (note.has(NoteColumns.ID))  {
                        long id = note.getLong(NoteColumns.ID);
                        if (DataUtils.existInNoteDatabase(mContentResolver,  id)) {
                            note.remove(NoteColumns.ID);  // ID已存在则移除
                        }
                    }
                }

                // 处理数据项ID冲突
                if (js.has(GTaskStringUtils.META_HEAD_DATA))  {
                    JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
                    for (int i = 0; i < dataArray.length();  i++) {
                        JSONObject data = dataArray.getJSONObject(i);
                        if (data.has(DataColumns.ID))  {
                            long dataId = data.getLong(DataColumns.ID);
                            if (DataUtils.existInDataDatabase(mContentResolver,  dataId)) {
                                data.remove(DataColumns.ID);  // ID已存在则移除
                            }
                        }
                    }
                }
            } catch (JSONException e) {
                Log.w(TAG, e.toString());    // 记录JSON异常
                e.printStackTrace();
            }

            sqlNote.setContent(js);          // 设置笔记内容

            // 设置父文件夹ID
            Long parentId = mGidToNid.get(((Task)  node).getParent().getGid());
            if (parentId == null) {
                Log.e(TAG, "cannot find task's parent id locally");
                throw new ActionFailureException("cannot add local node");
            }
            sqlNote.setParentId(parentId);   // 设置父节点ID
        }

        // 创建本地节点
        sqlNote.setGtaskId(node.getGid());   // 设置Google任务ID
        sqlNote.commit(false);               // 提交到数据库

        // 更新ID映射关系
        mGidToNid.put(node.getGid(),  sqlNote.getId());
        mNidToGid.put(sqlNote.getId(),  node.getGid());

        // 更新远程元数据
        updateRemoteMeta(node.getGid(),  sqlNote);
    }
    // 更新本地节点（需要Android R及以上版本）
    @RequiresApi(api = Build.VERSION_CODES.R)
    private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException {
        if (mCancelled) {
            return;  // 如果同步已取消则直接返回
        }

        SqlNote sqlNote;
        // 步骤1：更新本地笔记
        sqlNote = new SqlNote(mContext, c); // 根据游标创建SqlNote对象
        sqlNote.setContent(node.getLocalJSONFromContent());  // 设置节点内容

        // 步骤2：设置父节点ID（任务节点取父任务列表ID，否则使用根文件夹ID）
        Long parentId = (node instanceof Task) ?
                mGidToNid.get(((Task)  node).getParent().getGid()) : // 获取任务的父列表本地ID
                Long.valueOf(Notes.ID_ROOT_FOLDER);  // 默认使用根文件夹ID
        if (parentId == null) {
            Log.e(TAG, "无法在本地找到任务的父ID");
            throw new ActionFailureException("无法更新本地节点");
        }
        sqlNote.setParentId(parentId.longValue());  // 设置父节点ID
        sqlNote.commit(true);  // 提交修改到数据库

        // 步骤3：更新元数据信息
        updateRemoteMeta(node.getGid(),  sqlNote);
    }

    // 添加远程节点（需要Android R及以上版本）
    @RequiresApi(api = Build.VERSION_CODES.R)
    private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException {
        if (mCancelled) {
            return;  // 如果同步已取消则直接返回
        }

        SqlNote sqlNote = new SqlNote(mContext, c); // 根据游标创建SqlNote对象
        Node n;  // 要创建的节点

        // 步骤1：判断节点类型并处理
        if (sqlNote.isNoteType())  {
            // 情况1：普通笔记节点
            Task task = new Task(); // 创建新任务
            task.setContentByLocalJSON(sqlNote.getContent());  // 设置任务内容

            // 获取父任务列表的Google ID
            String parentGid = mNidToGid.get(sqlNote.getParentId());
            if (parentGid == null) {
                Log.e(TAG, "无法找到任务的父任务列表");
                throw new ActionFailureException("无法添加远程任务");
            }
            // 将任务添加到父列表
            Objects.requireNonNull(mGTaskListHashMap.get(parentGid)).addChildTask(task);

            // 在远程创建任务
            GTaskClient.getInstance().createTask(task);
            n = (Node) task; // 设置返回节点

            // 添加元数据
            updateRemoteMeta(task.getGid(),  sqlNote);
        } else {
            // 情况2：文件夹节点
            TaskList tasklist = null;

            // 构建文件夹名称（带MIUI前缀）
            String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX;
            if (sqlNote.getId()  == Notes.ID_ROOT_FOLDER) {
                folderName += GTaskStringUtils.FOLDER_DEFAULT; // 根文件夹
            } else if (sqlNote.getId()  == Notes.ID_CALL_RECORD_FOLDER) {
                folderName += GTaskStringUtils.FOLDER_CALL_NOTE; // 通话记录文件夹
            } else {
                folderName += sqlNote.getSnippet();  // 普通文件夹使用笔记摘要
            }

            // 检查是否已存在同名文件夹
            for (Map.Entry<String, TaskList> entry : mGTaskListHashMap.entrySet())  {
                String gid = entry.getKey();
                TaskList list = entry.getValue();

                if (list.getName().equals(folderName))  {
                    tasklist = list; // 找到已有文件夹
                    if (mGTaskHashMap.containsKey(gid))  {
                        mGTaskHashMap.remove(gid);  // 从缓存移除
                    }
                    break;
                }
            }

            // 如果不存在则创建新文件夹
            if (tasklist == null) {
                tasklist = new TaskList(); // 创建新任务列表
                tasklist.setContentByLocalJSON(sqlNote.getContent());  // 设置内容
                GTaskClient.getInstance().createTaskList(tasklist);  // 远程创建
                mGTaskListHashMap.put(tasklist.getGid(),  tasklist); // 加入缓存
            }
            n = (Node) tasklist; // 设置返回节点
        }

        // 步骤2：更新本地笔记
        sqlNote.setGtaskId(n.getGid());  // 设置Google任务ID
        sqlNote.commit(false);  // 提交修改到数据库
        sqlNote.resetLocalModified();  // 重置本地修改标记
        sqlNote.commit(true);  // 再次提交确保状态更新

        // 步骤3：更新ID映射关系
        mGidToNid.put(n.getGid(),  sqlNote.getId());  // Google ID → 本地ID
        mNidToGid.put(sqlNote.getId(),  n.getGid());  // 本地ID → Google ID
    }

    // 更新远程节点（需要Android R及以上版本）
    @RequiresApi(api = Build.VERSION_CODES.R)
    private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException {
        if (mCancelled) {
            return;  // 如果同步已取消则直接返回
        }

        SqlNote sqlNote = new SqlNote(mContext, c); // 根据游标创建SqlNote对象

        // 步骤1：更新远程节点内容
        node.setContentByLocalJSON(sqlNote.getContent());  // 设置节点内容
        GTaskClient.getInstance().addUpdateNode(node);  // 添加更新到队列

        // 步骤2：更新元数据
        updateRemoteMeta(node.getGid(),  sqlNote);

        // 步骤3：处理任务移动（如果是笔记类型）
        if (sqlNote.isNoteType())  {
            Task task = (Task) node; // 转换为任务对象
            TaskList preParentList = task.getParent();  // 获取原父列表

            // 获取当前父列表的Google ID
            String curParentGid = mNidToGid.get(sqlNote.getParentId());
            if (curParentGid == null) {
                Log.e(TAG, "无法找到任务的父任务列表");
                throw new ActionFailureException("无法更新远程任务");
            }
            TaskList curParentList = mGTaskListHashMap.get(curParentGid);  // 获取当前父列表

            // 如果父列表发生变化
            if (preParentList != curParentList) {
                preParentList.removeChildTask(task);  // 从原列表移除
                if (curParentList != null) {
                    curParentList.addChildTask(task);  // 添加到新列表
                }
                // 在远程执行移动操作
                GTaskClient.getInstance().moveTask(task,  preParentList, curParentList);
            }
        }

        // 步骤4：清除本地修改标记
        sqlNote.resetLocalModified();  // 重置修改标记
        sqlNote.commit(true);  // 提交到数据库
    }

    // 更新远程元数据信息
    private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException {
        // 只处理有效的笔记类型数据（非文件夹）
        if (sqlNote != null && sqlNote.isNoteType())  {
            // 尝试从缓存获取现有元数据
            MetaData metaData = mMetaHashMap.get(gid);

            if (metaData != null) {
                // 情况1：元数据已存在 - 更新操作
                metaData.setMeta(gid,  sqlNote.getContent());  // 设置新的元数据内容
                GTaskClient.getInstance().addUpdateNode(metaData);  // 添加到更新队列
            } else {
                // 情况2：元数据不存在 - 创建操作
                metaData = new MetaData(); // 创建新元数据对象
                metaData.setMeta(gid,  sqlNote.getContent());  // 设置元数据内容
                mMetaList.addChildTask(metaData);  // 添加到元数据列表
                mMetaHashMap.put(gid,  metaData); // 加入缓存
                GTaskClient.getInstance().createTask(metaData);  // 在远程创建
            }
        }
    }

    // 刷新本地同步ID（确保与远程一致）
    private void refreshLocalSyncId() throws NetworkFailureException {
        if (mCancelled) {
            return; // 如果同步已取消则直接返回
        }

        /* 步骤1：重新加载远程任务列表 */
        mGTaskHashMap.clear();   // 清空任务缓存
        mGTaskListHashMap.clear();  // 清空任务列表缓存
        mMetaHashMap.clear();   // 清空元数据缓存
        initGTaskList();       // 重新初始化任务列表

        /* 步骤2：更新本地同步ID */
        Cursor c = null;
        try {
            // 查询所有需要同步的笔记（非系统类型且不在回收站）
            c = mContentResolver.query(Notes.CONTENT_NOTE_URI,  SqlNote.PROJECTION_NOTE,
                    "(type<>? AND parent_id<>?)", new String[] {
                            String.valueOf(Notes.TYPE_SYSTEM),
                            String.valueOf(Notes.ID_TRASH_FOLER)
                    },
                    NoteColumns.TYPE + " DESC");

            if (c != null) {
                while (c.moveToNext())  { // 遍历查询结果
                    String gid = c.getString(SqlNote.GTASK_ID_COLUMN);  // 获取Google ID
                    Node node = mGTaskHashMap.get(gid);  // 查找对应远程节点

                    if (node != null) {
                        mGTaskHashMap.remove(gid);  // 从缓存移除

                        // 准备更新数据
                        ContentValues values = new ContentValues();
                        // 使用远程节点的最后修改时间作为同步ID
                        values.put(NoteColumns.SYNC_ID,  node.getLastModified());

                        // 更新数据库
                        mContentResolver.update(
                                ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
                                        c.getLong(SqlNote.ID_COLUMN)),  // 定位要更新的笔记
                                values, // 更新内容
                                null,   // WHERE条件
                                null    // WHERE参数
                        );
                    } else {
                        // 错误处理：本地存在但远程不存在的笔记
                        Log.e(TAG, "本地存在但远程找不到对应的任务");
                        throw new ActionFailureException("同步后仍有本地条目缺少Google ID");
                    }
                }
            } else {
                Log.w(TAG, "查询本地笔记刷新同步ID失败");
            }
        } finally {
            if (c != null) {
                c.close();  // 确保关闭游标
                c = null;
            }
        }
    }
    // 获取当前同步账号名称
    public String getSyncAccount() {
        // 从GTaskClient获取账号信息并返回名称
        return GTaskClient.getInstance().getSyncAccount().name;
    }
    // 取消同步操作
    public void cancelSync() {
        mCancelled = true; // 设置取消标志
    }
}
