using DotNext;
using LinqToDB;
using LinqToDB.Data;
using System.Security.Cryptography;

namespace Database;

public class ResourceManager
{
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    private readonly AppDataConnection _db;

    public ResourceManager(AppDataConnection db)
    {
        this._db = db;
    }

    /// <summary>
    /// 根据文件扩展名获取MIME类型
    /// </summary>
    /// <param name="extension">文件扩展名</param>
    /// <param name="fileName">文件名（可选，用于特殊文件判断）</param>
    /// <returns>MIME类型</returns>
    private string GetMimeTypeFromExtension(string extension, string fileName = "")
    {
        // 特殊文件名处理
        if (!string.IsNullOrEmpty(fileName) && fileName.Equals("diagram.json", StringComparison.OrdinalIgnoreCase))
        {
            return "application/json";
        }

        return extension.ToLowerInvariant() switch
        {
            ".png" => "image/png",
            ".jpg" or ".jpeg" => "image/jpeg",
            ".gif" => "image/gif",
            ".bmp" => "image/bmp",
            ".svg" => "image/svg+xml",
            ".bit" => "application/octet-stream",
            ".sbit" => "application/octet-stream",
            ".bin" => "application/octet-stream",
            ".mcs" => "application/octet-stream",
            ".hex" => "text/plain",
            ".json" => "application/json",
            ".zip" => "application/zip",
            ".md" => "text/markdown",
            _ => "application/octet-stream"
        };
    }

