using Mapster;
using Microsoft.Extensions.Logging;
using QYZH.InteractiveMagazine.Common.Extensions;
using QYZH.InteractiveMagazine.Common.Helpers;
using QYZH.InteractiveMagazine.Infrastructure.OSS;
using QYZH.InteractiveMagazine.Infrastructure.RabbitMQ;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Dto.DotMatrix;
using QYZH.InteractiveMagazine.Models.Dto.Journal;
using QYZH.InteractiveMagazine.Models.Entity;
using QYZH.InteractiveMagazine.Models.Enum;
using QYZH.InteractiveMagazine.Repository;
using SqlSugar;
using Yitter.IdGenerator;

namespace QYZH.InteractiveMagazine.Service;

public class JournalService(BaseRepository<JournalPage> JournalPageRepository,
    BaseRepository<JournalPageTask> JournalPageTaskRepository,
    BaseRepository<JournalPageTaskAnswer> JournalPageTaskAnswerRepository,
    BaseRepository<JournalCatalog> JournalCatalogRepository,
    BaseRepository<DotFile> dotFileRepository,
    BaseRepository<DotFileDetail> dotFileDetailRepository,
    OssService ossService,
    IRabbitMQService rabbitMqService,
    ILogger<JournalService> logger) : BaseRepository<Journal>, IJournalService
{
    private const string JournalExchange = "ex.journal";
    private const string PublishBookQueue = "mq.journal.publish.book";
    private const string PublishBookRoutingKey = "rk.journal.publish.book";
    private const string PublishBookPageQueue = "mq.journal.publish.bookpage";
    private const string PublishBookPageRoutingKey = "rk.journal.publish.bookpage";

    /// <summary>
    /// 查询List
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    public async Task<List<JournalDto>> GetListAsync(JournalQueryDto dto)
    {
        var query = await Queryable()
               .WhereIF(!string.IsNullOrWhiteSpace(dto.Name), a => a.Name.Contains(dto.Name))
               .WhereIF(dto.Status.HasValue, a => a.Status == (int)dto.Status)
               .Select(a => new JournalDto(), true)
               .ToListAsync();

        return query;
    }

    /// <summary>
    /// 分页查询
    /// </summary>
    /// <param name="search"></param>
    /// <returns></returns>
    public async Task<PageListModel<JournalDto>> GetPageListAsync(PageQueryModel<JournalQueryDto> search)
    {
        RefAsync<int> totalNumber = 0;

        var dataList = await Queryable()
               //.InnerJoin<IcrJournalOrganization>((a, b) => a.Id == b.JournalId)
               .WhereIF(!string.IsNullOrWhiteSpace(search.Params.Name), a => a.Name.Contains(search.Params.Name))
               .WhereIF(search.Params.Status.HasValue, a => a.Status == (int)search.Params.Status)
               .WhereIF(!string.IsNullOrWhiteSpace(search.Params.Name), a => a.Name.Contains(search.Params.Name))
               .OrderByDescending(a => a.CreatedAt)
               .Select(a => new JournalDto(), true)

               .ToPageListAsync(search.PageIndex, search.PageSize, totalNumber);

        return new PageListModel<JournalDto>(dataList, search.PageIndex, search.PageSize, totalNumber);
    }

    /// <summary>
    /// 创建杂志
    /// </summary>
    /// <param name="input">杂志信息</param>
    /// <returns>新杂志ID</returns>
    public async Task<BaseResponse<long>> AddAsync(JournalAddDto input)
    {
        BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.Name), "书籍名称不能为空", ResultCode.BAD_REQUEST);

        var journal = new Journal
        {
            Name = input.Name.Trim(),
            Title = input.Title,
            TotalPage = input.TotalPage,
            Status = (int)JournalStatusEnum.Created,
            PdfUrl = input.PdfUrl?.RemoveDomain(),
            Width = input.Width,
            Height = input.Height,
            Cover = input.Cover?.RemoveDomain(),
            BackCover = input.BackCover?.RemoveDomain(),
            PdfPreviewUrl = input.PdfPreviewUrl?.RemoveDomain(),
            StartTime = input.StartTime,
            EndTime = input.EndTime,
            CreatedBy = "System",
            UpdatedBy = "System",
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now,
            IsDeleted = false
        };

        // Id 已由 YitIdHelper 预生成，先搬运文件到正式目录，再一次性插入
        if (!string.IsNullOrWhiteSpace(input.Cover))
        {
            var coverKey = $"journal/{journal.Id}/cover.{input.Cover.ToExtension()}";
            ossService.CopyObject(input.Cover.RemoveDomain(), coverKey);
            journal.Cover = coverKey;
        }

        if (!string.IsNullOrWhiteSpace(input.BackCover))
        {
            var backCoverKey = $"journal/{journal.Id}/backCover.{input.BackCover.ToExtension()}";
            ossService.CopyObject(input.BackCover.RemoveDomain(), backCoverKey);
            journal.BackCover = backCoverKey;
        }

        if (!string.IsNullOrWhiteSpace(input.PdfUrl))
        {
            var pdfKey = $"journal/{journal.Id}/Journal.{input.PdfUrl.ToExtension()}";
            ossService.CopyObject(input.PdfUrl.RemoveDomain(), pdfKey);
            journal.PdfUrl = pdfKey;
        }

        var res = await base.InsertAsync(journal);
        if (!res)
        {
            new BusinessException("创建失败", ResultCode.GLOBAL_ERROR);
        }
        logger.LogInformation("杂志创建成功，ID: {Id}, 名称: {Name}", journal.Id, input.Name);
        return BaseResponse<long>.Success(journal.Id);
    }

    /// <summary>
    /// 编辑
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<BaseResponse<bool>> EditAsync(JournalEditDto input)
    {

        var Journal = await base.GetByIdAsync(input.Id);
        BusinessException.ThrowIf(Journal.IsNull(), "不存在的Id", ResultCode.NOT_FOUND);
        BusinessException.ThrowIf(Journal.Status == (int)JournalStatusEnum.Archive, "已归档不可编辑", ResultCode.CONFLICT);
        if (string.IsNullOrWhiteSpace(input.Cover))
        {
            Journal.Cover = null;
        }
        else if (input.Cover.RemoveDomain() != Journal.Cover)
        {
            var key = $"journal/{Journal.Id}/cover.{input.Cover.ToExtension()}";
            ossService.CopyObject(input.Cover.RemoveDomain(), key);
            Journal.Cover = key;
        }

        if (string.IsNullOrWhiteSpace(input.BackCover))
        {
            Journal.BackCover = null;
        }
        else if (input.BackCover.RemoveDomain() != Journal.BackCover)
        {
            var backCoverkey = $"journal/{Journal.Id}/backCover.{input.BackCover.ToExtension()}";
            ossService.CopyObject(input.BackCover.RemoveDomain(), backCoverkey);
            Journal.BackCover = backCoverkey;
        }

        if (string.IsNullOrWhiteSpace(input.PdfUrl))
        {
            Journal.PdfUrl = null;
        }
        else if (input.PdfUrl.RemoveDomain() != Journal.PdfUrl)
        {
            var pdfUrlkey = $"journal/{Journal.Id}/Journal.{input.PdfUrl.ToExtension()}";
            ossService.CopyObject(input.PdfUrl.RemoveDomain(), pdfUrlkey);
            Journal.PdfUrl = pdfUrlkey;
        }
        Journal.Width = input.Width;
        Journal.Height = input.Height;
        Journal.Name = input.Name;
        Journal.Title = input.Title;
        Journal.StartTime = input.StartTime;
        Journal.EndTime = input.EndTime;
        Journal.UpdatedAt = DateTime.Now;

        var res = await UseTranAsync(async () =>
        {
            var result = await Context.Updateable(Journal).IgnoreColumns(c => c.Status).ExecuteCommandAsync();
            return result > 0;
        });



        return BaseResponse<bool>.Success(res);
    }


    public async Task<List<JournalTaskOutput>> Tasks(long id)
    {
        var data = await JournalPageTaskRepository.Queryable().Where(w => w.JournalId == id)
           .Select(x => new JournalTaskOutput()
           {
               TaskId = x.Id,
               TaskNo = x.No,
               TaskSubType = x.Type
           }).ToListAsync();
        return data.OrderBy(x =>
        {
            var parts = x.TaskNo.Split('-').Select(int.Parse).ToArray();
            return (parts[0], parts[1], parts[2], parts[3]); // 元组
        }).ToList();
    }

    public async Task<JournalDto> DetailAsync(long id)
    {
        var Journal = await base.Queryable().Where(w => w.Id == id).Select<JournalDto>().FirstAsync();
        return Journal;
    }

    public async Task<bool> DeleteAsync(List<long> ids)
    {
        BusinessException.ThrowIf(!base.Queryable().Any(w => ids.Contains(w.Id)), "ID不存在", ResultCode.NOT_FOUND);
        BusinessException.ThrowIf(base.Queryable().Any(w => ids.Contains(w.Id) && w.Status == (int)JournalStatusEnum.Archive), "已归档不可删除", ResultCode.CONFLICT);
        var result = await UseTranAsync(async () =>
        {
            await base.DeleteAsync(d => ids.Contains(d.Id));
            await JournalPageRepository.DeleteAsync(d => ids.Contains(d.JournalId));
            await JournalCatalogRepository.DeleteAsync(d => ids.Contains(d.JournalId));

            // 先查询要删除的 JournalPageTask Id 列表
            var taskIds = await JournalPageTaskRepository.Queryable()
                .Where(d => ids.Contains(d.JournalId))
                .Select(d => d.Id)
                .ToListAsync();

            // 删除 JournalPageTaskAnswer
            if (taskIds.Any())
            {
                await JournalPageTaskAnswerRepository.DeleteAsync(d => taskIds.Contains(d.JournalPageTaskId));
            }

            // 再删除 JournalPageTask
            await JournalPageTaskRepository.DeleteAsync(d => ids.Contains(d.JournalId));
            return true;
        });

        //删除OSS上的journal/{JournalId}文件夹
        if (result)
        {
            foreach (var JournalId in ids)
            {
                var prefix = $"journal/{JournalId}/";
                var keys = ossService.ListObjects(prefix);
                if (keys?.Count > 0)
                {
                    ossService.DeleteObjects(keys);
                }
            }
        }
        return result;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="id">书id</param>
    /// <param name="index">起始页码</param>
    /// <returns></returns>
    public async Task<bool> StartPageAsync(long id, int index)
    {
        var oldIndex = await JournalPageRepository.Queryable().Where(w => w.JournalId == id && w.PageNum == 1).Select(x => x.Sort).FirstAsync();

        // 如果位置没有变化，直接返回
        if (index == oldIndex) return true;

        if (index > oldIndex)
        {
            // 向后移动的情况
            var x = index - oldIndex;
            await JournalPageRepository.Updateable().SetColumns(s => s.PageNum, 0).Where(w => w.Sort < index).Where(w => w.JournalId == id).ExecuteCommandAsync();
            await JournalPageRepository.Updateable().SetColumns(s => s.PageNum == s.PageNum - x).Where(w => w.Sort >= index).Where(w => w.JournalId == id).ExecuteCommandAsync();
        }
        else
        {
            // 向前移动的情况
            //var x = oldIndex - input.Index;
            await JournalPageRepository.Updateable().SetColumns(s => s.PageNum, 0).Where(w => w.Sort < index).Where(w => w.JournalId == id).ExecuteCommandAsync();

            // 将目标页面设置为第一页
            await JournalPageRepository.Updateable().SetColumns(s => s.PageNum == s.Sort - (index - 1)).Where(w => w.Sort >= index).Where(w => w.JournalId == id).ExecuteCommandAsync();
        }

        // await JournalPageRepository.Updateable().SetColumns(s => s.PageNum == s.Sort).Where(w => w.JournalId == input.Id).ExecuteCommandAsync();
        return true;
    }

    public async Task<DotMatrixOutput> PrintCodeAsync(long id)
    {
        var Journal = await base.Queryable().Where(w => w.Id == id).FirstAsync();
        BusinessException.ThrowIf(Journal.IsNull(), "不存在书", ResultCode.NOT_FOUND);
        BusinessException.ThrowIf(Journal.Status == (int)JournalStatusEnum.Codeing, "正在生成中...", ResultCode.CONFLICT);

        var pages = await JournalPageRepository.Queryable().Where(w => w.JournalId == id).ToListAsync();
        BusinessException.ThrowIf(pages.IsNull(), "不存在页", ResultCode.NOT_FOUND);

        var output = new DotMatrixOutput()
        {
            Method = "Journal",
            Name = Journal.Name,
            NoteId = id,
            PdfUrl = Journal.PdfUrl
        };

        return output;
    }

    public async Task<bool> ResultReportAsync(DotMatrixNoteJournalReportInput input)
    {
        if (input.FileUrl.IsNull() && input.FileKey.IsNull())
        {
            return await Updateable().SetColumns(s => s.Status, input.Success ? JournalStatusEnum.CodeSuccess : JournalStatusEnum.CodeFail)
          .SetColumns(s => s.PdfPreviewUrl, input.FileKey)
          .Where(w => w.Id == input.Id)
          .ExecuteCommandAsync() > 0;
        }
        return await Updateable().SetColumns(s => s.Status, JournalStatusEnum.CodeFail).Where(w => w.Id == input.Id).ExecuteCommandAsync() > 0;
    }

    public async Task<bool> StatusAsync(long id, JournalStatusEnum status)
    {
        var book = await base.GetByIdAsync(id);
        var tasks = await Context.Queryable<JournalPageTask>().Where(w => w.JournalId == id).ToListAsync();
        BusinessException.ThrowIf(tasks.Count == 0 && status == JournalStatusEnum.Archive, "未添加任何题目，无法归档", ResultCode.UNPROCESSABLE_ENTITY);
        BusinessException.ThrowIf(tasks.Any(a => string.IsNullOrWhiteSpace(a.TaskUrl)) && status == JournalStatusEnum.Archive, $"{string.Join(',', tasks.Where(a => string.IsNullOrWhiteSpace(a.TaskUrl)).Select(a => a.No).ToList())}未保存，无法归档", ResultCode.UNPROCESSABLE_ENTITY);
        var res = await base.Updateable().SetColumns(s => s.Status, status).Where(w => w.Id == id).ExecuteCommandAsync() > 0;
        return res;
    }

    public async Task<bool> PublishAsync(long id)
    {
        var book = await base.GetByIdAsync(id);
        BusinessException.ThrowIf(book == null || book.IsDeleted, "杂志不存在", ResultCode.NOT_FOUND);

        var pages = await JournalPageRepository.Queryable()
            .Where(x => x.JournalId == id && !x.IsDeleted)
            .OrderBy(x => x.PageNum)
            .OrderBy(x => x.Sort)
            .Select(x => new
            {
                x.Id,
                x.PageNo,
                x.Layout,
                x.Url
            })
            .ToListAsync();
        BusinessException.ThrowIf(pages.Count == 0, "书籍未添加任何书页，无法发布", ResultCode.UNPROCESSABLE_ENTITY);

        BusinessException.ThrowIf(!book.StartTime.HasValue, "发布开始时间不能为空", ResultCode.UNPROCESSABLE_ENTITY);
        BusinessException.ThrowIf(!book.EndTime.HasValue, "发布结束时间不能为空", ResultCode.UNPROCESSABLE_ENTITY);
        BusinessException.ThrowIf(book.EndTime < book.StartTime, "发布结束时间不能早于开始时间", ResultCode.UNPROCESSABLE_ENTITY);

        var pageIds = pages.Select(x => x.Id).ToList();
        var pageQuestions = await JournalPageTaskRepository.Queryable()
            .Where(x => x.JournalId == id && pageIds.Contains(x.JournalPageId) && !x.IsDeleted)
            .Select(x => new
            {
                x.JournalPageId,
                x.Id,
                x.No
            })
            .ToListAsync();
        var questionIdsByPageId = pageQuestions
            .GroupBy(x => x.JournalPageId)
            .ToDictionary(
                x => x.Key,
                x => string.Join(',', x.OrderBy(q => ParseTaskNo(q.No)).Select(q => q.Id)));

        var bookMessageSent = await rabbitMqService.SendAsync(new RabbitMQSendParam
        {
            Exchange = JournalExchange,
            Queue = PublishBookQueue,
            RoutingKey = PublishBookRoutingKey,
            Data = new JournalPublishBookMessage
            {
                BookId = id,
                StartTime = book.StartTime.Value,
                EndTime = book.EndTime.Value
            }
        });
        BusinessException.ThrowIf(!bookMessageSent, "发布书籍消息发送失败", ResultCode.GLOBAL_ERROR);

        foreach (var page in pages)
        {
            var pageMessageSent = await rabbitMqService.SendAsync(new RabbitMQSendParam
            {
                Exchange = JournalExchange,
                Queue = PublishBookPageQueue,
                RoutingKey = PublishBookPageRoutingKey,
                Data = new JournalPublishBookPageMessage
                {
                    BookId = id,
                    PageId = page.Id,
                    PageNo = page.PageNo,
                    Layout = page.Layout,
                    Url = DomainHelper.OssFullUrl(page.Url),
                    QuestionNo = questionIdsByPageId.GetValueOrDefault(page.Id) ?? string.Empty
                }
            });
            BusinessException.ThrowIf(!pageMessageSent, $"发布书页消息发送失败，PageId: {page.Id}", ResultCode.GLOBAL_ERROR);
        }

        return await base.Updateable()
            .SetColumns(s => s.Status, JournalStatusEnum.Published)
            .SetColumns(s => s.UpdatedAt, DateTime.Now)
            .Where(w => w.Id == id)
            .ExecuteCommandAsync() > 0;

        static (int First, int Second, int Third, int Fourth) ParseTaskNo(string? no)
        {
            var parts = no?.Split('-') ?? Array.Empty<string>();
            return (GetNoPart(parts, 0), GetNoPart(parts, 1), GetNoPart(parts, 2), GetNoPart(parts, 3));
        }

        static int GetNoPart(string[] parts, int index)
        {
            return parts.Length > index && int.TryParse(parts[index], out var value) ? value : int.MaxValue;
        }
    }
}
