using System.Collections.Concurrent;
using System.Reflection;
using System.Runtime.Loader;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.VisualBasic.ApplicationServices;
using Newtonsoft.Json;
using OfficeOpenXml;
using static System.String;

namespace NBConfigBuilder;

using TableDictionary = SortedDictionary<string, List<int>>;

/// <summary>
/// Excel 数据导出器，用于从 Excel 文件导出数据到二进制格式和生成 C# 类文件。
/// </summary>
public sealed class ExcelExporter
{
    public ExportType ExportType;
    private readonly string _excelProgramPath;
    private readonly string _versionFilePath;

    // public VersionInfo VersionInfo; // 存储 Excel 文件的版本信息。
    private readonly Regex _regexName = new Regex("^[a-zA-Z][a-zA-Z0-9_]*$"); // 用于验证 Excel 表名的正则表达式。
    private readonly HashSet<string> _loadFiles = [".xlsx", ".xlsm", ".csv"]; // 加载的支持文件扩展名。

    private readonly ConcurrentDictionary<string, ExcelTable> _excelTables =
        new ConcurrentDictionary<string, ExcelTable>(); // 存储解析后的 Excel 表。

    /// <summary>
    /// 导表支持的数据类型集合。
    /// </summary>
    public static readonly HashSet<string> ColTypeSet =
    [
        "", "0", "bool", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "string",
        "IntDictionaryConfig", "StringDictionaryConfig",
        "short[]", "int[]", "long[]", "float[]", "string[]", "uint[]"
    ];

    // 进度报告回调
    private Action<string> _progressCallback;

    static ExcelExporter()
    {
        ExcelPackage.License.SetNonCommercialOrganization("Fantasy");
    }

    /// <summary>
    /// 根据指定的 exportType 初始化 ExcelExporter 类的新实例。
    /// </summary>
    /// <param name="exportType">要执行的导出类型（AllExcel 或 AllExcelIncrement）。</param>
    public ExcelExporter(ExportType exportType)
    {
        ExportType = exportType;

        if (App.Config.ExcelVersionPath == null || App.Config.ExcelVersionPath.Trim() == "")
        {
            Log.Info($"ExcelVersionFile Can not be empty!");
            return;
        }

        if (App.Config.ExcelPath == null || App.Config.ExcelPath.Trim() == "")
        {
            Log.Info($"ExcelProgramPath Can not be empty!");
            return;
        }

        _excelProgramPath = FileHelper.GetFullPath(App.Config.ExcelPath);
        _versionFilePath = FileHelper.GetFullPath(App.Config.ExcelVersionPath);

        if (App.Config.GenClient)
        {
            if (App.Config.ClientJsonPath == null ||
                App.Config.ClientJsonPath.Trim() == "")
            {
                Log.Info($"ExcelClientJsonDirectory Can not be empty!");
                return;
            }

            if (App.Config.ClientPath == null ||
                App.Config.ClientPath.Trim() == "")
            {
                Log.Info($"ExcelServerFileDirectory Can not be empty!");
                return;
            }
        }

        if (App.Config.GenServer)
        {
            if (App.Config.ServerPath == null ||
                App.Config.ServerPath.Trim() == "")
            {
                Log.Info($"ExcelServerFileDirectory Can not be empty!");
                return;
            }

            if (App.Config.ServerJsonPath == null ||
                App.Config.ServerJsonPath.Trim() == "")
            {
                Log.Info($"ExcelServerJsonDirectory Can not be empty!");
                return;
            }
        }

        switch (ExportType)
        {
            case ExportType.AllExcelIncrement:
            {
                break;
            }
            case ExportType.AllExcel:
            {
                if (File.Exists(_versionFilePath))
                {
                    File.Delete(_versionFilePath);
                }

                break;
            }
        }
    }

    /// <summary>
    /// 设置进度报告回调
    /// </summary>
    /// <param name="callback">进度报告回调函数</param>
    public void SetProgressCallback(Action<string> callback)
    {
        _progressCallback = callback;
    }

    public void Run()
    {
        ReportProgress("正在查找配置文件...");
        Find();
        ReportProgress("正在解析配置表...");
        Parsing();
        ReportProgress("正在导出JSON数据...");
        ExportToJson();
        ReportProgress("导出完成");
    }

