using IRaCIS.Core.Infrastructure.ExpressionExtend;
using IRaCIS.Application.Interfaces;
using IRaCIS.Application.Contracts;
using IRaCIS.Core.Application.Filter;
using Microsoft.AspNetCore.Mvc;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Application.MediatR.CommandAndQueries;
using Magicodes.ExporterAndImporter.Core;
using Magicodes.ExporterAndImporter.Excel;
using Magicodes.ExporterAndImporter.Excel.AspNetCore;
using IRaCIS.Core.Infrastructure;
using Microsoft.AspNetCore.Authorization;
using IRaCIS.Core.Application.Auth;

namespace IRaCIS.Application.Services
{
    [ApiExplorerSettings(GroupName = "Trial")]
    public class VisitPlanService : BaseService, IVisitPlanService
    {
        private readonly IRepository<VisitStage> _visitStageRepository;
        private readonly IRepository<Trial> _trialRepository;
        private readonly IRepository<SubjectVisit> _subjectVisitRepository;
        private readonly IRepository<VisitPlanInfluenceStudy> _influnceRepository;

        public VisitPlanService(IRepository<VisitStage> visitStageRepository, IRepository<Trial> trialRepository, IRepository<SubjectVisit> subjectVisitRepository,
            IRepository<VisitPlanInfluenceStudy> visitPlanInfluenceStudy)
        {
            _visitStageRepository = visitStageRepository;
            _trialRepository = trialRepository;
            _subjectVisitRepository = subjectVisitRepository;
            _influnceRepository = visitPlanInfluenceStudy;
        }


        ///暂时不用
        /// <summary> 获取项目访视计划</summary>
        [HttpPost]
        public async Task<PageOutput<VisitStageDTO>> GetTrialVisitStageList(VisitPlanQueryDTO param)
        {
            var visitStageQuery = _visitStageRepository.AsQueryable(true).Where(u => u.TrialId == param.TrialId)
                  .WhereIf(!string.IsNullOrWhiteSpace(param.Keyword), t => t.VisitName.Contains(param.Keyword))
                  .ProjectTo<VisitStageDTO>(_mapper.ConfigurationProvider);

            return await visitStageQuery.ToPagedListAsync(param.PageIndex, param.PageSize, "CreateTime", param.Asc);


        }

        /// <summary> 根据项目Id，获取项目访视计划(不分页)[New]</summary>
        [HttpGet("{trialId:guid}")]
        public async Task<VisitPlanView> GetVisitStageList(Guid trialId)
        {
            var query = _visitStageRepository.AsQueryable(true).Where(u => u.TrialId == trialId)
                .ProjectTo<VisitStageDTO>(_mapper.ConfigurationProvider).OrderBy(t => t.VisitNum);
            var list = await query.ToListAsync();
            var trial = (await _repository.FirstOrDefaultAsync<Trial>(t => t.Id == trialId)).IfNullThrowException();

            return new VisitPlanView()
            {
                VisitPlanList = list,
                TimePointsPerPatient = trial.TimePointsPerPatient,
                VisitPlanConfirmed = trial.VisitPlanConfirmed,
                IsHaveFirstGiveMedicineDate = trial.IsHaveFirstGiveMedicineDate,
                //SubjectHasAdded = _subjectVisitRepository.Any(t => t.TrialId == trialId)
            };
        }

        /// <summary>
        /// 获取访视计划下拉框列表
        /// </summary>
        /// <param name="trialId"></param>
        /// <returns></returns>
        [HttpGet("{trialId:guid}")]
        public async Task<IEnumerable<VisitStageSelectDTO>> GetTrialVisitStageSelect(Guid trialId)
        {
            var query = _visitStageRepository.Where(u => u.TrialId == trialId)
                .ProjectTo<VisitStageSelectDTO>(_mapper.ConfigurationProvider).OrderBy(t => t.VisitNum);
            var list = await query.ToListAsync();
            return list;
        }


