using Furion.DatabaseAccessor.Extensions;
using Furion.JsonSerialization;
using YBEE.EQM.Core;

namespace YBEE.EQM.Application;

/// <summary>
/// 监测机构上报类型服务
/// </summary>
public class ExamOrgDataReportService : IExamOrgDataReportService, ITransient
{
    private readonly IRepository<ExamOrgDataReport> _rep;
    private readonly IResourceFileService _resourceFileService;
    private readonly IExamSpecialStudentService _examSpecialStudentService;
    private readonly IExamAbsentReplaceService _examAbsentReplaceService;

    public ExamOrgDataReportService(IRepository<ExamOrgDataReport> rep, IResourceFileService resourceFileService, IExamSpecialStudentService examSpecialStudentService, IExamAbsentReplaceService examAbsentReplaceService)
    {
        _rep = rep;
        _resourceFileService = resourceFileService;
        _examSpecialStudentService = examSpecialStudentService;
        _examAbsentReplaceService = examAbsentReplaceService;
    }

    /// <summary>
    /// 提交上报
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task Submit(SubmitExamOrgDataReportInput input)
    {
        var orgId = CurrentSysUserInfo.SysOrgId;

        var examOrg = await _rep.Change<ExamOrg>().DetachedEntities
                                                  .Include(t => t.ExamPlan).ThenInclude(t => t.ExamDataReports)
                                                  .FirstOrDefaultAsync(t => t.SysOrgId == orgId && t.ExamPlanId == input.ExamPlanId);
        var edr = examOrg.ExamPlan.ExamDataReports.FirstOrDefault(t => t.Type == input.Type) ?? throw Oops.Oh(ErrorCode.E2001);
        if (edr.Status != ExamStatus.ACTIVE)
        {
            throw Oops.Oh(ErrorCode.E2006);
        }
        if (edr.EndTime < DateTime.Now)
        {
            throw Oops.Oh(ErrorCode.E2008);
        }

        if (input.Type == DataReportType.SP_STUDENT)
        {
            var vspSuccess = await _examSpecialStudentService.VerifyAttachment(input.ExamPlanId);
            if (vspSuccess == false)
            {
                throw Oops.Oh(ErrorCode.E1012, "特殊学生明细");
            }
        }
        else if(input.Type == DataReportType.ABSENT_REPLACE)
        {
            var vspSuccess = await _examAbsentReplaceService.VerifyAttachment(input.ExamPlanId);
            if (vspSuccess == false)
            {
                throw Oops.Oh(ErrorCode.E1012, "缺测替补学生明细");
            }
        }

        var item = await _rep.FirstOrDefaultAsync(t => t.ExamOrg.SysOrgId == CurrentSysUserInfo.SysOrgId && t.ExamOrg.ExamPlanId == input.ExamPlanId && t.Type == input.Type);
        if (item == null)
        {
            item = new ExamOrgDataReport()
            {
                Type = input.Type,
                Status = DataReportStatus.REPORTED,
                ReportSysUserId = CurrentSysUserInfo.SysUserId,
                ReportTime = DateTime.Now,
                ExamOrgId = examOrg.Id,
                ExamPlanId = input.ExamPlanId,
                SysOrgId = orgId,
            };
            await _rep.InsertNowAsync(item);
        }
        else
        {
            item.Status = DataReportStatus.REPORTED;
            item.ReportSysUserId = CurrentSysUserInfo.SysUserId;
            item.ReportTime = DateTime.Now;
            await item.UpdateIncludeNowAsync(new[] { nameof(item.Status), nameof(item.ReportSysUserId), nameof(item.ReportTime) });
        }
    }
    /// <summary>
    /// 添加特殊学生佐证材料
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task AddAttachment(AddExamOrgDataReportAttachmentInput input)
    {
        var orgId = CurrentSysUserInfo.SysOrgId;

        var item = await _rep.FirstOrDefaultAsync(t => t.Id == input.SourceId);
        bool hasItem = true;
        if (item == null)
        {
            hasItem = false;
            var examOrg = await _rep.Change<ExamOrg>().DetachedEntities.FirstOrDefaultAsync(t => t.SysOrgId == orgId && t.ExamPlanId == input.ExamPlanId) ?? throw Oops.Oh(ErrorCode.E2001, "监测机构");
            item = new()
            {
                Type = input.Type,
                Status = DataReportStatus.UNREPORT,
                ExamOrgId = examOrg.Id,
                ExamPlanId = input.ExamPlanId,
                SysOrgId = orgId,
            };
        }
        item.Attachments = AttachmentUtil.InsertInto(item.Attachments, input.Adapt<AttachmentItem>());

        if (hasItem)
        {
            await item.UpdateIncludeAsync(new[] { nameof(item.Attachments) });
        }
        else
        {
            await _rep.InsertNowAsync(item);
        }
    }
    /// <summary>
    /// 删除特殊学生佐证材料
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task DelAttachment(DeleteAttachmentInput input)
    {
        var item = await _rep.FirstOrDefaultAsync(t => t.Id == input.SourceId) ?? throw Oops.Oh(ErrorCode.E2001);
        var attachments = AttachmentUtil.GetList(item.Attachments);
        var a = attachments.FirstOrDefault(t => t.FileId == input.FileId);
        if (a != null)
        {
            attachments.Remove(a);
            item.Attachments = JSON.Serialize(attachments);
            await item.UpdateIncludeAsync(new[] { nameof(item.Attachments) });

            await _resourceFileService.Del(new() { Id = a.FileId });
            if (a.ThumbFileId.HasValue && a.ThumbFileId > 0)
            {
                await _resourceFileService.Del(new() { Id = a.ThumbFileId.Value });
            }
        }
    }
    /// <summary>
    /// 退回机构上报
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task ReportReject(BaseId input)
    {
        var item = await _rep.FirstOrDefaultAsync(t => t.Id == input.Id);
        item.Status = DataReportStatus.REJECTED;
        await item.UpdateIncludeAsync(new[] { nameof(item.Status) });
    }