    private void ReportProgress(string message)
    {
        _progressCallback?.Invoke(message);
    }

    /// <summary>
    /// 查找配置文件
    /// </summary>
    private void Find()
    {
        var dir = new DirectoryInfo(_excelProgramPath);
        var excelFiles = dir.GetFiles("*", SearchOption.AllDirectories);

        if (excelFiles.Length <= 0)
        {
            return;
        }


        var tables = new List<FileInfo>();

        // var tables = new OneToManyList<string, ExportInfo>();
        foreach (var excelFile in excelFiles)
        {
            // 过滤掉非指定后缀的文件

            if (!_loadFiles.Contains(excelFile.Extension))
            {
                continue;
            }

            var lastIndexOf = excelFile.Name.LastIndexOf(".", StringComparison.Ordinal);

            if (lastIndexOf < 0)
            {
                continue;
            }

            var fullName = excelFile.FullName;
            var excelName = excelFile.Name.Substring(0, lastIndexOf);
            var path = fullName.Substring(0, fullName.Length - excelFile.Name.Length);

            // 过滤~开头文件

            if (excelName.StartsWith("~", StringComparison.Ordinal))
            {
                continue;
            }

            // 如果文件名以#开头，那么这个文件夹下的所有文件都不导出
            if (excelName.StartsWith("#", StringComparison.Ordinal))
            {
                continue;
            }

            // 如果文件夹名包含#，那么这个文件夹下的所有文件都不导出
            if (path.Contains("#", StringComparison.Ordinal))
            {
                continue;
            }

            if (!_regexName.IsMatch(excelName))
            {
                Log.Info($"{excelName} 配置文件名非法");
                continue;
            }

            tables.Add(excelFile);
        }


        var generateTasks = new List<Task>();

        foreach (var table in tables)
        {
            var task = Task.Run(() =>
            {
                var worksheets = LoadExcel(table.FullName);
                foreach (var worksheet in worksheets)
                {
                    var tableName = worksheet.Name;
                    if (tableName.Contains("#", StringComparison.Ordinal))
                    {
                        continue;
                    }

                    ReportProgress($"正在解析配置表: {table.Name} Sheet:{tableName}");
                    var excelTable = new ExcelTable(worksheet);
                    try
                    {
                        var serverColInfoList = new List<int>();
                        var clientColInfoList = new List<int>();

                        for (var col = 3; col <= worksheet.Columns.EndColumn; col++)
                        {
                            // 列名字第一个字符是#不参与导出

                            var colName = worksheet.GetCellValue(5, col);
                            if (colName.StartsWith("#", StringComparison.Ordinal))
                            {
                                continue;
                            }

                            // 数值列不参与导出

                            var numericalCol = worksheet.GetCellValue(3, col);
                            if (numericalCol != "" && numericalCol != "0")
                            {
                                continue;
                            }

                            var serverType = worksheet.GetCellValue(1, col);
                            var clientType = worksheet.GetCellValue(2, col);
                            var isExportServer = !IsNullOrEmpty(serverType) && serverType != "0";
                            var isExportClient = !IsNullOrEmpty(clientType) && clientType != "0";

                            if (!isExportServer && !isExportClient)
                            {
                                continue;
                            }

                            if (isExportServer && isExportClient & serverType != clientType)
                            {
                                Log.Info(
                                    $"配置表 {tableName} {col} 列 [{colName}] 客户端类型 {clientType} 和 服务端类型 {serverType} 不一致");
                                continue;
                            }

                            if (!ColTypeSet.Contains(serverType) || !ColTypeSet.Contains(clientType))
                            {
                                Log.Info(
                                    $"配置表 {tableName} {col} 列 [{colName}] 客户端类型 {clientType}, 服务端类型 {serverType} 不合法");
                                continue;
                            }

                            if (!_regexName.IsMatch(colName))
                            {
                                Log.Info($"配置表 {tableName} {col} 列 [{colName}] 列名非法");
                                continue;
                            }

                            serverColInfoList.Add(col);

                            if (isExportClient)
                            {
                                clientColInfoList.Add(col);
                            }
                        }

                        if (clientColInfoList.Count > 0)
                        {
                            excelTable.ClientColInfos.Add(tableName, clientColInfoList);
                        }

                        if (serverColInfoList.Count > 0)
                        {
                            excelTable.ServerColInfos.Add(tableName, serverColInfoList);
                        }
                    }
                    catch (Exception e)
                    {
                        Log.Error($"Config : {tableName}, Name : {tableName}, Error : {e}");
                    }

                    _excelTables.TryAdd(tableName, excelTable);
                }
            });

            generateTasks.Add(task);
        }

        Task.WaitAll(generateTasks.ToArray());
    }

