using System;
using System.Collections.Concurrent;
using System.IO;
using SimpleCore.Library;

namespace StandardScene.CommonTools
{
    /// <summary>
    /// 按文件路径串行化读写，保证高并发下对同一文件的更新原子、不覆盖其他记录。
    /// 调用方在委托中完成“读当前内容 → 修改 → 返回新内容”，由本类负责加锁与写回。
    /// </summary>
    public static class AtomicFileUpdateHelper
    {
        private static readonly ConcurrentDictionary<string, object> PathLocks = new ConcurrentDictionary<string, object>(StringComparer.OrdinalIgnoreCase);

        /// <summary>
        /// 对指定路径执行原子更新：在持锁下读取当前文件内容，调用 update 得到新内容并写回。
        /// 不修改其他记录时，应在 update 中仅变更需要变更的条目后返回完整内容。
        /// </summary>
        /// <param name="filePath">文件完整路径</param>
        /// <param name="update">接收当前文件文本（若文件不存在则为 null），返回要写回的新文本；返回 null 表示不写入</param>
        public static void ExecuteAtomicUpdate(string filePath, Func<string, string> update)
        {
            if (string.IsNullOrEmpty(filePath))
                throw new ArgumentNullException(nameof(filePath));
            if (update == null)
                throw new ArgumentNullException(nameof(update));

            var lockObj = PathLocks.GetOrAdd(filePath, _ => new object());
            lock (lockObj)
            {
                string current = null;
                try
                {
                    if (File.Exists(filePath))
                        current = File.ReadAllText(filePath);
                }
                catch (Exception ex)
                {
                    Diagnosis.Log($"AtomicFileUpdate read error: {filePath}, {ex.Message}", "AtomicFileUpdate", true);
                    throw;
                }

                string newContent = update(current);
                if (newContent == null)
                    return;

                var dir = Path.GetDirectoryName(filePath);
                if (!string.IsNullOrEmpty(dir))
                    Directory.CreateDirectory(dir);

                try
                {
                    var tempPath = filePath + ".tmp";
                    File.WriteAllText(tempPath, newContent);
                    if (File.Exists(filePath))
                        File.Replace(tempPath, filePath, null);
                    else
                        File.Move(tempPath, filePath);
                }
                catch (Exception ex)
                {
                    Diagnosis.Log($"AtomicFileUpdate write error: {filePath}, {ex.Message}", "AtomicFileUpdate", true);
                    throw;
                }
            }
        }
    }
}
