using Microsoft.Extensions.Logging;
using QYZH.InteractiveMagazine.Infrastructure.OSS;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Dto.Material;
using QYZH.InteractiveMagazine.Models.Enum;
using QYZH.InteractiveMagazine.Repository;
using SqlSugar;
using MaterialEntity = QYZH.InteractiveMagazine.Models.Entity.Material;
using MaterialFileEntity = QYZH.InteractiveMagazine.Models.Entity.MaterialFile;

namespace QYZH.InteractiveMagazine.Service;

/// <summary>
/// 资料服务实现
/// </summary>
public class MaterialService(
    BaseRepository<MaterialEntity> materialRepository,
    BaseRepository<MaterialFileEntity> materialFileRepository,
    OssService ossService,
    ILogger<MaterialService> logger) : BaseRepository<MaterialEntity>, IMaterialService
{
    /// <summary>
    /// 创建资料
    /// </summary>
    public async Task<MaterialOutput> CreateAsync(MaterialInput input)
    {
        ValidateInput(input);

        var material = new MaterialEntity
        {
            Title = input.Title.Trim(),
            Name = Normalize(input.Name),
            Description = Normalize(input.Description),
            Sort = input.Sort,
            StartTime = input.StartTime,
            EndTime = input.EndTime,
            Status = (int)DefaultStatusEnum.Active,
            IsDeleted = false,
            CreatedBy = "System",
            CreatedAt = DateTime.Now,
            UpdatedBy = "System",
            UpdatedAt = DateTime.Now
        };

        await materialRepository.UseTranAsync(async () =>
        {
            var materialResult = await materialRepository.InsertAsync(material);
            BusinessException.ThrowIf(!materialResult, "创建资料失败", ResultCode.GLOBAL_ERROR);

            var files = await BuildFileEntitiesAsync(material.Id, input.Files);
            var fileResult = await materialFileRepository.Context.Insertable(files).ExecuteCommandAsync();
            BusinessException.ThrowIf(fileResult <= 0, "创建资料文件失败", ResultCode.GLOBAL_ERROR);
        });

        logger.LogInformation("资料创建成功：{Id}", material.Id);
        return await GetByIdAsync(material.Id);
    }

    /// <summary>
    /// 更新资料
    /// </summary>
    public async Task<MaterialOutput> UpdateAsync(long id, MaterialInput input)
    {
        ValidateInput(input);

        var material = await GetValidMaterialAsync(id);
        material.Title = input.Title.Trim();
        material.Name = Normalize(input.Name);
        material.Description = Normalize(input.Description);
        material.Sort = input.Sort;
        material.StartTime = input.StartTime;
        material.EndTime = input.EndTime;
        material.UpdatedBy = "System";
        material.UpdatedAt = DateTime.Now;

        await materialRepository.UseTranAsync(async () =>
        {
            var materialResult = await materialRepository.UpdateAsync(material);
            BusinessException.ThrowIf(!materialResult, "更新资料失败", ResultCode.GLOBAL_ERROR);

            await SoftDeleteFilesAsync(material.Id);
            var files = await BuildFileEntitiesAsync(material.Id, input.Files);
            var fileResult = await materialFileRepository.Context.Insertable(files).ExecuteCommandAsync();
            BusinessException.ThrowIf(fileResult <= 0, "更新资料文件失败", ResultCode.GLOBAL_ERROR);
        });

        logger.LogInformation("资料更新成功：{Id}", material.Id);
        return await GetByIdAsync(material.Id);
    }

    /// <summary>
    /// 删除资料
    /// </summary>
    public async Task DeleteAsync(long id)
    {
        await GetValidMaterialAsync(id);

        await materialRepository.UseTranAsync(async () =>
        {
            var materialResult = await materialRepository.Context.Updateable<MaterialEntity>()
                .SetColumns(m => new MaterialEntity
                {
                    IsDeleted = true,
                    UpdatedBy = "System",
                    UpdatedAt = DateTime.Now
                })
                .Where(m => m.Id == id && !m.IsDeleted)
                .ExecuteCommandAsync();
            BusinessException.ThrowIf(materialResult <= 0, "删除资料失败", ResultCode.GLOBAL_ERROR);

            await SoftDeleteFilesAsync(id);
        });

        logger.LogInformation("资料删除成功：{Id}", id);
    }

    /// <summary>
    /// 根据ID获取资料
    /// </summary>
    public async Task<MaterialOutput> GetByIdAsync(long id)
    {
        var material = await GetValidMaterialAsync(id);
        var files = await GetFilesAsync(material.Id);
        return MapToOutput(material, files);
    }

    /// <summary>
    /// 分页查询资料列表
    /// </summary>
    public async Task<PageListModel<MaterialOutput>> GetListAsync(MaterialQueryInput input)
    {
        BusinessException.ThrowIf(input.PageIndex <= 0, "页码必须大于0", ResultCode.BAD_REQUEST);
        BusinessException.ThrowIf(input.PageSize <= 0 || input.PageSize > 100, "每页条数必须在1-100之间", ResultCode.BAD_REQUEST);

        RefAsync<int> totalNumber = 0;
        var materials = await materialRepository.Queryable()
            .Where(m => !m.IsDeleted)
            .WhereIF(!string.IsNullOrWhiteSpace(input.Title), m => m.Title.Contains(input.Title!.Trim()))
            .WhereIF(!string.IsNullOrWhiteSpace(input.Name), m => m.Name != null && m.Name.Contains(input.Name!.Trim()))
            .WhereIF(input.Status.HasValue, m => m.Status == input.Status!.Value)
            .OrderBy(m => m.Sort)
            .OrderByDescending(m => m.CreatedAt)
            .ToPageListAsync(input.PageIndex, input.PageSize, totalNumber);

        var result = await BuildOutputsAsync(materials);
        return new PageListModel<MaterialOutput>(result, input.PageIndex, input.PageSize, totalNumber);
    }

    /// <summary>
    /// 更新资料启用/禁用状态
    /// </summary>
    public async Task<bool> UpdateStatusAsync(long id)
    {
        var material = await GetValidMaterialAsync(id);
        material.Status = material.Status == (int)DefaultStatusEnum.Active
            ? (int)DefaultStatusEnum.Inactive
            : (int)DefaultStatusEnum.Active;
        material.UpdatedBy = "System";
        material.UpdatedAt = DateTime.Now;

        var result = await materialRepository.UpdateAsync(material);
        BusinessException.ThrowIf(!result, "更新资料状态失败", ResultCode.GLOBAL_ERROR);

        logger.LogInformation("资料状态更新成功：{Id}，状态：{Status}", id, material.Status);
        return result;
    }

    /// <summary>
    /// 获取小程序可见资料列表
    /// </summary>
    public async Task<List<MaterialOutput>> GetEnabledListAsync()
    {
        var now = DateTime.Now;
        var materials = await materialRepository.Queryable()
            .Where(m => !m.IsDeleted)
            .Where(m => m.Status == (int)DefaultStatusEnum.Active)
            .Where(m => m.StartTime == null || m.StartTime <= now)
            .Where(m => m.EndTime == null || m.EndTime >= now)
            .OrderBy(m => m.Sort)
            .OrderByDescending(m => m.CreatedAt)
            .ToListAsync();

        return await BuildOutputsAsync(materials);
    }

    private async Task<MaterialEntity> GetValidMaterialAsync(long id)
    {
        var material = await materialRepository.Queryable()
            .Where(m => m.Id == id && !m.IsDeleted)
            .FirstAsync();

        BusinessException.ThrowIf(material == null, "资料不存在", ResultCode.NOT_FOUND);
        return material;
    }

    private async Task<List<MaterialFileEntity>> BuildFileEntitiesAsync(long materialId, List<MaterialFileInput> inputs)
    {
        var helper = CreateImageHelper();
        var files = new List<MaterialFileEntity>();

        foreach (var input in inputs.OrderBy(f => f.Sort))
        {
            var file = new MaterialFileEntity
            {
                MaterialId = materialId,
                FileType = input.FileType,
                FileUrl = input.FileUrl.Trim(),
                FileName = Normalize(input.FileName),
                Sort = input.Sort,
                Status = (int)DefaultStatusEnum.Active,
                IsDeleted = false,
                CreatedBy = "System",
                CreatedAt = DateTime.Now,
                UpdatedBy = "System",
                UpdatedAt = DateTime.Now
            };

            if (OssImageHelper.IsTempImage(file.FileUrl))
            {
                file.FileUrl = await helper.MoveToFormalAsync(file.FileUrl, GetMaterialFormalFolder(materialId, file.FileType, file.Id));
            }

            files.Add(file);
        }

        return files;
    }

    private async Task SoftDeleteFilesAsync(long materialId)
    {
        await materialFileRepository.Context.Updateable<MaterialFileEntity>()
            .SetColumns(f => new MaterialFileEntity
            {
                IsDeleted = true,
                UpdatedBy = "System",
                UpdatedAt = DateTime.Now
            })
            .Where(f => f.MaterialId == materialId && !f.IsDeleted)
            .ExecuteCommandAsync();
    }

    private async Task<List<MaterialFileEntity>> GetFilesAsync(long materialId)
    {
        return await materialFileRepository.Queryable()
            .Where(f => f.MaterialId == materialId && !f.IsDeleted)
            .OrderBy(f => f.Sort)
            .OrderByDescending(f => f.CreatedAt)
            .ToListAsync();
    }

    private async Task<List<MaterialOutput>> BuildOutputsAsync(List<MaterialEntity> materials)
    {
        if (materials.Count == 0)
        {
            return new List<MaterialOutput>();
        }

        var materialIds = materials.Select(m => m.Id).ToList();
        var files = await materialFileRepository.Queryable()
            .Where(f => materialIds.Contains(f.MaterialId) && !f.IsDeleted)
            .OrderBy(f => f.Sort)
            .OrderByDescending(f => f.CreatedAt)
            .ToListAsync();
        var fileMap = files.GroupBy(f => f.MaterialId).ToDictionary(g => g.Key, g => g.ToList());

        return materials.Select(m => MapToOutput(m, fileMap.GetValueOrDefault(m.Id) ?? new List<MaterialFileEntity>())).ToList();
    }

    private static void ValidateInput(MaterialInput input)
    {
        BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.Title), "资料标题不能为空", ResultCode.BAD_REQUEST);
        BusinessException.ThrowIf(input.Title.Trim().Length > 100, "资料标题长度不能超过100个字符", ResultCode.BAD_REQUEST);
        BusinessException.ThrowIf(!string.IsNullOrWhiteSpace(input.Name) && input.Name.Length > 100, "资料名称长度不能超过100个字符", ResultCode.BAD_REQUEST);
        BusinessException.ThrowIf(!string.IsNullOrWhiteSpace(input.Description) && input.Description.Length > 1000, "资料描述长度不能超过1000个字符", ResultCode.BAD_REQUEST);
        BusinessException.ThrowIf(input.Sort < 0, "排序值不能小于0", ResultCode.BAD_REQUEST);
        BusinessException.ThrowIf(input.StartTime.HasValue && input.EndTime.HasValue && input.EndTime <= input.StartTime, "结束时间必须大于开始时间", ResultCode.BAD_REQUEST);
        BusinessException.ThrowIf(input.Files == null || input.Files.Count == 0, "资料文件不能为空", ResultCode.BAD_REQUEST);

        foreach (var file in input.Files)
        {
            BusinessException.ThrowIf(!Enum.IsDefined(typeof(MaterialFileTypeEnum), file.FileType), "资料文件类型不正确", ResultCode.BAD_REQUEST);
            BusinessException.ThrowIf(string.IsNullOrWhiteSpace(file.FileUrl), "资料文件地址不能为空", ResultCode.BAD_REQUEST);
            BusinessException.ThrowIf(file.FileUrl.Trim().Length > 500, "资料文件地址长度不能超过500个字符", ResultCode.BAD_REQUEST);
            BusinessException.ThrowIf(!string.IsNullOrWhiteSpace(file.FileName) && file.FileName.Length > 100, "资料文件名称长度不能超过100个字符", ResultCode.BAD_REQUEST);
            BusinessException.ThrowIf(file.Sort < 0, "资料文件排序值不能小于0", ResultCode.BAD_REQUEST);
        }
    }

    private OssImageHelper CreateImageHelper() => new(ossService, logger);

    private static string GetMaterialFormalFolder(long materialId, MaterialFileTypeEnum fileType, long fileId)
    {
        return $"material/{materialId}/{fileType.ToString().ToLowerInvariant()}/{fileId}";
    }

    private static string? Normalize(string? value)
    {
        return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
    }

    private static MaterialOutput MapToOutput(MaterialEntity material, List<MaterialFileEntity> files)
    {
        return new MaterialOutput
        {
            Id = material.Id,
            Title = material.Title,
            Name = material.Name,
            Description = material.Description,
            Sort = material.Sort,
            StartTime = material.StartTime,
            EndTime = material.EndTime,
            Status = material.Status,
            CreatedBy = material.CreatedBy,
            CreatedAt = material.CreatedAt,
            UpdatedBy = material.UpdatedBy,
            UpdatedAt = material.UpdatedAt,
            Files = files.Select(MapFileToOutput).ToList()
        };
    }

    private static MaterialFileOutput MapFileToOutput(MaterialFileEntity file)
    {
        return new MaterialFileOutput
        {
            Id = file.Id,
            FileType = file.FileType,
            FileUrl = file.FileUrl,
            FileName = file.FileName,
            Sort = file.Sort
        };
    }
}