    /// <summary>
    /// 生成配置文件
    /// </summary>
    private void Parsing()
    {
        var generateTasks = new List<Task>();

        foreach ((var tableName, ExcelTable excelTable) in _excelTables)
        {
            var task = Task.Run(() =>
            {
                // 生成cs文件
                var writeToClassTask = new List<Task>();

                var table = excelTable;
                if (App.Config.GenServer)
                {
                    writeToClassTask.Add(Task.Run(() =>
                    {
                        ReportProgress($"正在生成服务端CS文件: {tableName}");
                        WriteToClass(table, true);
                    }));
                }

                if (App.Config.GenClient)
                {
                    writeToClassTask.Add(Task.Run(() =>
                    {
                        ReportProgress($"正在生成客户端CS文件: {tableName}");
                        WriteToClass(table, false);
                    }));
                }

                Task.WaitAll(writeToClassTask.ToArray());
            });

            generateTasks.Add(task);
        }

        Task.WaitAll(generateTasks.ToArray());
        Console.WriteLine("build cs success===");
    }

    #region json

    private Dictionary<string, List<Dictionary<string, object>>> _clientTableJsonDictionary =
        new Dictionary<string, List<Dictionary<string, object>>>();

    private Dictionary<string, List<Dictionary<string, object>>> _serverTableJsonDictionary =
        new Dictionary<string, List<Dictionary<string, object>>>();

    /// <summary>
    /// 把数据生成json
    /// </summary>
    private void ExportToJson()
    {
        _clientTableJsonDictionary.Clear();
        _serverTableJsonDictionary.Clear();
        foreach (var (tableName, excelTable) in _excelTables)
        {
            // 示例：客户端JSON导出
            if (App.Config.GenClient && excelTable.ClientColInfos.Count > 0)
            {
                ReportProgress($"正在生成客户端JSON文件: {tableName}");
                ExportTableToJson(excelTable, excelTable.ClientColInfos, _clientTableJsonDictionary);
            }

            // 示例：服务端JSON导出
            if (App.Config.GenServer && excelTable.ServerColInfos.Count > 0)
            {
                ReportProgress($"正在生成服务端JSON文件: {tableName}");
                ExportTableToJson(excelTable, excelTable.ServerColInfos, _serverTableJsonDictionary);
            }
        }

        if (_clientTableJsonDictionary.Count > 0)
        {
            ReportProgress("正在写入客户端JSON文件: configs.json");
            var json = JsonConvert.SerializeObject(_clientTableJsonDictionary, Formatting.Indented);
            File.WriteAllText(Path.Combine(App.Config.ClientJsonPath, $"configs.json"), json);
        }

        if (_serverTableJsonDictionary.Count > 0)
        {
            ReportProgress("正在写入服务端JSON文件: configs.json");
            var json = JsonConvert.SerializeObject(_serverTableJsonDictionary, Formatting.Indented);
            File.WriteAllText(Path.Combine(App.Config.ServerJsonPath, $"configs.json"), json);
        }
    }

    private void ExportTableToJson(ExcelTable table, TableDictionary colInfos,
        Dictionary<string, List<Dictionary<string, object>>> dictionary)
    {
        var result = new List<Dictionary<string, object>>();

        var worksheet = table.Sheet;
        foreach (var (fileName, columns) in colInfos)
        {
            // 从第7行开始读取数据（前6行为表头）
            for (int row = 7; row <= worksheet.Rows.EndRow; row++)
            {
                // 检查是否为空行
                bool isEmptyRow = true;
                foreach (var col in columns)
                {
                    var cellValue = worksheet.GetCellValue(row, col);
                    if (!IsNullOrEmpty(cellValue))
                    {
                        isEmptyRow = false;
                        break;
                    }
                }

                // 如果整行都为空，则停止处理
                if (isEmptyRow)
                {
                    break;
                }

                var rowData = new Dictionary<string, object>();

                foreach (var col in columns)
                {
                    var columnName = worksheet.GetCellValue(5, col);
                    var cellValue = worksheet.GetCellValue(row, col);
                    // 可在此处做类型转换
                    var columnType = GetColumnType(worksheet, col); // 需要实现获取列类型的方法
                    rowData[columnName] = ConvertCellValue(cellValue, columnType);
                }

                result.Add(rowData);
            }
        }

        dictionary[table.Name] = result;
    }