        /// <summary> 添加或更新访视计划某项</summary>
        [UnitOfWork]
        [HttpPost]
        [Authorize(Policy = IRaCISPolicy.PM_APM)]
        public async Task<IResponseOutput> AddOrUpdateVisitStage(VisitPlanCommand visitPlan)
        {

            if (!await _trialRepository.Where(t => t.Id == visitPlan.TrialId).IgnoreQueryFilters().AnyAsync(t => t.TrialStatusStr == StaticData.TrialOngoing || t.TrialStatusStr == StaticData.TrialInitializing))
            {
                throw new BusinessValidationFailedException("只有当项目状态为：初始化或进行中时，可以操作。 ");
            }

            var visitPlanList = await _visitStageRepository.Where(t => t.TrialId == visitPlan.TrialId, ignoreQueryFilters: true)
                .Select(t => new { t.Id, t.VisitNum, t.VisitDay }).OrderBy(t => t.VisitNum).ToListAsync();

            //更新的时候，需要排除自己
            if (visitPlan.Id != null)
            {
                visitPlanList = visitPlanList.Where(t => t.Id != visitPlan.Id).ToList();
            }

            if (visitPlanList.Any(t => t.VisitNum < visitPlan.VisitNum))
            {
                //比当前 visitNum小的 visitDay的最大值    还小 不允许添加
                if (visitPlan.VisitDay <= visitPlanList.Where(t => t.VisitNum < visitPlan.VisitNum).Select(t => t.VisitDay).Max())
                {
                    throw new BusinessValidationFailedException("访视计划中，访视号大的访视，其访视间隔也应该比访视号小的访视大。");

                }
            }

            if (visitPlanList.Any(t => t.VisitNum > visitPlan.VisitNum))
            {
                if (visitPlan.VisitDay >= visitPlanList.Where(t => t.VisitNum > visitPlan.VisitNum).Select(t => t.VisitDay).Min())
                {
                    throw new BusinessValidationFailedException("访视计划中，访视号大的计划访视，其访视间隔也应该比访视号小的计划访视大。");
                }
            }

            if (visitPlan.Id == Guid.Empty || visitPlan.Id == null)//add
            {
                var trial = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == visitPlan.TrialId)).IfNullThrowException();

                trial.VisitPlanConfirmed = false;

                if (await _visitStageRepository.AnyAsync(t => t.TrialId == visitPlan.TrialId && (t.VisitName == visitPlan.VisitName || t.VisitNum == visitPlan.VisitNum), true))
                {
                    throw new BusinessValidationFailedException("访视计划中已经存在具有项目访视名称或者访视号的计划访视模板。");
                }

                if (await _visitStageRepository.AnyAsync(t => t.TrialId == visitPlan.TrialId && t.IsBaseLine, true) && visitPlan.IsBaseLine)
                {
                    throw new BusinessValidationFailedException("访视计划中已经存在基线。");
                }

                //不用前端传递的值
                visitPlan.BlindName = "B" + ((int)visitPlan.VisitNum * 10).ToString("D3");

                var visitPlanItem = await _visitStageRepository.InsertFromDTOAsync(visitPlan, true);

                #region 废弃前

                // var visitPlanItem = _mapper.Map<VisitStage>(visitPlan);

                //visitPlanItem.BlindName = "B" + ((int)visitPlanItem.VisitNum * 10).ToString("D3");

                //var result = await _visitStageRepository.AddAsync(visitPlanItem,true);
                #endregion


                return ResponseOutput.Ok(visitPlanItem.Id);
            }

