using System.IO;
using System.IO.Compression;
using System.Text;
using System.Text.RegularExpressions;
using fNbt;
using PCL.Core.App;
using PCL.Core.App.Localization;
using PCL.Core.Utils;
using PCL.Core.Utils.Exts;
using PCL.Core.Utils.Hash;
using static PCL.ModComp;
using static PCL.ModLoader;

namespace PCL;

public static class ModLocalComp
{
    private const int localModCacheVersion = 7;

    private static readonly Lazy<HashCache> _hashCache = new(() =>
        new HashCache(ModBase.pathTemp + @"Cache\HashCache.db"));

    public class LocalCompFile
    {
        /// <summary>
        ///     是否可能为前置 Mod。
        /// </summary>
        public bool IsPresetMod()
        {
            return !Dependencies.Any() && Name is not null &&
                   (Name.ToLower().Contains("core") || Name.ToLower().Contains("lib"));
        }

        /// <summary>
        ///     根据完整文件路径的文件扩展名判断是否为 Mod 文件。
        /// </summary>
        public static bool IsModFile(string path)
        {
            if (path is null || !path.Contains("."))
                return false;
            path = path.ToLower();
            if (path.EndsWithF(".jar", true) || path.EndsWithF(".zip", true) || path.EndsWithF(".litemod", true) ||
                path.EndsWithF(".jar.disabled", true) || path.EndsWithF(".zip.disabled", true) ||
                path.EndsWithF(".litemod.disabled", true) || path.EndsWithF(".jar.old", true) ||
                path.EndsWithF(".zip.old", true) || path.EndsWithF(".litemod.old", true))
                return true;
            return false;
        }

        /// <summary>
        ///     检查是否为指定类型的组件文件。
        /// </summary>
        public static bool IsCompFile(string path, CompType compType)
        {
            if (path is null || !path.Contains("."))
                return false;
            path = path.ToLower();
            switch (compType)
            {
                case CompType.Mod:
                {
                    return IsModFile(path);
                }
                case CompType.ResourcePack:
                case CompType.Shader:
                {
                    return path.EndsWithF(".zip", true);
                }
                case CompType.DataPack:
                {
                    return path.EndsWithF(".zip", true) || path.EndsWithF(".zip.disabled", true);
                }
                case CompType.Schematic:
                {
                    return path.EndsWithF(".litematic", true) || path.EndsWithF(".nbt", true) ||
                           path.EndsWithF(".schematic", true) || path.EndsWithF(".schem", true);
                }

                default:
                {
                    return false;
                }
            }
        }

        /// <summary>
        ///     获取图标路径。
        /// </summary>
        public string GetLogo()
        {
            if (Comp is not null && Comp.LogoUrl is not null)
                return Comp.LogoUrl;
            if (Logo is not null)
                return Logo;

            // 为文件夹设置特定图标
            if (IsFolder) return "pack://application:,,,/images/Icons/Folder.png";

            return ModBase.pathImage + "Icons/NoIcon.png";
        }

        #region Litematic 文件处理

        /// <summary>
        ///     读取 Litematic 文件的 NBT 数据。
        /// </summary>
        private void LoadLitematicNbtData()
        {
            try
            {
                ModBase.Log($"开始读取 Litematic NBT 数据：{path}", ModBase.LogLevel.Debug);
                using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    var scheNbt = new NbtFile();
                    scheNbt.LoadFromStream(fs, NbtCompression.AutoDetect);
                    // 读取版本信息
                    var versionTag = (NbtInt)scheNbt.RootTag.Get("Version");
                    if (versionTag is not null) _litematicVersion = versionTag.Value;

                    // 读取 Metadata 节点
                    var metadataTag = scheNbt.RootTag.Get<NbtCompound>("Metadata");
                    if (metadataTag is not null)
                    {
                        ModBase.Log("找到 Litematic Metadata 节点", ModBase.LogLevel.Debug);

                        // 读取名称
                        var nameTag = metadataTag.Get<NbtString>("Name");
                        if (nameTag is not null && !string.IsNullOrWhiteSpace(nameTag.Value) &&
                            nameTag.Value != "Unnamed") _litematicOriginalName = nameTag.Value;

                        // 读取描述信息
                        var descriptionTag = metadataTag.Get<NbtString>("Description");
                        if (descriptionTag is not null && !string.IsNullOrWhiteSpace(descriptionTag.Value))
                            _Description = descriptionTag.Value;

                        // 读取作者信息
                        var authorTag = metadataTag.Get<NbtString>("Author");
                        if (authorTag is not null && !string.IsNullOrWhiteSpace(authorTag.Value))
                            _Authors = authorTag.Value;

                        // 读取时间信息
                        var timeCreatedTag = metadataTag.Get<NbtLong>("TimeCreated");
                        if (timeCreatedTag is not null) _litematicTimeCreated = timeCreatedTag.Value;

                        var timeModifiedTag = metadataTag.Get<NbtLong>("TimeModified");
                        if (timeModifiedTag is not null) _litematicTimeModified = timeModifiedTag.Value;

                        // 读取包围盒大小
                        var enclosingSizeTag = metadataTag.Get<NbtCompound>("EnclosingSize");
                        if (enclosingSizeTag is not null)
                        {
                            var xTag = enclosingSizeTag.Get<NbtInt>("x");
                            var yTag = enclosingSizeTag.Get<NbtInt>("y");
                            var zTag = enclosingSizeTag.Get<NbtInt>("z");
                            if (xTag is not null && yTag is not null && zTag is not null)
                                _litematicEnclosingSize = $"{xTag.Value} × {yTag.Value} × {zTag.Value}";
                        }

                        // 读取区域数量
                        var regionCountTag = metadataTag.Get<NbtInt>("RegionCount");
                        if (regionCountTag is not null) _litematicRegionCount = regionCountTag.Value;

                        // 读取总方块数
                        var totalBlocksTag = metadataTag.Get<NbtInt>("TotalBlocks");
                        if (totalBlocksTag is not null) _litematicTotalBlocks = totalBlocksTag.Value;

                        // 读取总体积
                        var totalVolumeTag = metadataTag.Get<NbtInt>("TotalVolume");
                        if (totalVolumeTag is not null) _litematicTotalVolume = totalVolumeTag.Value;
                    }
                    else
                    {
                        ModBase.Log("未找到 Litematic Metadata 节点", ModBase.LogLevel.Debug);
                    }
                }

                ModBase.Log("Litematic NBT 数据读取完成", ModBase.LogLevel.Debug);
            }
            catch (Exception ex)
            {
                ModBase.Log(ex, "读取 Litematic NBT 数据时出错（" + path + "）");
            }
        }

        #endregion

        #region Schem 文件处理