    private string GetColumnType(ExcelWorksheet worksheet, int column)
    {
        var serverType = worksheet.GetCellValue(1, column);
        var clientType = worksheet.GetCellValue(2, column);

        // 根据实际情况确定使用哪个类型，这里简化处理
        return !string.IsNullOrEmpty(serverType) && serverType != "0" ? serverType : clientType;
    }

    private object ConvertCellValue(string cellValue, string targetType)
    {
        if (string.IsNullOrEmpty(cellValue))
        {
            // 返回默认值
            if (targetType.EndsWith("[]"))
            {
                var elementType = targetType.Substring(0, targetType.Length - 2);
                return Array.CreateInstance(Type.GetType(elementType) ?? typeof(object), 0);
            }

            return GetDefaultValue(targetType);
        }

        // 处理数组类型
        if (targetType.EndsWith("[]"))
        {
            var elementType = targetType.Substring(0, targetType.Length - 2);
            var values = cellValue.Split(',');

            switch (elementType)
            {
                case "short":
                    return values.Select(v =>
                    {
                        short result;
                        return short.TryParse(v, out result) ? result : (short)0;
                    }).ToArray();

                case "ushort":
                    return values.Select(v =>
                    {
                        ushort result;
                        return ushort.TryParse(v, out result) ? result : (ushort)0;
                    }).ToArray();

                case "int":
                    return values.Select(v =>
                    {
                        int result;
                        return int.TryParse(v, out result) ? result : 0;
                    }).ToArray();

                case "uint":
                    return values.Select(v =>
                    {
                        uint result;
                        return uint.TryParse(v, out result) ? result : 0u;
                    }).ToArray();

                case "long":
                    return values.Select(v =>
                    {
                        long result;
                        return long.TryParse(v, out result) ? result : 0L;
                    }).ToArray();

                case "ulong":
                    return values.Select(v =>
                    {
                        ulong result;
                        return ulong.TryParse(v, out result) ? result : 0UL;
                    }).ToArray();

                case "bool":
                    return values.Select(v =>
                    {
                        bool result;
                        return bool.TryParse(v, out result) ? result : false;
                    }).ToArray();

                case "string":
                    return values.Select(v => v.Trim()).ToArray();

                default:
                    return values;
            }
        }

        // 处理单个值
        switch (targetType)
        {
            case "short":
                short shortResult;
                return short.TryParse(cellValue, out shortResult) ? shortResult : (short)0;

            case "ushort":
                ushort ushortResult;
                return ushort.TryParse(cellValue, out ushortResult) ? ushortResult : (ushort)0;

            case "int":
                int intResult;
                return int.TryParse(cellValue, out intResult) ? intResult : 0;

            case "uint":
                uint uintResult;
                return uint.TryParse(cellValue, out uintResult) ? uintResult : 0u;

            case "long":
                long longResult;
                return long.TryParse(cellValue, out longResult) ? longResult : 0L;

            case "ulong":
                ulong ulongResult;
                return ulong.TryParse(cellValue, out ulongResult) ? ulongResult : 0UL;

            case "bool":
                bool boolResult;
                if (bool.TryParse(cellValue, out boolResult))
                {
                    return boolResult;
                }
                else
                {
                    int numericBool;
                    return int.TryParse(cellValue, out numericBool) ? numericBool != 0 : false;
                }

            case "string":
                return cellValue;

            default:
                return cellValue;
        }
    }