    /// <summary>
    /// 将二进制数据写入指定路径
    /// </summary>
    /// <param name="path">目标文件路径</param>
    /// <param name="data">要写入的二进制数据</param>
    /// <returns>写入是否成功</returns>
    public Result<bool> WriteBytesToPath(string path, byte[] data)
    {
        try
        {
            var filePath = Path.Combine(Global.DataPath, path);
            var directory = Path.GetDirectoryName(filePath);
            if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }
            File.WriteAllBytes(filePath, data);
            logger.Info($"成功写入文件: {filePath}，大小: {data.Length} bytes");
            return new(true);
        }
        catch (Exception ex)
        {
            logger.Error($"写入文件时出错: {ex.Message}");
            return new(ex);
        }
    }

    /// <summary>
    /// 从指定路径读取二进制数据
    /// </summary>
    /// <param name="path">要读取的文件路径</param>
    /// <returns>读取到的二进制数据</returns>
    public Result<byte[]> ReadBytesFromPath(string path)
    {
        try
        {
            var filePath = Path.Combine(Global.DataPath, path);
            if (!File.Exists(filePath))
            {
                logger.Error($"文件不存在: {filePath}");
                return new(new Exception($"文件不存在: {filePath}"));
            }
            var data = File.ReadAllBytes(filePath);
            logger.Info($"成功读取文件: {filePath}，大小: {data.Length} bytes");
            return new(data);
        }
        catch (Exception ex)
        {
            logger.Error($"读取文件时出错: {ex.Message}");
            return new(ex);
        }
    }

    /// <summary>
    /// 添加资源
    /// </summary>
    /// <param name="userId">上传用户ID</param>
    /// <param name="resourceType">资源类型</param>
    /// <param name="resourcePurpose">资源用途（template 或 user）</param>
    /// <param name="resourceName">资源名称</param>
    /// <param name="data">资源二进制数据</param>
    /// <param name="examId">所属实验ID（可选）</param>
    /// <param name="mimeType">MIME类型（可选，将根据文件扩展名自动确定）</param>
    /// <returns>创建的资源</returns>
    public Result<Resource> AddResource(
        Guid userId, string resourceType, ResourcePurpose resourcePurpose,
        string resourceName, byte[] data, string? examId = null, string? mimeType = null)
    {
        try
        {
            // 验证用户是否存在
            var user = _db.UserTable.Where(u => u.ID == userId).FirstOrDefault();
            if (user == null)
            {
                logger.Error($"用户不存在: {userId}");
                return new(new Exception($"用户不存在: {userId}"));
            }

            // 如果指定了实验ID，验证实验是否存在
            if (!string.IsNullOrEmpty(examId))
            {
                var exam = _db.ExamTable.Where(e => e.ID.ToString() == examId).FirstOrDefault();
                if (exam == null)
                {
                    logger.Error($"实验不存在: {examId}");
                    return new(new Exception($"实验不存在: {examId}"));
                }
            }

            // 验证资源用途
            if (resourcePurpose != ResourcePurpose.Template &&
                resourcePurpose != ResourcePurpose.User)
            {
                logger.Error($"无效的资源用途: {resourcePurpose}");
                return new(new Exception($"无效的资源用途: {resourcePurpose}"));
            }

            // 如果未指定MIME类型，根据文件扩展名自动确定
            if (string.IsNullOrEmpty(mimeType))
            {
                var extension = Path.GetExtension(resourceName).ToLowerInvariant();
                mimeType = GetMimeTypeFromExtension(extension, resourceName);
            }

            // 计算数据的SHA256
            var sha256 = SHA256.HashData(data).ToString();
            if (string.IsNullOrEmpty(sha256))
            {
                logger.Error($"SHA256计算失败");
                return new(new Exception("SHA256计算失败"));
            }

            var duplicateResource = _db.ResourceTable.Where(r => r.SHA256 == sha256).FirstOrDefault();
            if (duplicateResource != null && duplicateResource.ResourceName == resourceName)
            {
                logger.Info($"资源已存在: {resourceName}");
                return duplicateResource;
            }

            var nowTime = DateTime.Now;
            var resource = new Resource
            {
                UserID = userId,
                ExamID = examId,
                ResourceType = resourceType,
                Purpose = resourcePurpose,
                ResourceName = resourceName,
                Path = duplicateResource == null ?
                    Path.Combine(resourceType, nowTime.ToString("yyyyMMddHH"), resourceName) :
                    duplicateResource.Path,
                SHA256 = sha256,
                MimeType = mimeType,
                UploadTime = nowTime
            };

            var insertedId = _db.Insert(resource);

            var writeRet = WriteBytesToPath(resource.Path, data);
            if (writeRet.IsSuccessful && writeRet.Value)
            {
                logger.Info($"新资源已添加: {userId}/{resourceType}/{resourcePurpose}/{resourceName} ({data.Length} bytes)" +
                           (examId != null ? $" [实验: {examId}]" : "") + $" [ID: {resource.ID}]");
                return new(resource);
            }
            else
            {
                _db.ResourceTable.Where(r => r.ID == resource.ID).Delete();

                logger.Error($"写入资源文件时出错: {writeRet.Error}");
                return new(new Exception(writeRet.Error?.ToString() ?? $"写入失败"));
            }
        }
        catch (Exception ex)
        {
            logger.Error($"添加资源时出错: {ex.Message}");
            return new(ex);
        }
    }

    /// <summary>
    /// 获取资源信息列表（返回ID和名称）
    /// <param name="resourceType">资源类型</param>
    /// <param name="examId">实验ID（可选）</param>
    /// <param name="resourcePurpose">资源用途（可选）</param>
    /// <param name="userId">用户ID（可选）</param>
    /// </summary>
    /// <returns>资源信息列表</returns>
    public Result<(string ID, string Name)[]> GetResourceListByType(
        string resourceType,
        ResourcePurpose? resourcePurpose = null,
        string? examId = null,
        Guid? userId = null)
    {
        try
        {
            var query = _db.ResourceTable.Where(r => r.ResourceType == resourceType);

            if (examId != null)
            {
                query = query.Where(r => r.ExamID == examId);
            }

            if (resourcePurpose != null)
            {
                query = query.Where(r => r.Purpose == resourcePurpose);
            }

            if (userId != null)
            {
                query = query.Where(r => r.UserID == userId);
            }

            var resources = query
                .Select(r => new { r.ID, r.ResourceName })
                .ToArray();

            var result = resources.Select(r => (r.ID.ToString(), r.ResourceName)).ToArray();
            logger.Info($"获取资源列表: {resourceType}" +
                       (examId != null ? $"/{examId}" : "") +
                       ($"/{resourcePurpose.ToString()}") +
                       (userId != null ? $"/{userId}" : "") +
                       $"，共 {result.Length} 个资源");
            return new(result);
        }
        catch (Exception ex)
        {
            logger.Error($"获取资源列表时出错: {ex.Message}");
            return new(ex);
        }
    }

    /// <summary>
    /// 获取完整的资源列表
    /// </summary>
    /// <param name="examId">实验ID（可选）</param>
    /// <param name="resourceType">资源类型（可选）</param>
    /// <param name="resourcePurpose">资源用途（可选）</param>
    /// <param name="userId">用户ID（可选）</param>
    /// <returns>完整的资源对象列表</returns>
    public Result<List<Resource>> GetFullResourceList(
        string? examId = null,
        string? resourceType = null,
        ResourcePurpose? resourcePurpose = null,
        Guid? userId = null)
    {
        try
        {
            var query = _db.ResourceTable.AsQueryable();

            if (examId != null)
            {
                query = query.Where(r => r.ExamID == examId);
            }

            if (resourceType != null)
            {
                query = query.Where(r => r.ResourceType == resourceType);
            }

            if (resourcePurpose != null)
            {
                query = query.Where(r => r.Purpose == resourcePurpose);
            }

            if (userId != null)
            {
                query = query.Where(r => r.UserID == userId);
            }

            var resources = query.OrderByDescending(r => r.UploadTime).ToList();
            logger.Info($"获取完整资源列表" +
                       (examId != null ? $" [实验: {examId}]" : "") +
                       (resourceType != null ? $" [类型: {resourceType}]" : "") +
                       ($" [用途: {resourcePurpose.ToString()}]") +
                       (userId != null ? $" [用户: {userId}]" : "") +
                       $"，共 {resources.Count} 个资源");
            return new(resources);
        }
        catch (Exception ex)
        {
            logger.Error($"获取完整资源列表时出错: {ex.Message}");
            return new(ex);
        }
    }

    /// <summary>
    /// 根据资源ID获取资源
    /// </summary>
    /// <param name="resourceId">资源ID</param>
    /// <returns>资源数据</returns>
    public Optional<Resource> GetResourceById(string resourceId)
    {
        var resource = _db.ResourceTable.Where(r => r.ID.ToString() == resourceId).FirstOrDefault();

        if (resource == null)
        {
            logger.Info($"未找到资源: {resourceId}");
            return new(null);
        }

        logger.Info($"成功获取资源: {resourceId} ({resource.ResourceName})");
        return new(resource);
    }

    /// <summary>
    /// 删除资源
    /// </summary>
    /// <param name="resourceId">资源ID</param>
    /// <returns>删除的记录数</returns>
    public Result<int> DeleteResource(string resourceId)
    {
        try
        {
            var result = _db.ResourceTable.Where(r => r.ID.ToString() == resourceId).Delete();
            logger.Info($"资源已删除: {resourceId}，删除记录数: {result}");
            return new(result);
        }
        catch (Exception ex)
        {
            logger.Error($"删除资源时出错: {ex.Message}");
            return new(ex);
        }
    }

}