        /// <summary>
        ///     读取 .schem 文件的 NBT 数据（Sponge Schematic 格式）。
        /// </summary>
        private void LoadSchemNbtData()
        {
            try
            {
                ModBase.Log($"开始读取 Schem NBT 数据：{path}", ModBase.LogLevel.Debug);

                // 使用自动检测压缩格式
                using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    var scheNbt = new NbtFile();
                    scheNbt.LoadFromStream(fs, NbtCompression.AutoDetect);

                    // 读取Sponge版本信息
                    var versionTag = scheNbt.RootTag.Get<NbtInt>("Version");
                    if (versionTag is not null) _spongeVersion = versionTag.Value;

                    // 读取数据版本信息
                    var dataVersionTag = scheNbt.RootTag.Get<NbtInt>("DataVersion");
                    if (dataVersionTag is not null) _structureDataVersion = dataVersionTag.Value;

                    // 读取尺寸信息
                    var widthTag = scheNbt.RootTag.Get<NbtShort>("Width");
                    var heightTag = scheNbt.RootTag.Get<NbtShort>("Height");
                    var lengthTag = scheNbt.RootTag.Get<NbtShort>("Length");

                    if (widthTag is not null && heightTag is not null && lengthTag is not null)
                    {
                        _litematicEnclosingSize = $"{widthTag.Value} × {heightTag.Value} × {lengthTag.Value}";
                        _litematicTotalVolume = (short)(widthTag.Value * heightTag.Value) * lengthTag.Value;

                        // 对于Sponge格式，方块数量等于总体积（因为包含空气方块）
                        _litematicTotalBlocks = _litematicTotalVolume;
                    }

                    // 读取调色板信息来计算区域数量
                    var paletteTag = scheNbt.RootTag.Get<NbtCompound>("Palette");
                    if (paletteTag is not null) _litematicRegionCount = 1; // Sponge Schematic 通常只有一个区域

                    // 读取元数据
                    var metadataTag = scheNbt.RootTag.Get<NbtCompound>("Metadata");
                    if (metadataTag is not null)
                    {
                        // 读取名称
                        var nameTag = metadataTag.Get<NbtString>("Name");
                        if (nameTag is not null && !string.IsNullOrWhiteSpace(nameTag.Value))
                            _schemOriginalName = nameTag.Value;

                        // 读取作者信息
                        var authorTag = metadataTag.Get<NbtString>("Author");
                        if (authorTag is not null && !string.IsNullOrWhiteSpace(authorTag.Value))
                        {
                            _structureAuthor = authorTag.Value;
                            if (_Authors is null)
                                _Authors = _structureAuthor;
                        }
                    }
                }

                ModBase.Log("Schem NBT 数据读取完成", ModBase.LogLevel.Debug);
            }
            catch (Exception ex)
            {
                ModBase.Log(ex, "读取 Schem NBT 数据时出错（" + path + "）");
            }
        }

        #endregion

        #region Schematic 文件处理

        /// <summary>
        ///     读取 .schematic 文件的 NBT 数据（MCEdit/WorldEdit 格式）。
        /// </summary>
        private void LoadSchematicNbtData()
        {
            try
            {
                ModBase.Log($"开始读取 Schematic NBT 数据：{path}", ModBase.LogLevel.Debug);
                using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    var scheNbt = new NbtFile();
                    scheNbt.LoadFromStream(fs, NbtCompression.AutoDetect);
                    // 读取尺寸信息
                    var widthTag = scheNbt.RootTag.Get<NbtShort>("Width");
                    var heightTag = scheNbt.RootTag.Get<NbtShort>("Height");
                    var lengthTag = scheNbt.RootTag.Get<NbtShort>("Length");
                    if (widthTag is not null && heightTag is not null && lengthTag is not null)
                    {
                        _litematicEnclosingSize = $"{widthTag.Value} × {heightTag.Value} × {lengthTag.Value}";
                        _litematicTotalVolume = (short)(widthTag.Value * heightTag.Value) * lengthTag.Value;
                    }

                    // 读取材料列表
                    var materialsTag = scheNbt.RootTag.Get<NbtString>("Materials");
                    if (materialsTag is not null)
                        ModBase.Log($"Schematic 材料类型：{materialsTag.Value}", ModBase.LogLevel.Debug);

                    ModBase.Log("Schematic NBT 数据读取完成", ModBase.LogLevel.Debug);
                }
            }
            catch (Exception ex)
            {
                ModBase.Log(ex, "读取 Schematic NBT 数据时出错（" + path + "）");
            }
        }

        #endregion

        #region NBT 结构文件处理

        /// <summary>
        ///     读取 .nbt 文件的 NBT 数据（Minecraft 结构文件格式）。
        /// </summary>
        private void LoadStructureNbtData()
        {
            try
            {
                ModBase.Log($"开始读取 NBT 结构文件数据：{path}", ModBase.LogLevel.Debug);
                using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    var scheNbt = new NbtFile();
                    scheNbt.LoadFromStream(fs, NbtCompression.AutoDetect);
                    // 读取作者信息
                    var authorTag = scheNbt.RootTag.Get<NbtString>("author");
                    if (authorTag is not null && !string.IsNullOrWhiteSpace(authorTag.Value))
                    {
                        _structureAuthor = authorTag.Value;
                        if (_Authors is null)
                            _Authors = _structureAuthor;
                    }

                    // 读取尺寸信息
                    var sizeTag = scheNbt.RootTag.Get<NbtList>("size");
                    if (sizeTag is not null)
                    {
                        var sizeElements = sizeTag.ToArray();
                        if (sizeElements.Length >= 3)
                        {
                            var sizeArray = sizeElements.Take(3).Select(e => e.IntValue).ToArray();
                            _litematicEnclosingSize = $"{sizeArray[0]} × {sizeArray[1]} × {sizeArray[2]}";
                            _litematicTotalVolume = sizeArray[0] * sizeArray[1] * sizeArray[2];
                        }
                    }

                    // 读取方块数量信息
                    var blocksTag = scheNbt.RootTag.Get<NbtList>("blocks");
                    if (blocksTag is not null)
                        _litematicTotalBlocks = blocksTag.Where(x => x.TagType == NbtTagType.Compound).Count();

                    // 读取调色板信息来计算区域数量
                    var paletteTag = scheNbt.RootTag.Get<NbtList>("palette");
                    if (paletteTag is not null) _litematicRegionCount = 1; // 原版结构文件通常只有一个区域
                }
            }
            catch (Exception ex)
            {
                ModBase.Log(ex, "读取 NBT 结构文件数据时出错（" + path + "）");
            }
        }

        #endregion

        #region 基础

        /// <summary>
        ///     资源的文件的地址。
        /// </summary>
        public readonly string path;

        /// <summary>
        ///     是否为文件夹项。
        /// </summary>
        public bool IsFolder => path.EndsWithF(@"\__FOLDER__", true);

        /// <summary>
        ///     获取实际的文件夹路径（去除 __FOLDER__ 标记）。
        /// </summary>
        public string ActualPath
        {
            get
            {
                if (IsFolder) return path.Replace(@"\__FOLDER__", "");

                return path;
            }
        }

        public LocalCompFile(string path)
        {
            this.path = path ?? "";
        }

        /// <summary>
        ///     NBT数据是否已加载（用于延迟加载优化）。
        /// </summary>
        private bool _nbtDataLoaded;

        /// <summary>
        ///     Mod 资源的完整路径，去除最后的 .disabled 和 .old。
        /// </summary>
        public string RawPath => ModBase.GetPathFromFullPath(path) + RawFileName;

        /// <summary>
        ///     资源的完整文件名。
        /// </summary>
        public string FileName
        {
            get
            {
                if (IsFolder && !string.IsNullOrEmpty(Name)) return Name;

                return ModBase.GetFileNameFromPath(path);
            }
        }

        /// <summary>
        ///     Mod 资源的完整文件名，去除最后的 .disabled 和 .old。
        /// </summary>
        public string RawFileName => FileName.Replace(".disabled", "").Replace(".old", "");

        /// <summary>
        ///     资源的状态。对于 Mod 有 Disabled
        /// </summary>
        public LocalFileStatus State
        {
            get
            {
                Load();
                if (!IsFileAvailable) return LocalFileStatus.Unavailable;

                if (path.EndsWithF(".disabled", true) || path.EndsWithF(".old", true)) return LocalFileStatus.Disabled;

                return LocalFileStatus.Fine;
            }
        }

        public enum LocalFileStatus
        {
            Fine = 0,
            Disabled = 1,
            Unavailable = 2
        }

        #endregion

        #region 信息项

        /// <summary>
        ///     Mod 的名称。若不可用则为 ModID 或无扩展的文件名。
        /// </summary>
        public string Name
        {
            get
            {
                if (_Name is null)
                    Load();
                if (_Name is null)
                    _Name = _ModId;
                if (_Name is null)
                {
                    if (IsFolder)
                        _Name = ModBase.GetFolderNameFromPath(ActualPath);
                    else
                        _Name = ModBase.GetFileNameWithoutExtentionFromPath(path);
                }

                return _Name;
            }
            set
            {
                if (_Name is null && value is not null && !value.Contains("modname") && value.ToLower() != "name" &&
                    value.Length > 1 && (ModBase.Val(value).ToString() ?? "") != (value ?? "")) _Name = value;
            }
        }

        private string _Name;

        /// <summary>
        ///     Mod 的描述信息。
        /// </summary>
        public string Description
        {
            get
            {
                if (_Description is null)
                    Load();
                if (_Description is null && FileUnavailableReason is not null)
                    _Description = FileUnavailableReason.Message;
                // If _Description Is Nothing Then _Description = Path
                return _Description;
            }
            set
            {
                if (_Description is null && value is not null && value.Length > 2)
                {
                    _Description = value.Trim('\n');
                    // 优化显示：若以 [a-zA-Z0-9] 结尾，加上小数点句号
                    if (_Description.ToLower().LastIndexOfAny("qwertyuiopasdfghjklzxcvbnm0123456789".ToCharArray()) ==
                        _Description.Length - 1)
                        _Description += ".";
                }
            }
        }

        private string _Description;

        /// <summary>
        ///     文件类型标签。
        /// </summary>
        public List<string> Tags
        {
            get
            {
                if (field is null)
                {
                    field = new List<string>();
                    if (IsFolder)
                    {
                        field.Add("文件夹");
                    }
                    else
                    {
                        var extension = System.IO.Path.GetExtension(RawPath).ToLower();
                        switch (extension ?? "")
                        {
                            case ".litematic":
                            {
                                field.Add("原理图");
                                break;
                            }
                            case ".schem":
                            case ".schematic":
                            {
                                field.Add("Schematic结构");
                                break;
                            }
                            case ".nbt":
                            {
                                field.Add("原版结构");
                                break;
                            }
                        }
                    }
                }

                return field;
            }
        }

        /// <summary>
        ///     Mod 的版本，不保证符合版本格式规范。
        /// </summary>
        public string Version
        {
            get
            {
                if (_Version is null)
                    Load();
                return _Version;
            }
            set
            {
                if (_Version is not null && _Version.RegexCheck(@"[0-9.\-]+"))
                    return;
                if (value?.ContainsF("version", true) == true)
                    value = "version"; // 需要修改的标识
                _Version = value;
            }
        }

        public string _Version;

        /// <summary>
        ///     用于依赖检查的 ModID。
        /// </summary>
        public string ModId
        {
            get
            {
                if (_ModId is null)
                    Load();
                return _ModId;
            }
            set
            {
                if (value is null)
                    return;
                value = value.RegexSeek(RegexPatterns.ModIdMatch);
                if (value is null || value.Length <= 1 || (ModBase.Val(value).ToString() ?? "") == (value ?? ""))
                    return;
                if (value.ContainsF("name", true) || value.ContainsF("modid", true))
                    return;
                if (!possibleModId.Contains(value))
                    possibleModId.Add(value);
                if (_ModId is null)
                    _ModId = value;
            }
        }

        private string _ModId;

        /// <summary>
        ///     其他可能的 ModID。
        /// </summary>
        public List<string> possibleModId = new();

        /// <summary>
        ///     Mod 的主页。
        /// </summary>
        public string Url
        {
            get
            {
                if (field is null)
                    Load();
                return field;
            }
            set
            {
                if (field is null && value is not null && value.StartsWithF("http")) field = value;
            }
        }

        /// <summary>
        ///     Mod 的作者列表。
        /// </summary>
        public string Authors
        {
            get
            {
                if (_Authors is null)
                    Load();
                return _Authors;
            }
            set
            {
                if (_Authors is null && !string.IsNullOrWhiteSpace(value)) _Authors = value;
            }
        }

        private string _Authors;

        /// <summary>
        ///     Litematic 文件的创建时间戳。
        /// </summary>
        public long? LitematicTimeCreated
        {
            get
            {
                LoadNbtDataIfNeeded();
                return _litematicTimeCreated;
            }
        }

        private long? _litematicTimeCreated;

        /// <summary>
        ///     Litematic 文件的修改时间戳。
        /// </summary>
        public long? LitematicTimeModified
        {
            get
            {
                LoadNbtDataIfNeeded();
                return _litematicTimeModified;
            }
        }

        private long? _litematicTimeModified;

        /// <summary>
        ///     Schem 读取到的原始名称。
        /// </summary>
        public string SchemOriginalName
        {
            get
            {
                LoadNbtDataIfNeeded();
                return _schemOriginalName;
            }
        }

        private string _schemOriginalName;

        /// <summary>
        ///     Litematic 读取到的原始名称。
        /// </summary>
        public string LitematicOriginalName
        {
            get
            {
                LoadNbtDataIfNeeded();
                return _litematicOriginalName;
            }
        }

        private string _litematicOriginalName;

        /// <summary>
        ///     Litematic 文件的版本。
        /// </summary>
        public int? LitematicVersion
        {
            get
            {
                LoadNbtDataIfNeeded();
                return _litematicVersion;
            }
        }

        private int? _litematicVersion;

        /// <summary>
        ///     Litematic 文件的包围盒大小。
        /// </summary>
        public string LitematicEnclosingSize
        {
            get
            {
                LoadNbtDataIfNeeded();
                return _litematicEnclosingSize;
            }
        }

        private string _litematicEnclosingSize;

        /// <summary>
        ///     Litematic 文件的区域数量。
        /// </summary>
        public int? LitematicRegionCount
        {
            get
            {
                LoadNbtDataIfNeeded();
                return _litematicRegionCount;
            }
        }

        private int? _litematicRegionCount;

        /// <summary>
        ///     Litematic 文件的总方块数。
        /// </summary>
        public int? LitematicTotalBlocks
        {
            get
            {
                LoadNbtDataIfNeeded();
                return _litematicTotalBlocks;
            }
        }

        private int? _litematicTotalBlocks;

        /// <summary>
        ///     Litematic 文件的总体积。
        /// </summary>
        public int? LitematicTotalVolume
        {
            get
            {
                LoadNbtDataIfNeeded();
                return _litematicTotalVolume;
            }
        }

        private int? _litematicTotalVolume;

        /// <summary>
        ///     原版结构文件的游戏版本。
        /// </summary>
        public string StructureGameVersion
        {
            get
            {
                LoadNbtDataIfNeeded();
                return _structureGameVersion;
            }
        }

        private string _structureGameVersion;

        /// <summary>
        ///     原版结构文件的数据版本。
        /// </summary>
        public int? StructureDataVersion
        {
            get
            {
                LoadNbtDataIfNeeded();
                return _structureDataVersion;
            }
        }

        private int? _structureDataVersion;

        /// <summary>
        ///     原版结构文件的作者。
        /// </summary>
        public string StructureAuthor
        {
            get
            {
                LoadNbtDataIfNeeded();
                return _structureAuthor;
            }
        }

        private string _structureAuthor;

        /// <summary>
        ///     Sponge Schematic 文件的版本。
        /// </summary>
        public int? SpongeVersion
        {
            get
            {
                LoadNbtDataIfNeeded();
                return _spongeVersion;
            }
        }

        private int? _spongeVersion;

        /// <summary>
        ///     Mod 图标路径。
        /// </summary>
        public string Logo { get; set; }

        /// <summary>
        ///     依赖项，其中包括了 Minecraft 的版本要求。格式为 ModID - VersionRequirement，若无版本要求则为 Nothing。
        /// </summary>
        public Dictionary<string, string> Dependencies
        {
            get
            {
                Load();
                return _Dependencies;
            }
        }

        private Dictionary<string, string> _Dependencies = new();

        private void AddDependency(string modID, string versionRequirement = null)
        {
            // 确保信息正确
            if (modID is null || modID.Length < 2)
                return;
            modID = modID.ToLower();
            if (modID == "name" || (ModBase.Val(modID).ToString() ?? "") == (modID ?? ""))
                return; // 跳过 name 与纯数字 id
            if (versionRequirement is null ||
                (!versionRequirement.Contains(".") && !versionRequirement.Contains("-")) ||
                versionRequirement.Contains("$"))
                versionRequirement = null;
            else if (!versionRequirement.StartsWithF("[") && !versionRequirement.StartsWithF("(") &&
                     !versionRequirement.EndsWithF("]") && !versionRequirement.EndsWithF(")"))
                versionRequirement = "[" + versionRequirement + ",)";
            // 向依赖项中添加
            if (_Dependencies.ContainsKey(modID))
            {
                if (_Dependencies[modID] is null)
                    _Dependencies[modID] = versionRequirement;
            }
            else
            {
                _Dependencies.Add(modID, versionRequirement);
            }
        }

        #endregion

        #region 加载步骤标记

        // 1. 进行文件可用性检查
        // 成功：继续第二步。
        // 失败：标记 FileUnavailableReason， 并停止后续加载。
        /// <summary>
        ///     是否已进行 Mod 文件的基础加载。（这包括第一步和第二步）
        /// </summary>
        private bool isLoaded;

        /// <summary>
        ///     标记为已加载。用于内嵌（Jar-in-Jar）子项——其元数据已由 <see cref="ModJarInJar" /> 通过
        ///     LookupMetadata 从已打开的嵌套流读入，虚拟路径不是真实文件，须避免属性 getter 再触发 Load() 清空元数据。
        /// </summary>
        internal void MarkLoaded() => isLoaded = true;

        /// <summary>
        ///     Mod 文件是否可被正常读取。
        /// </summary>
        public bool IsFileAvailable
        {
            get
            {
                Load();
                return FileUnavailableReason is null;
            }
        }

        /// <summary>
        ///     Mod 文件出错的原因。若无错误，则为 Nothing。
        /// </summary>
        public Exception FileUnavailableReason
        {
            get
            {
                Load();
                return _FileUnavailableReason;
            }
        }

        private Exception _FileUnavailableReason;

        // 2. 进行 .class 以外的信息获取
        // 成功：标记 IsInfoWithoutClassAvailable。
        // 失败：什么也不干。如果需要补充信息的话，检测到 IsInfoWithoutClassAvailable 为 False，会自动继续加载。
        /// <summary>
        ///     是否已在不获取 .class 文件的前提下完成了所需信息的加载。
        /// </summary>
        private bool isInfoWithoutClassAvailable = false;

        // 3. 尝试从 .class 文件中获取信息
        // 成功：标记 IsInfoWithClassAvailable。
        // 失败：什么也不干。
        /// <summary>
        ///     是否已进行 .class 文件的信息获取。
        /// </summary>
        private bool isInfoWithClassLoaded;

        /// <summary>
        ///     是否已在 .class 文件中完成了所需信息的加载。
        /// </summary>
        private bool isInfoWithClassAvailable;

        #endregion

        #region 加载

        /// <summary>
        ///     初始化所有数据。
        /// </summary>
        private void Init()
        {
            _Name = null;
            _Description = null;
            _Version = null;
            _ModId = null;
            possibleModId = new List<string>();
            _Dependencies = new Dictionary<string, string>();
            isLoaded = false;
            _FileUnavailableReason = null;
            isInfoWithClassLoaded = false;
            isInfoWithClassAvailable = false;
        }

        /// <summary>
        ///     加载基本信息（不解析NBT数据）。
        /// </summary>
        public void LoadBasicInfo()
        {
            try
            {
                // 可用性检查
                if (IsFolder)
                {
                    // 文件夹项不需要进一步处理
                    isLoaded = true;
                    return;
                }

                if (!File.Exists(path))
                {
                    _FileUnavailableReason = new FileNotFoundException("未找到资源文件（" + path + ")");
                    isLoaded = true;
                    return;
                }

                // 对于原理图文件，只设置基本状态，不解析NBT数据
                if (path.EndsWithF(".litematic", true) || path.EndsWithF(".nbt", true) ||
                    path.EndsWithF(".schem", true) || path.EndsWithF(".schematic", true))
                {
                    _Name = ModBase.GetFileNameWithoutExtentionFromPath(path);
                    isLoaded = true;
                    return;
                }

                // 对于其他文件类型，正常加载
                Load();
            }
            catch (Exception ex)
            {
                ModBase.Log(ex, $"加载基本信息失败：{path}");
            }
        }

        /// <summary>
        ///     延迟加载NBT数据。
        /// </summary>
        public void LoadNbtDataIfNeeded()
        {
            try
            {
                // 如果已经加载过NBT数据，则跳过
                if (_nbtDataLoaded)
                    return;

                // 根据文件类型加载NBT数据
                if (path.EndsWithF(".litematic", true))
                    LoadLitematicNbtData();
                else if (path.EndsWithF(".nbt", true))
                    LoadStructureNbtData();
                else if (path.EndsWithF(".schem", true))
                    LoadSchemNbtData();
                else if (path.EndsWithF(".schematic", true)) LoadSchematicNbtData();

                _nbtDataLoaded = true;
            }
            catch (Exception ex)
            {
                ModBase.Log(ex, $"延迟加载NBT数据失败：{path}");
            }
        }

        /// <summary>
        ///     进行文件可用性检查与 .class 以外的信息获取。
        /// </summary>
        public void Load(bool forceReload = false)
        {
            if (isLoaded && !forceReload)
                return;
            // 初始化
            Init();

            // 基础可用性检查
            if (path.Length < 2)
            {
                _FileUnavailableReason = new FileNotFoundException("错误的资源文件路径（" + (path ?? "null") + "）");
                isLoaded = true;
                return;
            }

            // 对于文件夹项，检查实际文件夹路径是否存在
            if (IsFolder)
            {
                if (!Directory.Exists(ActualPath))
                {
                    _FileUnavailableReason = new DirectoryNotFoundException("未找到文件夹（" + ActualPath + "）");
                    isLoaded = true;
                    return;
                }

                // 文件夹项不需要进一步处理
                isLoaded = true;
                return;
            }

            if (!File.Exists(path))
            {
                _FileUnavailableReason = new FileNotFoundException("未找到资源文件（" + path + "）");
                isLoaded = true;
                return;
            }

            // 对于投影文件，跳过 zip 解析
            if (path.EndsWithF(".litematic", true) || path.EndsWithF(".nbt", true) || path.EndsWithF(".schem", true) ||
                path.EndsWithF(".schematic", true))
            {
                try
                {
                    _Name = ModBase.GetFileNameWithoutExtentionFromPath(path);
                    // 根据文件类型加载数据
                    if (path.EndsWithF(".litematic", true))
                    {
                        LoadLitematicNbtData();
                    }
                    else if (path.EndsWithF(".schem", true) || path.EndsWithF(".schematic", true))
                    {
                        if (path.EndsWithF(".schem", true))
                            LoadSchemNbtData();
                        else
                            LoadSchematicNbtData();
                    }
                    else if (path.EndsWithF(".nbt", true))
                    {
                        LoadStructureNbtData();
                    }

                    _nbtDataLoaded = true;
                }
                catch (Exception ex)
                {
                    ModBase.Log(ex, "投影文件信息获取失败（" + path + "）", ModBase.LogLevel.Developer);
                    _FileUnavailableReason = ex;
                }

                isLoaded = true;
                return;
            }

            // 对于其他文件，尝试作为 Jar 文件打开
            ZipArchive jar = null;
            try
            {
                jar = new ZipArchive(new FileStream(path, FileMode.Open));
                // 信息获取
                LookupMetadata(jar);
                EmbeddedMods = ModJarInJar.Resolve(path, jar);
            }
            catch (UnauthorizedAccessException ex)
            {
                ModBase.Log(ex, "资源文件由于无权限无法打开（" + path + "）", ModBase.LogLevel.Developer);
                _FileUnavailableReason = new UnauthorizedAccessException("没有读取此文件的权限，请尝试右键以管理员身份运行 PCL", ex);
            }
            catch (Exception ex)
            {
                ModBase.Log(ex, "资源文件无法打开（" + path + "）", ModBase.LogLevel.Developer);
                _FileUnavailableReason = ex;
            }
            finally
            {
                if (jar is not null)
                    jar.Dispose();
            }

            // 完成标记
            isLoaded = true;
        }

        /// <summary>
        ///     内嵌模组列表，由 <see cref="ModJarInJar" /> 解析填充。
        /// </summary>
        public List<LocalCompFile> EmbeddedMods { get; internal set; } = new();

        /// <summary>
        ///     从 Jar 文件中获取 Mod 信息。
        /// </summary>
        internal void LookupMetadata(ZipArchive jar)
        {
            #region 尝试使用 mcmod.info

            do
            {
                try
                {
                    // 获取信息文件
                    var infoEntry = jar.GetEntry("mcmod.info");
                    string infoString = null;
                    if (infoEntry is not null)
                    {
                        infoString = ModBase.ReadFile(infoEntry.Open());
                        if (infoString.Length < 15)
                            infoString = null;
                    }

                    if (infoString is null)
                        break;
                    // 获取可用 Json 项
                    JsonObject infoObject;
                    var jsonObject = (JsonNode)ModBase.GetJson(infoString);
                    if (jsonObject.GetValueKind() == JsonValueKind.Array)
                        infoObject = (JsonObject)jsonObject[0];
                    else
                        infoObject = (JsonObject)jsonObject["modList"][0];
                    // 从文件中获取 Mod 信息项
                    Name = (string)infoObject["name"];
                    Description = (string)infoObject["description"];
                    Version = (string)infoObject["version"];
                    Url = (string)infoObject["url"];
                    ModId = (string)infoObject["modid"];
                    var authorJson = (JsonArray)infoObject["authorList"];
                    if (authorJson is not null)
                    {
                        var author = new List<string>();
                        foreach (var Token in authorJson)
                            author.Add(Token.ToString());
                        if (author.Any())
                            Authors = author.Join(", ");
                    }

                    var logoFile = (string)infoObject["logoFile"];
                    if (logoFile is not null)
                    {
                        var logoItem = jar.GetEntry(logoFile);
                        if (logoItem is not null)
                        {
                            var md5 = ModBase.GetStringMD5(logoItem.Length + logoItem.CompressedLength + path);
                            Logo = System.IO.Path.Combine(ModBase.pathTemp, "Cache", "Images", $"{md5}.png");
                            using (var entryStream = logoItem.Open())
                            {
                                ModBase.WriteFile(Logo, entryStream);
                            }
                        }
                    }

                    var reqs = (JsonArray)infoObject["requiredMods"];
                    if (reqs is not null)
                        foreach (string item in reqs) // 将迭代变量重命名为 item
                            if (!string.IsNullOrEmpty(item))
                            {
                                // 使用一个局部变量 token 来处理逻辑
                                var token = item;

                                token = token.Substring(token.IndexOfF(":") + 1);
                                if (token.Contains("@"))
                                {
                                    var parts = token.Split("@");
                                    AddDependency(parts[0], parts[1]);
                                }
                                else
                                {
                                    AddDependency(token);
                                }
                            }

                    reqs = (JsonArray)infoObject["dependencies"];
                    if (reqs is not null)
                        foreach (string rawToken in reqs)
                            if (!string.IsNullOrEmpty(rawToken))
                            {
                                var id = rawToken.Substring(rawToken.IndexOfF(":") + 1);

                                if (id.Contains("@"))
                                {
                                    var parts = id.Split("@");
                                    AddDependency(parts[0], parts[1]);
                                }
                                else
                                {
                                    AddDependency(id);
                                }
                            }
                }
                catch (Exception ex)
                {
                    ModBase.Log(ex, "读取 mcmod.info 时出现未知错误（" + path + "）", ModBase.LogLevel.Developer);
                }
            } while (false);

            #endregion

            #region 尝试使用 fabric.mod.json

            do
            {
                try
                {
                    var fabricEntry = jar.GetEntry("fabric.mod.json");
                    string fabricText = null;
                    if (fabricEntry is not null)
                    {
                        fabricText = ModBase.ReadFile(fabricEntry.Open(), Encoding.UTF8);
                        if (!fabricText.Contains("schemaVersion")) fabricText = null;
                    }

                    if (fabricText is null) break;

                    var fabricObject = (JsonObject)ModBase.GetJson(fabricText);

                    if (fabricObject.ContainsKey("name")) Name = fabricObject["name"].ToString();
                    if (fabricObject.ContainsKey("version")) Version = fabricObject["version"].ToString();
                    if (fabricObject.ContainsKey("description")) Description = fabricObject["description"].ToString();
                    if (fabricObject.ContainsKey("id")) ModId = fabricObject["id"].ToString();
                    if (fabricObject.ContainsKey("contact") && fabricObject["contact"]["homepage"] is not null)
                        Url = fabricObject["contact"]["homepage"].ToString();

                    var authorJson = (JsonArray)fabricObject["authors"];
                    if (authorJson is not null)
                    {
                        var authorList = authorJson.Select(t => t.ToString()).ToList();
                        if (authorList.Any()) Authors = string.Join(", ", authorList);
                    }

                    if (fabricObject.ContainsKey("icon"))
                    {
                        var logoFile = fabricObject["icon"].ToString();
                        var logoItem = jar.GetEntry(logoFile);
                        if (logoItem is not null)
                        {
                            var md5 = ModBase.GetStringMD5(logoItem.Length + logoItem.CompressedLength + path);
                            Logo = System.IO.Path.Combine(ModBase.pathTemp, "Cache", "Images", $"{md5}.png");
                            using (var entryStream = logoItem.Open())
                            {
                                ModBase.WriteFile(Logo, entryStream);
                            }
                        }
                    }

                    // 依赖处理 (省略了 VB 中的注释部分，按逻辑实现)
                    if (fabricObject.ContainsKey("depends"))
                        foreach (var dep in (JsonObject)fabricObject["depends"])
                            AddDependency(dep.Key, dep.Value.ToString());
                }
                catch (Exception ex)
                {
                    ModBase.Log(ex, "读取 fabric.mod.json 时出错（" + path + "）", ModBase.LogLevel.Developer);
                }
            } while (false);

            #endregion

            #region 尝试使用 quilt.mod.json

            do
            {
                try
                {
                    // 获取 quilt.mod.json 文件
                    var quiltEntry = jar.GetEntry("quilt.mod.json");
                    string quiltText = null;
                    if (quiltEntry is not null)
                    {
                        quiltText = ModBase.ReadFile(quiltEntry.Open(), Encoding.UTF8);
                        if (!quiltText.Contains("schema_version"))
                            quiltText = null;
                    }

                    if (quiltText is null)
                        break;
                    var quiltObject = (JsonObject)((JsonObject)ModBase.GetJson(quiltText))["quilt_loader"];
                    // 从文件中获取 Mod 信息项
                    if (quiltObject.ContainsKey("id"))
                        ModId = (string)quiltObject["id"];
                    if (quiltObject.ContainsKey("version"))
                        Version = (string)quiltObject["version"];
                    if (quiltObject.ContainsKey("metadata"))
                    {
                        var quiltMetadata = (JsonObject)quiltObject["metadata"];
                        if (quiltMetadata.ContainsKey("name"))
                            Name = (string)quiltMetadata["name"];
                        if (quiltMetadata.ContainsKey("description"))
                            Description = (string)quiltMetadata["description"];
                        if (quiltMetadata.ContainsKey("contact"))
                            Url = (string)(quiltMetadata["contact"]["homepage"] ?? "");
                    }

                    if (quiltObject.ContainsKey("icon"))
                    {
                        var logoFile = (string)quiltObject["icon"];
                        if (logoFile is not null)
                        {
                            var logoItem = jar.GetEntry(logoFile);
                            if (logoItem is not null)
                            {
                                var md5 = ModBase.GetStringMD5(logoItem.Length + logoItem.CompressedLength + path);
                                Logo = System.IO.Path.Combine(ModBase.pathTemp, "Cache", "Images", $"{md5}.png");
                                using (var entryStream = logoItem.Open())
                                {
                                    ModBase.WriteFile(Logo, entryStream);
                                }
                            }
                        }
                    }

                    goto Finished;
                }
                catch (Exception ex)
                {
                    ModBase.Log(ex, "读取 quilt.mod.json 时出现未知错误（" + path + "）", ModBase.LogLevel.Developer);
                }
            } while (false);

            #endregion

            #region 尝试使用 mods.toml

            try
            {
                // 获取 mods.toml 文件
                var tomlEntry = jar.GetEntry("META-INF/mods.toml")
                                ?? jar.GetEntry("META-INF/neoforge.mods.toml");
                string tomlText = null;
                if (tomlEntry is not null)
                {
                    using (var reader = new StreamReader(tomlEntry.Open()))
                    {
                        tomlText = reader.ReadToEnd();
                    }

                    if (tomlText.Length < 15) tomlText = null;
                }

                if (tomlText is not null)
                {
                    // 文件标准化：统一换行符为 \n，去除注释、头尾的空格、空行
                    var lines = new List<string>();
                    var rawLines = tomlText.Replace("\r\n", "\n").Replace("\r", "\n").Split('\n');

                    foreach (var rawLine in rawLines)
                    {
                        var line = rawLine;
                        if (line.StartsWithF("#")) continue; // 去除注释
                        if (line.Contains("#")) line = line.Substring(0, line.IndexOfF("#"));
                        // 去除头尾的空格（包含全角空格）
                        line = line.Trim(' ', '\t', '　');
                        if (!string.IsNullOrEmpty(line)) lines.Add(line);
                    }

                    // 读取文件数据
                    // TomlData 存储段落名及其对应的键值对
                    var tomlData = new List<KeyValuePair<string, Dictionary<string, object>>>
                    {
                        new("", new Dictionary<string, object>())
                    };

                    for (var i = 0; i < lines.Count; i++)
                    {
                        var line = lines[i];
                        if (line.StartsWithF("[") && line.EndsWithF("]"))
                        {
                            // 段落标记
                            var header = line.Trim('[', ']');
                            tomlData.Add(
                                new KeyValuePair<string, Dictionary<string, object>>(header,
                                    new Dictionary<string, object>()));
                        }
                        else if (line.Contains("="))
                        {
                            // 字段标记
                            var key = line.Substring(0, line.IndexOfF("=")).TrimEnd(' ', '\t', '　');
                            var rawValue = line.Substring(line.IndexOfF("=") + 1).TrimStart(' ', '\t', '　');
                            object value;

                            if (rawValue.StartsWithF("\"") && rawValue.EndsWithF("\""))
                            {
                                // 单行字符串
                                value = rawValue.Trim('\"');
                            }
                            else if (rawValue.StartsWithF("'''"))
                            {
                                // 多行字符串
                                var valueLines = new List<string> { rawValue.Replace("'''", "") };
                                if (!rawValue.EndsWithF("'''") || rawValue.Length == 3)
                                    while (i < lines.Count - 1)
                                    {
                                        i++;
                                        var valueLine = lines[i];
                                        if (valueLine.EndsWithF("'''"))
                                        {
                                            valueLines.Add(valueLine.Replace("'''", ""));
                                            break;
                                        }

                                        valueLines.Add(valueLine);
                                    }

                                value = string.Join("\n", valueLines).Trim('\n').Replace("\n", "\r\n");
                            }
                            else if (rawValue.ToLower() == "true" || rawValue.ToLower() == "false")
                            {
                                // 布尔型
                                value = rawValue.ToLower() == "true";
                            }
                            else if (double.TryParse(rawValue, out var num))
                            {
                                // 数字型 (模拟 VB 的 Val)
                                value = num;
                            }
                            else
                            {
                                // 默认当做字符串存储
                                value = rawValue;
                            }

                            // 将值存入当前最后的段落中
                            var lastPair = tomlData[tomlData.Count - 1];
                            lastPair.Value[key] = value;
                        }
                    }

                    // 从解析出的数据中提取 Mod 信息
                    Dictionary<string, object> modEntry = null;
                    foreach (var subData in tomlData)
                        if (subData.Key == "mods")
                        {
                            modEntry = subData.Value;
                            break;
                        }

                    if (modEntry is not null && modEntry.ContainsKey("modId"))
                    {
                        ModId = modEntry["modId"].ToString();
                        // 假设 _ModId 是内部属性，如果为 null 说明设置失败
                        if (_ModId is not null)
                        {
                            if (modEntry.ContainsKey("displayName")) Name = modEntry["displayName"].ToString();
                            if (modEntry.ContainsKey("description")) Description = modEntry["description"].ToString();
                            if (modEntry.ContainsKey("version")) Version = modEntry["version"].ToString();

                            // [0] 是全局段落（无 Header）
                            if (tomlData[0].Value.ContainsKey("displayURL"))
                                Url = tomlData[0].Value["displayURL"].ToString();
                            if (tomlData[0].Value.ContainsKey("authors"))
                                Authors = tomlData[0].Value["authors"].ToString();

                            // 读取依赖
                            foreach (var subData in tomlData)
                                if (subData.Key.ToLower() == $"dependencies.{ModId.ToLower()}")
                                {
                                    var depEntry = subData.Value;
                                    if (depEntry.ContainsKey("modId") &&
                                        depEntry.ContainsKey("mandatory") && (bool)depEntry["mandatory"] &&
                                        depEntry.ContainsKey("side") &&
                                        depEntry["side"].ToString().ToLower() != "server")
                                        AddDependency(
                                            depEntry["modId"].ToString(),
                                            depEntry.ContainsKey("versionRange")
                                                ? depEntry["versionRange"].ToString()
                                                : null
                                        );
                                }

                            // 加载成功，跳转到完成标签
                            goto Finished;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ModBase.Log(ex, "读取 mods.toml 时出现未知错误（" + path + "）", ModBase.LogLevel.Developer);
            }

            #endregion

            #region 尝试使用 fml_cache_annotation.json

            do
            {
                try
                {
                    // 获取 fml_cache_annotation.json 文件
                    var fmlEntry = jar.GetEntry("META-INF/fml_cache_annotation.json");
                    string fmlText = null;
                    if (fmlEntry is not null)
                    {
                        fmlText = ModBase.ReadFile(fmlEntry.Open(), Encoding.UTF8);
                        if (!fmlText.Contains("Lnet/minecraftforge/fml/common/Mod;"))
                            fmlText = null;
                    }

                    if (fmlText is null)
                        break;
                    var fmlJson = (JsonObject)ModBase.GetJson(fmlText);
                    // 获取可用 Json 项
                    JsonObject fmlObject = null;
                    foreach (var ModFilePair in fmlJson)
                    {
                        var modFileAnnos = (JsonArray)ModFilePair.Value["annotations"];
                        if (modFileAnnos is not null)
                            // 先获取 Mod
                            foreach (var ModFileAnno in modFileAnnos)
                            {
                                var name = (string)(ModFileAnno["name"] ?? "");
                                if (name == "Lnet/minecraftforge/fml/common/Mod;")
                                {
                                    fmlObject = (JsonObject)ModFileAnno["values"];
                                    goto Got;
                                }
                            }
                    }

                    break;
                    Got: ;

                    // 从文件中获取 Mod 信息项
                    if (fmlObject.ContainsKey("useMetadata") &&
                        (fmlObject["useMetadata"]["value"] ?? "").ToString().ToLower() == "true")
                    {
                        // 要求使用 mcmod.info 中的信息
                        var value = (string)fmlObject["modid"]["value"];
                        if (value is null)
                            break;
                        value = value.ToLower().RegexSeek(RegexPatterns.ModIdMatch);
                        if (value is not null && value.ToLower() != "name" && value.Length > 1 &&
                            (ModBase.Val(value).ToString() ?? "") != (value ?? ""))
                            if (!possibleModId.Contains(value))
                                possibleModId.Add(value);
                        break;
                    }

                    if (fmlObject.ContainsKey("name"))
                        Name = (string)fmlObject["name"]["value"];
                    if (fmlObject.ContainsKey("version"))
                        Version = (string)fmlObject["version"]["value"];
                    if (fmlObject.ContainsKey("modid"))
                        ModId = (string)fmlObject["modid"]["value"];
                    if (!fmlObject.ContainsKey("serverSideOnly") ||
                        !fmlObject["serverSideOnly"]["value"].ToObject<bool>())
                    {
                        // 添加 Minecraft 依赖
                        var depMinecraft = (string)((fmlObject["acceptedMinecraftVersions"] is not null
                            ? fmlObject["acceptedMinecraftVersions"]["value"]
                            : "") ?? "");
                        if (!string.IsNullOrEmpty(depMinecraft))
                            AddDependency("minecraft", depMinecraft);
                        // 添加其他依赖
                        var deps = (string)((fmlObject["dependencies"] is not null
                            ? fmlObject["dependencies"]["value"]
                            : "") ?? "");
                        if (!string.IsNullOrEmpty(deps))
                            foreach (var item in deps.Split(";"))
                            {
                                if (string.IsNullOrEmpty(item) || !item.StartsWithF("required-"))
                                    continue;

                                // 使用局部变量处理逻辑，不要直接修改迭代变量 item
                                var dep = item.Substring(item.IndexOfF(":") + 1);

                                if (dep.Contains("@"))
                                {
                                    var parts = dep.Split("@");
                                    AddDependency(parts[0], parts[1]);
                                }
                                else
                                {
                                    AddDependency(dep);
                                }
                            }
                    }
                }
                catch (Exception ex)
                {
                    ModBase.Log(ex, "读取 fml_cache_annotation.json 时出现未知错误（" + path + "）");
                }
            } while (false);

            #endregion

            #region 尝试识别资源包图标

            try
            {
                // 检查并提取资源包的 pack.png 图标
                var packPngEntry = jar.GetEntry("pack.png");
                if (packPngEntry is not null)
                    try
                    {
                        var md5 = ModBase.GetStringMD5(packPngEntry.Length + packPngEntry.CompressedLength + path);
                        Logo = System.IO.Path.Combine(ModBase.pathTemp, "Cache", "Images", $"{md5}.png");
                        using (var entryStream = packPngEntry.Open())
                        {
                            ModBase.WriteFile(Logo, entryStream);
                        }

                        ModBase.Log("成功提取资源包图标：" + path, ModBase.LogLevel.Developer);
                    }
                    catch (Exception logoEx)
                    {
                        ModBase.Log(logoEx, "提取 pack.png 图标失败（" + path + "）", ModBase.LogLevel.Developer);
                    }
            }
            catch (Exception ex)
            {
                ModBase.Log(ex, "识别资源包图标时出现未知错误（" + path + "）", ModBase.LogLevel.Developer);
            }

            #endregion

            Finished: ;

            #region 将 Version 代号转换为 META-INF 中的版本

            if (_Version == "version")
                try
                {
                    var metaEntry = jar.GetEntry("META-INF/MANIFEST.MF");
                    if (metaEntry is not null)
                    {
                        var metaString = ModBase.ReadFile(metaEntry.Open()).Replace(" :", ":").Replace(": ", ":");
                        if (metaString.Contains("Implementation-Version:"))
                        {
                            metaString = metaString.Substring(metaString.IndexOfF("Implementation-Version:") +
                                                              "Implementation-Version:".Count());
                            metaString = metaString.Substring(0, metaString.IndexOfAny("\r\n".ToCharArray()))
                                .Trim();
                            Version = metaString;
                        }
                    }
                }
                catch (Exception ex)
                {
                    ModBase.Log("获取 META-INF 中的版本信息失败（" + path + "）", ModBase.LogLevel.Developer);
                    Version = null;
                }

            if (_Version is not null && !(_Version.Contains(".") || _Version.Contains("-")))
                Version = null;

            #endregion
        }

        #endregion

        #region 网络信息

        /// <summary>
        ///     当任何网络信息更新时触发。
        /// </summary>
        public event OnCompUpdateEventHandler? OnCompUpdate;

        public delegate void OnCompUpdateEventHandler(LocalCompFile sender);

        /// <summary>
        ///     该 Mod 关联的网络项目。
        /// </summary>
        public CompProject Comp
        {
            get => field;
            set
            {
                field = value;
                OnCompUpdate?.Invoke(this);
            }
        }

        /// <summary>
        ///     本地文件对应的联网文件信息。
        /// </summary>
        public CompFile compFile;

        /// <summary>
        ///     该 Mod 对应的联网最新版本。
        /// </summary>
        public CompFile UpdateFile
        {
            get => field;
            set
            {
                field = value;
                OnCompUpdate?.Invoke(this);
            }
        }

        /// <summary>
        ///     该 Mod 的更新日志网址。
        /// </summary>
        public List<string> changelogUrls = new();

        /// <summary>
        ///     所有网络信息是否已成功加载。
        /// </summary>
        public bool compLoaded;

        /// <summary>
        ///     将网络信息保存为 Json。
        /// </summary>
        public JsonObject ToJson()
        {
            var json = new JsonObject();
            if (Comp is not null)
                json.Add("Comp", Comp.ToJson());
            json.Add("ChangelogUrls", new JsonArray(changelogUrls.Select(s => (JsonNode)s).ToArray()));
            json.Add("CompLoaded", compLoaded);
            if (compFile is not null)
                json.Add("CompFile", compFile.ToJson());
            if (UpdateFile is not null)
                json.Add("UpdateFile", UpdateFile.ToJson());
            return json;
        }

        /// <summary>
        ///     从 Json 中读取网络信息。
        /// </summary>
        public void FromJson(JsonObject json)
        {
            compLoaded = (bool)json["CompLoaded"];
            if (json.ContainsKey("Comp"))
                Comp = new CompProject((JsonObject)json["Comp"]);
            if (json.ContainsKey("ChangelogUrls"))
                changelogUrls = json["ChangelogUrls"].ToObject<List<string>>();
            if (json.ContainsKey("CompFile"))
                compFile = new CompFile((JsonObject)json["CompFile"], CompType.Mod);
            if (json.ContainsKey("UpdateFile"))
                UpdateFile = new CompFile((JsonObject)json["UpdateFile"], CompType.Mod);
        }

        /// <summary>
        ///     该文件是否可以更新。
        /// </summary>
        public bool CanUpdate => !Config.Preference.Hide.FunctionModUpdate && changelogUrls.Any();

        /// <summary>
        ///     获取用于 CurseForge 信息获取的 Hash 值（MurmurHash2）。
        /// </summary>
        public uint CurseForgeHash
        {
            get
            {
                if (_CurseForgeHash is null)
                {
                    var buf = _hashCache.Value
                        .GetMurmurHash2Async(path)
                        .GetAwaiter().GetResult()
                        .HexToBytes();
                    _CurseForgeHash = BitConverter.ToUInt32(buf);
                }

                return (uint)_CurseForgeHash;
            }
        }

        private uint? _CurseForgeHash;

        /// <summary>
        ///     获取用于 Modrinth 信息获取的 Hash 值（SHA1）。
        /// </summary>
        public string ModrinthHash
        {
            get
            {
                if (field is null)
                    field = _hashCache.Value.GetSHA1Async(path).GetAwaiter().GetResult();

                return field;
            }
        }

        #endregion

        #region API

        public override string ToString()
        {
            return $"{State} - {path}";
        }

        public override bool Equals(object obj)
        {
            var target = obj as LocalCompFile;
            return target is not null && (path ?? "") == (target.path ?? "");
        }

        #endregion
    }

    /// <summary>
    ///     获取文件夹描述信息。
    /// </summary>
    private static string GetFolderDescription(string folderPath)
    {
        try
        {
            if (!Directory.Exists(folderPath))
                return "空文件夹";
            return "文件夹";
        }
        catch (Exception ex)
        {
            ModBase.Log(ex, $"获取文件夹描述失败：{folderPath}");
            return "文件夹";
        }
    }

    public class CompLocalLoaderData
    {
        public string compPath;
        public CompType compType;

        public KeyValuePair<List<LocalCompFile>, JsonObject> detailInfo;
        public PageInstanceCompResource frm;
        public McInstance gameVersion;
        public List<CompLoaderType> loaders;
    }

    // 加载资源列表
    public static LoaderTask<CompLocalLoaderData, List<LocalCompFile>> compResourceListLoader =
        new("Comp Resource List Loader", CompResourceListLoad);

    private static void CompResourceListLoad(LoaderTask<CompLocalLoaderData, List<LocalCompFile>> loader)
    {
        try
        {
            ModBase.RunInUiWait(() =>
            {
                if (loader.input.frm is not null) loader.input.frm.Load.ShowProgress = false;
            });

            // 等待 Mod 更新完成
            if (PageInstanceCompResource.updatingVersions.Contains(loader.input.compPath))
            {
                ModBase.Log("[Mod] 等待资源更新完成后才能继续加载资源列表：" + loader.input.compPath);
                try
                {
                    ModBase.RunInUiWait(() =>
                    {
                        if (loader.input.frm is not null)
                            loader.input.frm.Load.Text = Lang.Text("Instance.Resource.Update.WaitingForUpdate");
                    });
                    while (PageInstanceCompResource.updatingVersions.Contains(loader.input.compPath))
                    {
                        if (loader.IsAborted)
                            return;
                        Thread.Sleep(100);
                    }
                }
                finally
                {
                    ModBase.RunInUiWait(() =>
                    {
                        if (loader.input.frm is not null)
                            loader.input.frm.Load.Text = Lang.Text("Instance.Resource.List.Loading");
                    });
                }

                loader.input.frm.LoaderRun(LoaderFolderRunType.UpdateOnly);
            }

            // 获取 Mod 文件夹下的可用文件列表
            var modList = new List<LocalCompFile>();
            if (Directory.Exists(loader.input.compPath))
            {
                var rawName = loader.input.compPath.ToLower();

                if (loader.input.compType == CompType.Schematic)
                {
                    var currentFolderPath = "";
                    if (loader.input.frm is not null) currentFolderPath = loader.input.frm.CurrentFolderPath;

                    var searchPath = string.IsNullOrEmpty(currentFolderPath)
                        ? loader.input.compPath
                        : currentFolderPath;

                    try
                    {
                        var dirInfo = new DirectoryInfo(searchPath);
                        foreach (var Dir in dirInfo.EnumerateDirectories("*", SearchOption.AllDirectories))
                            modList.Add(new LocalCompFile(Path.Combine(Dir.FullName, "__FOLDER__")));
                        foreach (var File in dirInfo.EnumerateFiles("*", SearchOption.AllDirectories))
                            try
                            {
                                if (LocalCompFile.IsCompFile(File.FullName, loader.input.compType))
                                    modList.Add(new LocalCompFile(File.FullName));
                            }
                            catch (Exception ex)
                            {
                                ModBase.Log(ex, $"处理文件失败：{File.FullName}");
                            }
                    }
                    catch (Exception ex)
                    {
                        ModBase.Log(ex, $"枚举文件失败：{searchPath}");
                    }
                }
                else
                {
                    try
                    {
                        foreach (var File in ModBase.EnumerateFiles(loader.input.compPath))
                            try
                            {
                                if ((File.DirectoryName.ToLower() ?? "") != (rawName.TrimEnd('\\') ?? ""))
                                    if (!(PageInstanceLeft.McInstance is not null &&
                                          PageInstanceLeft.McInstance.Info.HasForge &&
                                          PageInstanceLeft.McInstance.Info.Drop < 130 && (File.Directory.Name ?? "") ==
                                          (PageInstanceLeft.McInstance.Info.VanillaName ?? "")))
                                        continue;

                                if (LocalCompFile.IsCompFile(File.FullName, loader.input.compType))
                                    modList.Add(new LocalCompFile(File.FullName));
                            }
                            catch (Exception ex)
                            {
                                ModBase.Log(ex, $"处理文件失败：{File.FullName}");
                            }
                    }
                    catch (Exception ex)
                    {
                        ModBase.Log(ex, $"枚举文件夹失败：{loader.input.compPath}");
                    }
                }
            }

            // 确定是否显示进度
            loader.Progress = 0.05d;
            if (modList.Count > 50)
                ModBase.RunInUi(() =>
                {
                    if (loader.input.frm is not null) loader.input.frm.Load.ShowProgress = true;
                });

            // 获取本地文件缓存
            var cachePath = ModBase.pathTemp + @"Cache\LocalComp.json";
            var cache = new JsonObject();
            try
            {
                var cacheContent = ModBase.ReadFile(cachePath);
                if (!string.IsNullOrWhiteSpace(cacheContent))
                {
                    cache = (JsonObject)ModBase.GetJson(cacheContent);
                    if (!cache.ContainsKey("version") || cache["version"].ToObject<int>() != localModCacheVersion)
                    {
                        ModBase.Log("[Mod] 本地 Mod 信息缓存版本已过期，将弃用这些缓存信息", ModBase.LogLevel.Debug);
                        cache = new JsonObject();
                    }
                }
            }
            catch (Exception ex)
            {
                ModBase.Log(ex, "读取本地 Mod 信息缓存失败，已重置");
                cache = new JsonObject();
            }

            cache["version"] = localModCacheVersion;

            // 加载 Mod 列表 - 优化：对于原理图文件，延迟加载NBT数据
            var modUpdateList = new List<LocalCompFile>();
            foreach (var ModEntry in modList)
            {
                loader.Progress += 0.94d / modList.Count;
                if (loader.IsAborted)
                    return;
                if (ModEntry.IsFolder)
                    continue;

                // 优化：对于原理图文件，只进行基础加载，不解析NBT数据
                if (loader.input.compType == CompType.Schematic)
                    ModEntry.LoadBasicInfo();
                else
                    // 加载 McMod 对象
                    ModEntry.Load();
                
                // 读取 Comp 缓存
                if (ModEntry.State == LocalCompFile.LocalFileStatus.Unavailable)
                    continue;
                var cacheKey = ModEntry.ModrinthHash + loader.input.gameVersion.Info.VanillaName +
                               loader.input.loaders.Join("");
                if (cache.ContainsKey(cacheKey))
                {
                    ModEntry.FromJson((JsonObject)cache[cacheKey]);
                    // 如果缓存中的信息在 6 小时以内更新过，则无需重新获取
                    if (ModEntry.compLoaded &&
                        DateTime.Now - cache[cacheKey]["Comp"]["CacheTime"].ToObject<DateTime>() <
                        new TimeSpan(6, 0, 0))
                        continue;
                }

                modUpdateList.Add(ModEntry);
            }

            loader.Progress = 0.99d;
            ModBase.Log(
                $"[Mod] 共有 {modList.Count} 个 Mod，其中 {modUpdateList.Where(m => m.Comp is null).Count()} 个需要联网获取信息，{modUpdateList.Where(m => m.Comp is not null).Count()} 个需要更新信息");

            // 排序
            modList.Sort((left, right) =>
            {
                if (left.State == LocalCompFile.LocalFileStatus.Unavailable !=
                    (right.State == LocalCompFile.LocalFileStatus.Unavailable))
                    return left.State == LocalCompFile.LocalFileStatus.Unavailable ? 1 : -1;

                return right.FileName.CompareTo(left.FileName);
            });

            // 回设
            if (loader.IsAborted)
                return;
            loader.output = modList;

            // 开始联网加载
            if (modUpdateList.Any())
            {
                // TODO: 添加信息获取中提示
                loader.input.detailInfo = new KeyValuePair<List<LocalCompFile>, JsonObject>(modUpdateList, cache);
                compUpdateDetailLoader.Start(loader.input, true);
            }
        }

        catch (Exception ex)
        {
            ModBase.Log(ex, "Mod 列表加载失败");
            throw;
        }
    }

    // 联网加载 Mod 详情
    public static LoaderTask<CompLocalLoaderData, int> compUpdateDetailLoader =
        new("Comp List Detail Loader", CompUpdateDetailLoad);

    private static void CompUpdateDetailLoad(LoaderTask<CompLocalLoaderData, int> loader)
    {
        var mods = loader.input.detailInfo.Key;
        var cache = loader.input.detailInfo.Value;
        // 获取作为检查目标的加载器和版本
        var modLoaders = loader.input.loaders;
        var compType = loader.input.compType;
        var mcInstance = loader.input.gameVersion.Info.VanillaName;

        // 开始网络获取
        ModBase.Log($"[Mod] 目标加载器：{string.Join("/", modLoaders)}，版本：{mcInstance}");
        var endedThreadCount = 0;
        var isFailed = false;
        var currentTaskId = Task.CurrentId ?? -1;

        // 从 Modrinth 获取信息
        ModBase.RunInNewThread(() =>
        {
            try
            {
                // 步骤 1：获取 Hash 与对应的工程 ID
                var modrinthHashes = mods.Select(m => m.ModrinthHash).ToList();
                var modrinthVersion = (JsonObject)ModBase.GetJson(ModDownload.DlModRequest(
                    "https://api.modrinth.com/v2/version_files", "POST",
                    $"{{\"hashes\": [\"{string.Join("\",\"", modrinthHashes)}\"], \"algorithm\": \"sha1\"}}",
                    "application/json"));
                ModBase.Log($"[Mod] 从 Modrinth 获取到 {modrinthVersion.Count} 个本地 Mod 的对应信息");

                // 步骤 2：尝试读取工程信息缓存，构建其他 Mod 的对应关系
                if (modrinthVersion.Count == 0) return;
                var modrinthMapping = new Dictionary<string, List<LocalCompFile>>();
                foreach (var Entry in mods)
                {
                    if (modrinthVersion[Entry.ModrinthHash] is null) continue;
                    if (modrinthVersion[Entry.ModrinthHash]["files"][0]["hashes"]["sha1"].ToString() !=
                        Entry.ModrinthHash) continue;

                    var projectId = modrinthVersion[Entry.ModrinthHash]["project_id"].ToString();
                    // 读取已加载的缓存，加快结果出现速度
                    if (compProjectCache.ContainsKey(projectId) && Entry.Comp is null)
                        Entry.Comp = compProjectCache[projectId];

                    if (!modrinthMapping.ContainsKey(projectId)) modrinthMapping[projectId] = new List<LocalCompFile>();
                    modrinthMapping[projectId].Add(Entry);

                    // 记录对应的 CompFile
                    var fileInfo = new CompFile((JsonObject)modrinthVersion[Entry.ModrinthHash], CompType.Mod);
                    if (Entry.compFile is null || Entry.compFile.ReleaseDate < fileInfo.ReleaseDate)
                        Entry.compFile = fileInfo;
                }

                if (loader.IsAbortedWithThread(currentTaskId)) return;
                ModBase.Log($"[Mod] 需要从 Modrinth 获取 {modrinthMapping.Count} 个本地 Mod 的工程信息");

                // 步骤 3：获取工程信息
                if (!modrinthMapping.Any()) return;
                var modrinthProject = (JsonArray)ModBase.GetJson(ModDownload.DlModRequest(
                    $"https://api.modrinth.com/v2/projects?ids=[\"{string.Join("\",\"", modrinthMapping.Keys)}\"]",
                    "GET", "", "application/json"));

                foreach (var ProjectJson in modrinthProject)
                {
                    var project = new CompProject((JsonObject)ProjectJson);
                    foreach (var Entry in modrinthMapping[project.Id]) Entry.Comp = project;
                }

                ModBase.Log("[Mod] 已从 Modrinth 获取本地 Mod 信息，继续获取更新信息");

                // 步骤 4：获取更新信息
                var targetLoaders = compType == CompType.DataPack
                    ? "datapack"
                    : string.Join("\",\"", modLoaders).ToLower();
                var modrinthUpdate = (JsonObject)ModBase.GetJson(ModDownload.DlModRequest(
                    "https://api.modrinth.com/v2/version_files/update", "POST",
                    $"{{\"hashes\": [\"{string.Join("\",\"", modrinthMapping.SelectMany(l => l.Value.Select(m => m.ModrinthHash)))}\"], \"algorithm\": \"sha1\", " +
                    $"\"loaders\": [\"{targetLoaders}\"],\"game_versions\": [\"{mcInstance}\"]}}", "application/json"));

                foreach (var Entry in mods)
                {
                    if (modrinthUpdate[Entry.ModrinthHash] is null || Entry.compFile is null) continue;
                    var updateFile = new CompFile((JsonObject)modrinthUpdate[Entry.ModrinthHash], CompType.Mod);
                    if (!updateFile.Available) continue;

                    if (ModBase.modeDebug)
                        ModBase.Log($"[Mod] 本地文件 {Entry.compFile.FileName} 在 Modrinth 上的最新版为 {updateFile.FileName}");
                    if (Entry.compFile.ReleaseDate >= updateFile.ReleaseDate ||
                        Entry.compFile.Hash == updateFile.Hash) continue;

                    // 设置更新日志与更新文件
                    if (Entry.UpdateFile is not null && updateFile.Hash == Entry.UpdateFile.Hash)
                    {
                        Entry.changelogUrls.Add(
                            $"https://modrinth.com/mod/{modrinthUpdate[Entry.ModrinthHash]["project_id"]}/changelog?g={mcInstance}");
                        Entry.UpdateFile.DownloadUrls.AddRange(updateFile.DownloadUrls);
                        Entry.UpdateFile = updateFile;
                    }
                    else if (Entry.UpdateFile is null || updateFile.ReleaseDate >= Entry.UpdateFile.ReleaseDate)
                    {
                        Entry.changelogUrls = new List<string>
                        {
                            $"https://modrinth.com/mod/{modrinthUpdate[Entry.ModrinthHash]["project_id"]}/changelog?g={mcInstance}"
                        };
                        Entry.UpdateFile = updateFile;
                    }
                }

                ModBase.Log("[Mod] 从 Modrinth 获取本地 Mod 信息结束");
            }
            catch (Exception ex)
            {
                ModBase.Log(ex, "从 Modrinth 获取本地 Mod 信息失败");
                isFailed = true;
            }
            finally
            {
                Interlocked.Increment(ref endedThreadCount);
            }
        }, "Mod List Detail Loader Modrinth");

        // 从 CurseForge 获取信息（工程 ID 与文件 ID 均为数字）
        ModBase.RunInNewThread(() =>
        {
            try
            {
                // 步骤 1：获取 Hash 与对应的工程信息
                var curseForgeHashes = new List<uint>();
                foreach (var entry in mods)
                {
                    curseForgeHashes.Add(entry.CurseForgeHash);
                    if (loader.IsAbortedWithThread(currentTaskId)) return;
                }

                var curseForgeResponse = (JsonObject)ModBase.GetJson(ModDownload.DlModRequest(
                    "https://api.curseforge.com/v1/fingerprints/432", "POST",
                    $"{{\"fingerprints\": [{string.Join(",", curseForgeHashes)}]}}", "application/json"));
                var curseForgeRaw = (JsonArray)curseForgeResponse["data"]["exactMatches"];
                ModBase.Log($"[Mod] 从 CurseForge 获取到 {curseForgeRaw.Count} 个本地 Mod 的对应信息");

                // 步骤 2：尝试读取工程信息缓存，构建本地文件与工程 ID 的对应关系
                if (!curseForgeRaw.Any()) return;
                var curseForgeMapping = new Dictionary<string, List<LocalCompFile>>();
                foreach (var project in curseForgeRaw)
                {
                    var projectId = project["id"].ToString();
                    var hash = project["file"]["fileFingerprint"].ToObject<uint>();
                    foreach (var Entry in mods)
                    {
                        if (Entry.CurseForgeHash != hash) continue;
                        // 读取已加载的缓存，加快结果出现速度
                        if (compProjectCache.ContainsKey(projectId) && Entry.Comp is null)
                            Entry.Comp = compProjectCache[projectId];

                        if (!curseForgeMapping.ContainsKey(projectId))
                            curseForgeMapping[projectId] = new List<LocalCompFile>();
                        curseForgeMapping[projectId].Add(Entry);

                        // 记录对应的 CompFile
                        var fileInfo = new CompFile((JsonObject)project["file"], CompType.Mod);
                        if (Entry.compFile is null || Entry.compFile.ReleaseDate < fileInfo.ReleaseDate)
                            Entry.compFile = fileInfo;
                    }
                }

                if (loader.IsAbortedWithThread(currentTaskId)) return;
                ModBase.Log($"[Mod] 需要从 CurseForge 获取 {curseForgeMapping.Count} 个本地 Mod 的工程信息");

                // 步骤 3：获取工程信息，同时归纳需要检查更新的文件 ID
                if (!curseForgeMapping.Any()) return;
                var curseForgeProject = (JsonArray)((JsonObject)ModBase.GetJson(ModDownload.DlModRequest(
                    "https://api.curseforge.com/v1/mods", "POST",
                    $"{{\"modIds\": [{string.Join(",", curseForgeMapping.Keys)}]}}", "application/json")))["data"];

                var updateFileIds = new Dictionary<int, List<LocalCompFile>>(); // FileId -> 本地 Mod 文件列表
                var fileIdToProjectSlug = new Dictionary<int, string>();
                foreach (var projectJson in curseForgeProject)
                {
                    if (projectJson["isAvailable"] is not null && !projectJson["isAvailable"].ToObject<bool>())
                        continue;

                    // 设置 Entry 中的工程信息
                    var project = new CompProject((JsonObject)projectJson);
                    if (!curseForgeMapping.ContainsKey(project.Id)) continue;
                    foreach (var Entry in curseForgeMapping[project.Id])
                    {
                        // 若已从 Modrinth 获取到工程信息，则保留它，仅再次触发修改事件以刷新 UI
                        if (Entry.Comp is not null && !Entry.Comp.FromCurseForge)
                        {
                            var existing = Entry.Comp;
                            Entry.Comp = existing;
                            continue;
                        }

                        Entry.Comp = project;
                    }

                    // 仅在加载器唯一时查找可能有更新的文件
                    if (modLoaders.Count != 1) continue;
                    string newestVersion = null;
                    var newestFileIds = new List<int>();
                    foreach (var indexEntry in (JsonArray)projectJson["latestFilesIndexes"])
                    {
                        // 加载器唯一且匹配
                        if (compType != CompType.DataPack &&
                            (indexEntry["modLoader"] is null ||
                             (int)modLoaders.Single() != indexEntry["modLoader"].ToObject<int>()))
                            continue;

                        var indexVersion = indexEntry["gameVersion"].ToString();
                        if (indexVersion != mcInstance) continue; // MC 版本匹配
                        // latestFilesIndexes 按时间从新到老排序，只保留最新的 MC 版本
                        if (newestVersion is not null &&
                            McVersionComparer.CompareVersion(newestVersion, indexVersion) > -1)
                            continue;

                        if (newestVersion != indexVersion)
                        {
                            newestVersion = indexVersion;
                            newestFileIds.Clear();
                        }

                        newestFileIds.Add(indexEntry["fileId"].ToObject<int>());
                    }

                    foreach (var fileId in newestFileIds)
                    {
                        if (!updateFileIds.ContainsKey(fileId)) updateFileIds[fileId] = new List<LocalCompFile>();
                        updateFileIds[fileId].AddRange(curseForgeMapping[project.Id]);
                        fileIdToProjectSlug[fileId] = project.Slug;
                    }
                }

                if (loader.IsAbortedWithThread(currentTaskId)) return;
                ModBase.Log(
                    $"[Mod] 已从 CurseForge 获取本地 Mod 信息，需要获取 {updateFileIds.Count} 个用于检查更新的文件信息");

                // 步骤 4：获取更新文件信息
                if (!updateFileIds.Any()) return;
                var curseForgeFiles = (JsonArray)((JsonObject)ModBase.GetJson(ModDownload.DlModRequest(
                    "https://api.curseforge.com/v1/mods/files", "POST",
                    $"{{\"fileIds\": [{string.Join(",", updateFileIds.Keys)}]}}", "application/json")))["data"];

                var updateFiles = new Dictionary<LocalCompFile, CompFile>();
                foreach (var fileJson in curseForgeFiles)
                {
                    var updateFile = new CompFile((JsonObject)fileJson, CompType.Mod);
                    if (!updateFile.Available) continue;
                    if (!int.TryParse(updateFile.Id, out var fileId) || !updateFileIds.ContainsKey(fileId))
                        continue;
                    foreach (var Entry in updateFileIds[fileId])
                    {
                        if (updateFiles.ContainsKey(Entry) && updateFiles[Entry].ReleaseDate >= updateFile.ReleaseDate)
                            continue;
                        updateFiles[Entry] = updateFile;
                    }
                }

                foreach (var pair in updateFiles)
                {
                    var Entry = pair.Key;
                    var updateFile = pair.Value;
                    if (Entry.compFile is null) continue;
                    if (ModBase.modeDebug)
                        ModBase.Log(
                            $"[Mod] 本地文件 {Entry.compFile.FileName} 在 CurseForge 上的最新版为 {updateFile.FileName}");
                    if (Entry.compFile.ReleaseDate >= updateFile.ReleaseDate ||
                        Entry.compFile.Hash == updateFile.Hash) continue;

                    var changelogUrl =
                        $"https://www.curseforge.com/minecraft/mc-mods/{fileIdToProjectSlug[int.Parse(updateFile.Id)]}/files/{updateFile.Id}";

                    // 设置更新日志与更新文件
                    if (Entry.UpdateFile is not null && updateFile.Hash == Entry.UpdateFile.Hash)
                    {
                        // 合并下载源
                        Entry.changelogUrls.Add(changelogUrl);
                        Entry.UpdateFile.DownloadUrls.AddRange(updateFile.DownloadUrls);
                    }
                    else if (Entry.UpdateFile is null || updateFile.ReleaseDate > Entry.UpdateFile.ReleaseDate)
                    {
                        // 替换
                        Entry.changelogUrls = new List<string> { changelogUrl };
                        Entry.UpdateFile = updateFile;
                    }
                }

                ModBase.Log("[Mod] 从 CurseForge 获取 Mod 更新信息结束");
            }
            catch (Exception ex)
            {
                ModBase.Log(ex, "从 CurseForge 获取本地 Mod 信息失败");
                isFailed = true;
            }
            finally
            {
                Interlocked.Increment(ref endedThreadCount);
            }
        }, "Mod List Detail Loader CurseForge");

        // 等待线程结束
        while (endedThreadCount < 2)
        {
            if (loader.IsAborted) return;
            Thread.Sleep(10);
        }

        // 保存缓存
        var cachedMods = mods.Where(m => m.Comp is not null).ToList();
        ModBase.Log($"[Mod] 联网获取本地 Mod 信息完成，为 {cachedMods.Count} 个 Mod 更新缓存");
        if (!cachedMods.Any()) return;

        foreach (var Entry in cachedMods)
        {
            Entry.compLoaded = !isFailed;
            cache[Entry.ModrinthHash + mcInstance + string.Join("", modLoaders)] = Entry.ToJson();
        }

        ModBase.WriteFile(Path.Combine(ModBase.pathTemp, "Cache", "LocalComp.json"),
            cache.ToJsonString(ModBase.modeDebug ? new JsonSerializerOptions(JsonCompat.SerializerOptions) { WriteIndented = true } : null));

        // 刷新 UI
        ModBase.RunInUi(() =>
        {
            if (ModMain.frmInstanceMod?.Filter == PageInstanceCompResource.FilterType.CanUpdate)
                ModMain.frmInstanceMod?.RefreshUI();
            else
                ModMain.frmInstanceMod?.RefreshBars();
        });
    }

    public static List<CompLoaderType> GetCurrentVersionModLoader()
    {
        var modLoaders = new List<CompLoaderType>();
        if (PageInstanceLeft.McInstance.Info.HasForge)
            modLoaders.Add(CompLoaderType.Forge);
        if (PageInstanceLeft.McInstance.Info.HasNeoForge)
            modLoaders.Add(CompLoaderType.NeoForge);
        if (PageInstanceLeft.McInstance.Info.HasFabric)
            modLoaders.Add(CompLoaderType.Fabric);
        if (PageInstanceLeft.McInstance.Info.HasQuilt)
            modLoaders.AddRange(new[] { CompLoaderType.Fabric, CompLoaderType.Quilt });
        if (PageInstanceLeft.McInstance.Info.HasLiteLoader)
            modLoaders.Add(CompLoaderType.LiteLoader);
        if (!modLoaders.Any())
            modLoaders.AddRange(new[]
            {
                CompLoaderType.Forge, CompLoaderType.NeoForge, CompLoaderType.Fabric, CompLoaderType.LiteLoader,
                CompLoaderType.Quilt
            });
        return modLoaders;
    }

    public static string GetPathNameByCompType(CompType theType)
    {
        switch (theType)
        {
            case CompType.Mod:
            {
                return "mods";
            }
            case CompType.ResourcePack:
            {
                return "resourcepacks";
            }
            case CompType.Shader:
            {
                return "shaderpacks";
            }
            case CompType.Schematic:
            {
                return "schematics";
            }
            case CompType.World:
            {
                return "saves";
            }
        }

        return "Nothing";
    }

    private static readonly Regex regexIsJarFile = new(@"\.jar(\.disabled)?$");

    /// <summary>
    ///     通过文件名关键字和 Mod ID 比如 <c>fabric</c> <c>api</c> 和 <c>fabric-api</c> 来获取给定实例 mods 目录中某个 Mod 的
    ///     <see cref="LocalCompFile" /> 对象
    ///     <br />
    ///     <b>为了不浪费性能，关键字统一用小写</b>
    /// </summary>
    /// <returns>
    ///     如果文件名包含主关键字，以及其他关键字中的任意一个，同时 Mod ID 一致，即认为匹配，返回对应的对象，若没有匹配的文件则返回空值。
    /// </returns>
    public static LocalCompFile GetModLocalCompByKeywords(McInstance instance, string modId,
        string mainKeyword, params string[] keywords)
    {
        if (modId is null)
            return null;
        return GetModLocalCompByKeywords(instance, new[] { modId }, mainKeyword, keywords);
    }

    public static LocalCompFile GetModLocalCompByKeywords(McInstance instance, string[] modIds,
        string mainKeyword, params string[] keywords)
    {
        if (!instance.Modable)
            return null; // 跳过不可安装 Mod 实例
        var modFolder = $"{instance.PathInstance}mods";
        if (!Directory.Exists(modFolder))
            return null; // 确保 mods 目录存在
        foreach (var file in Directory.EnumerateFiles(modFolder, $"*{mainKeyword}*"))
        {
            var lowerFilePath = file.ToLower(); // 统一转为小写
            if (!regexIsJarFile.IsMatch(lowerFilePath))
                continue; // 检查是否是 jar 文件
            if ((keywords.Length > 0) && !keywords.Any(keyword => lowerFilePath.Contains(keyword)))
                continue; // 检查是否包含关键字
            var localComp = new LocalCompFile(file);
            localComp.Load();
            if (modIds.Any(modId => (localComp.ModId ?? "") == (modId ?? "")))
                return localComp;
        }

        return null;
    }
}