            else//update
            {


                if (await _visitStageRepository.AnyAsync(t => t.TrialId == visitPlan.TrialId && (t.VisitName == visitPlan.VisitName || t.VisitNum == visitPlan.VisitNum) && t.Id != visitPlan.Id, true))
                {
                    throw new BusinessValidationFailedException("访视计划中已经存在具有项目访视名称或者访视号的计划访视模板。");
                }

                if (await _visitStageRepository.AnyAsync(t => t.TrialId == visitPlan.TrialId && t.IsBaseLine && t.Id != visitPlan.Id, true) && visitPlan.IsBaseLine)
                {
                    throw new BusinessValidationFailedException("访视计划中已经存在基线。");
                }

                #region 废弃前  

                //var stage = (await _visitStageRepository.FirstOrDefaultAsync(t => t.Id == visitPlan.Id, true)).IfNullThrowException();

                ////修改是否是基线
                //if (stage.IsBaseLine && stage.IsBaseLine != visitPlan.IsBaseLine)
                //{
                //    if (await _repository.Where<SubjectVisit>(t => t.TrialId == visitPlan.TrialId).AnyAsync(v => v.IsBaseLine && v.SubmitState >= SubmitStateEnum.ToSubmit))
                //    {
                //        throw new BusinessValidationFailedException("有CRC已经为基线上传了影像数据,不允许修改基线");
                //    }
                //}

                //_mapper.Map(visitPlan, stage);

                //stage.BlindName = "B" + ((int)visitPlan.VisitNum * 10).ToString("D3");

                //await _visitStageRepository.SaveChangesAsync();

                #endregion

                //不用前端传递的值
                visitPlan.BlindName = "B" + ((int)visitPlan.VisitNum * 10).ToString("D3");
                //返回的是数据库查询的数据
                var stage = await _visitStageRepository.UpdateFromDTOAsync(visitPlan, true);

                if (stage.IsBaseLine && stage.IsBaseLine != visitPlan.IsBaseLine)
                {
                    if (await _repository.Where<SubjectVisit>(t => t.TrialId == visitPlan.TrialId).AnyAsync(v => v.IsBaseLine && v.SubmitState >= SubmitStateEnum.ToSubmit))
                    {
                        throw new BusinessValidationFailedException("有受试者的基线已经上传了影像数据,不允许修改基线访视。");
                    }
                }


                return ResponseOutput.Ok();
            }




        }



        [UnitOfWork]
        [HttpPost("{trialId:guid}")]
        [Authorize(Policy = IRaCISPolicy.PM_APM)]
        public async Task<IResponseOutput> ConfirmTrialVisitPlan(Guid trialId)
        {
            if (!await _trialRepository.AnyAsync(t => t.Id == trialId && (t.TrialStatusStr == StaticData.TrialInitializing || t.TrialStatusStr == StaticData.TrialOngoing)))
            {
                return ResponseOutput.NotOk("仅仅在项目初始化或者进行中时，才允许修改确认");
            }

            if (!await _visitStageRepository.AnyAsync(t => t.TrialId == trialId && t.IsBaseLine))
            {
                return ResponseOutput.NotOk("没有基线，不允许确认");
            }

            if (!await _trialRepository.AnyAsync(t => t.Id == trialId && t.IsTrialBasicLogicConfirmed && t.IsTrialProcessConfirmed && t.IsTrialUrgentConfirmed))
            {
                return ResponseOutput.NotOk("项目配置未确认，不允许确认访视计划");
            }

            var svList = await _visitStageRepository.Where(t => t.TrialId == trialId).Select(u => new { u.VisitDay, u.IsBaseLine }).ToListAsync();

            if (svList.Min(t => t.VisitDay) != svList.Where(t => t.IsBaseLine).FirstOrDefault()?.VisitDay)
            {
                return ResponseOutput.NotOk("基线VisitDay 不是最小的， 不允许确认");
            }


            //更新项目访视计划状态为已确认  必定生成更新的sql  通过状态改变 触发操作
            //await _trialRepository.UpdatePartialNowNoQueryAsync(trialId, t => new Trial() { VisitPlanConfirmed = true });


            var trial = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId)).IfNullThrowException();

            trial.VisitPlanConfirmed = true;


            var subjects = _repository.GetQueryable<Subject>().Where(x => x.TrialId == trialId).Select(x => new
            {
                x.Code,
                x.SiteId,
                x.Id,
                x.IsEnrollment,
                x.IsUrgent,
                x.TrialId

            });

            List<SubjectVisit> subjectVisits = new List<SubjectVisit>();

            var addvisitStages = await _visitStageRepository.Where(x => !x.IsHaveFirstConfirmed && x.TrialId == trialId).ToListAsync();

            addvisitStages.ForEach(visitPlan =>
            {
                subjects.ForEach(subject =>
                {
                    var addSv = _mapper.Map<SubjectVisit>(visitPlan);

                    addSv.SubjectId = subject.Id;
                    addSv.TrialId = subject.TrialId;
                    addSv.SiteId = subject.SiteId;
                   
                    subjectVisits.Add(addSv);
                });
            });
            await _subjectVisitRepository.AddRangeAsync(subjectVisits);

            //访视计划 整体状态变更为 确认
            await _visitStageRepository.UpdatePartialFromQueryAsync(u => u.TrialId == trialId && u.IsConfirmed == false, t => new VisitStage() { IsConfirmed = true, IsHaveFirstConfirmed = true });

            await _visitStageRepository.SaveChangesAsync();
            return ResponseOutput.Ok();

        }




        [HttpGet("{trialId:guid}")]
        public async Task<List<VisitPlanInfluenceSubjectVisitStatDTO>> GetInfluenceHistoryList(Guid trialId, [FromServices] IRepository<VisitPlanInfluenceStat> _influnceStatRepository)
        {
            var list = await _influnceStatRepository.Where(t => t.TrialId == trialId).ProjectTo<VisitPlanInfluenceSubjectVisitStatDTO>(_mapper.ConfigurationProvider).OrderByDescending(t => t.CreateTime).ToListAsync();

            return list;
        }

        [HttpGet("{visitPlanInfluenceStatId:guid}")]
        public async Task<IActionResult> DownloadInflunceStudyList(Guid visitPlanInfluenceStatId)
        {
            var list = _influnceRepository.Where(t => t.VisitPlanInfluenceStatId == visitPlanInfluenceStatId)
                .ProjectTo<VisitPlanInfluenceSubjectVisitDTO>(_mapper.ConfigurationProvider).ToList();


            IExporter exporter = new ExcelExporter();

            var result = await exporter.ExportAsByteArray(list);


            return new XlsxFileResult(bytes: result, fileDownloadName: $"检查导出_{DateTime.Now.ToString("yyyy-MM-dd:hh:mm:ss")}.xlsx");


        }




        /// <summary> 删除项目计划某一项 废弃  </summary>
        [HttpDelete("{id:guid}/{trialId:guid}")]
        [TrialAudit(AuditType.TrialAudit, AuditOptType.DeleteTrialVisitPlanItem)]
        [TypeFilter(typeof(TrialResourceFilter))]
        [Obsolete]
        public async Task<IResponseOutput> DeleteVisitStage(Guid id)
        {

            var visitPlan = await _visitStageRepository.FirstOrDefaultAsync(t => t.Id == id);

            if (visitPlan == null) return Null404NotFound(visitPlan);

            if (await _repository.AnyAsync<SubjectVisit>(t => t.VisitName == visitPlan.VisitName && t.TrialId == visitPlan.TrialId && t.VisitExecuted == VisitExecutedEnum.Executed))
            {
                return ResponseOutput.NotOk("The visit plan has been assigned to the subjects and executed.");
            }

            await _repository.BatchDeleteAsync<SubjectVisit>(t => t.TrialId == visitPlan.TrialId && t.VisitName == visitPlan.VisitName);

            var result = await _visitStageRepository.BatchDeleteNoTrackingAsync(u => u.Id == id);

            return ResponseOutput.Result(result);
        }


    }
}