    /// <summary>
    /// 获取监测机构上报类型列表
    /// </summary>
    /// <param name="examPlanId"></param>
    /// <param name="sysOrgId">不传取当前机构ID</param>
    /// <returns></returns>
    public async Task<List<ExamOrgDataReportUnionOutput>> GetListByExamPlanId(int examPlanId, short? sysOrgId = null)
    {
        var orgId = sysOrgId ?? CurrentSysUserInfo.SysOrgId;

        var pitems = await _rep.Change<ExamDataReport>().DetachedEntities.Where(t => t.ExamPlanId == examPlanId).ToListAsync();
        var oitems = await _rep.DetachedEntities.Include(t => t.ExamOrg).ThenInclude(t => t.SysOrg)
                                                .Include(t => t.ReportSysUser)
                                                .Where(t => t.ExamOrg.SysOrgId == orgId && t.ExamOrg.ExamPlanId == examPlanId)
                                                .ToListAsync();
        var items = from p in pitems
                    join o in oitems on p.Type equals o.Type into temp
                    from u in temp.DefaultIfEmpty()
                    select new ExamOrgDataReportUnionOutput
                    {
                        ExamDataReportId = p.Id,
                        ExamDataReport = p.Adapt<ExamDataReportOutput>(),
                        ExamOrgDataReportId = u?.Id ?? 0,
                        ExamOrgDataReport = u == null ? new() : u.Adapt<ExamOrgDataReportOutput>(),
                        SysOrgId = orgId,
                        IsExpired = p.EndTime < DateTime.Now,
                    };
        return items.ToList();
    }
    /// <summary>
    /// 根据监测计划ID和上报类型获取机构上报信息
    /// </summary>
    /// <param name="type"></param>
    /// <param name="examPlanId"></param>
    /// <param name="sysOrgId"></param>
    /// <returns></returns>
    public async Task<ExamOrgDataReportUnionOutput> GetByTypeExamPlanId(DataReportType type, int examPlanId, short? sysOrgId = null)
    {
        var items = await GetListByExamPlanId(examPlanId, sysOrgId);
        var item = items.FirstOrDefault(t => t.ExamDataReport.Type == type) ?? throw Oops.Oh(ErrorCode.E2001);
        item.ExamPlan = (await _rep.Change<ExamPlan>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == examPlanId))?.Adapt<ExamPlanOutput>();
        return item;
    }
    /// <summary>
    /// 分页查询机构上报类型列表
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<PageResult<ExamPlanOrgDataReportOutput>> QueryPageList(ExamOrgDataReportPageInput input)
    {
        string where = "T2.type = @type AND T1.sys_org_id = @sysOrgId AND (T4.status = 2 OR T4.status = 3) AND T4.is_deleted = 0";
        if (input.Status.HasValue)
        {
            where = $"{where} AND T3.status = {(short)input.Status.Value}";
        }
        if (input.SemesterId.HasValue)
        {
            where = $"{where} AND T4.semester_id = {(short)input.SemesterId.Value}";
        }
        if (!string.IsNullOrEmpty(input.Name?.Trim()))
        {
            where = $"{where} AND T4.full_name LIKE '%{input.Name.Trim()}%'";
        }

        var p = new
        {
            CurrentSysUserInfo.SysOrgId,
            input.PageSize,
            PageOffset = (input.PageIndex - 1) * input.PageSize,
            input.Type,
            input.Status,
            input.Name,
            input.SemesterId,
        };

        var totalCount = await _rep.SqlScalarAsync<int>($@"
                            SELECT COUNT(1) AS total_count
                            FROM exam_org AS T1
                            JOIN exam_data_report AS T2 ON T1.exam_plan_id = T2.exam_plan_id
                            INNER JOIN exam_plan AS T4 ON T1.exam_plan_id = T4.id
                            LEFT JOIN exam_org_data_report AS T3 ON T1.id = T3.exam_org_id AND T2.type = T3.type
                            WHERE {where}", p);

        var items = await _rep.SqlQueriesAsync<ExamPlanOrgDataReportOutput>($@"
                            SELECT ROW_NUMBER() OVER (ORDER BY T1.exam_plan_id DESC, T2.type) AS `row_number`, 
	                            T1.exam_plan_id, T1.sys_org_id, T2.type, T2.begin_time, T2.end_time, T2.`status` AS exam_status, 
	                            IFNULL(T3.`status`, 1) AS `status`, T3.report_sys_user_id, T5.`name` AS report_sys_user_name, T3.report_time,
	                            T4.full_name AS exam_plan_full_name, T4.`name` AS exam_plan_name, T4.short_name AS exam_plan_short_name,
	                            T4.`status` AS exam_plan_status, T4.education_stage, T4.semester_id
                            FROM exam_org AS T1
                            JOIN exam_data_report AS T2 ON T1.exam_plan_id = T2.exam_plan_id
                            JOIN exam_plan AS T4 ON T1.exam_plan_id = T4.id
                            LEFT JOIN exam_org_data_report AS T3 ON T1.id = T3.exam_org_id AND T2.type = T3.type
                            LEFT JOIN sys_user AS T5 ON T3.report_sys_user_id = T5.id
                            WHERE {where}
                            LIMIT @pageSize OFFSET @pageOffset;", p);
        PageResult<ExamPlanOrgDataReportOutput> ret = new()
        {
            PageIndex = input.PageIndex,
            PageSize = input.PageSize,
            TotalCount = totalCount,
            Items = items
        };
        return ret;
    }

    public async Task RefreshFileSize()
    {
        var items = await _rep.Entities.ToListAsync();
        foreach (var item in items)
        {
            var ats = AttachmentUtil.GetList(item.Attachments);
            foreach (var at in ats)
            {
                var file = await _rep.Change<ResourceFile>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == at.FileId) ?? throw new Exception("fuckfuck");
                at.FileSize = file.FileSize;
            }
            item.Attachments = JSON.Serialize(ats);
        }
        await _rep.UpdateAsync(items);
    }
}