    private object GetDefaultValue(string type)
    {
        switch (type)
        {
            case "short": return (short)0;
            case "ushort": return (ushort)0;
            case "int": return 0;
            case "uint": return 0u;
            case "long": return 0L;
            case "ulong": return 0UL;
            case "bool": return false;
            case "string": return "";
            case "short[]": return new short[0];
            case "ushort[]": return new ushort[0];
            case "int[]": return new int[0];
            case "uint[]": return new uint[0];
            case "long[]": return new long[0];
            case "ulong[]": return new ulong[0];
            case "bool[]": return new bool[0];
            case "string[]": return new string[0];
            default: return null;
        }
    }

    #endregion

    /// <summary>
    /// 从 Excel 文件加载工作表并返回 ExcelWorksheet 对象。
    /// </summary>
    /// <param name="name">工作表的名称或文件路径。</param>
    /// <returns>表示 Excel 工作表的 ExcelWorksheet 对象。</returns>
    public List<ExcelWorksheet> LoadExcel(string name)
    {
        var workbookWorksheets = ExcelHelper.LoadExcel(name).Workbook.Worksheets;
        return workbookWorksheets.ToList();
    }

    /// <summary>
    /// 写入到cs
    /// </summary>
    /// <param name="table"></param>
    /// <param name="isServer"></param>
    private void WriteToClass(ExcelTable table, bool isServer)
    {
        var colInfos = isServer ? table.ServerColInfos : table.ClientColInfos;
        var exportPath = isServer ? App.Config.ServerPath : App.Config.ClientPath;

        if (colInfos.Count <= 0)
        {
            return;
        }

        var excelWorksheet = table.Sheet;

        var index = 0;
        var fileBuilder = new StringBuilder();
        var colNameSet = new HashSet<string>();

        if (colInfos.Count == 0)
        {
            return;
        }

        var csName = Path.GetFileNameWithoutExtension(colInfos.First().Key)?.Split('_')[0];

        foreach (var (tableName, cols) in colInfos)
        {
            if (cols == null || cols.Count == 0)
            {
                continue;
            }


            foreach (var colIndex in cols)
            {
                var colName = excelWorksheet.GetCellValue(5, colIndex);

                if (!colNameSet.Add(colName))
                {
                    continue;
                }

                string colType;

                if (isServer)
                {
                    colType = excelWorksheet.GetCellValue(1, colIndex);

                    if (IsNullOrEmpty(colType) || colType == "0")
                    {
                        colType = excelWorksheet.GetCellValue(2, colIndex);
                    }
                }
                else
                {
                    colType = excelWorksheet.GetCellValue(2, colIndex);
                }

                var remarks = excelWorksheet.GetCellValue(4, colIndex);

                // 解决不同平台换行符不一致的问题

                switch (Environment.OSVersion.Platform)
                {
                    case PlatformID.Win32NT:
                    case PlatformID.Win32S:
                    case PlatformID.Win32Windows:
                    case PlatformID.WinCE:
                    {
                        fileBuilder.Append($"\r\n\t\t[ProtoMember({++index})]\r\n");
                        break;
                    }
                    default:
                    {
                        fileBuilder.Append($"\n\t\t[ProtoMember({++index})]\n");
                        break;
                    }
                }

                fileBuilder.Append(
                    IsArray(colType, out var t)
                        ? $"\t\tpublic {colType} {colName} {{ get; set; }} = Array.Empty<{t}>(); // {remarks}"
                        : $"\t\tpublic {colType} {colName} {{ get; set; }} // {remarks}");
            }
        }

        var template = GetTemplate(isServer);

        if (fileBuilder.Length > 0)
        {
            if (!Directory.Exists(exportPath))
            {
                FileHelper.CreateDirectory(exportPath);
            }

            var content = template.Replace("(ConfigName)", csName)
                .Replace("(Fields)", fileBuilder.ToString());
            File.WriteAllText(Path.Combine(exportPath, $"{csName}.cs"), content);
        }
    }

    private bool IsArray(string type, out string t)
    {
        t = null;
        var index = type.IndexOf("[]", StringComparison.Ordinal);

        if (index >= 0)
        {
            t = type.Remove(index, 2);
        }

        return index >= 0;
    }

    private string GetTemplate(bool isServer)
    {
        var fileName = isServer ? "TemplateServer.txt" : "TemplateClient.txt";
        return File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName));
    }
}