using IRaCIS.Core.Application.Filter;

using Microsoft.AspNetCore.Mvc;
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
using Microsoft.AspNetCore.Authorization;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Interfaces;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Application.Auth;
using IRaCIS.Core.Infra.EFCore.Common;
using MassTransit;
using IRaCIS.Core.Domain.Models;

namespace IRaCIS.Core.Application.Services
{
    [ApiExplorerSettings(GroupName = "Trial")]
    public class SubjectVisitService : BaseService, ISubjectVisitService
    {
        private readonly IRepository<SubjectVisit> _subjectVisitRepository;
        private readonly IRepository<ClinicalDataTrialSet> _clinicalDataTrialSetRepository;
        private readonly IRepository<ReadingClinicalData> _readingClinicalDataRepository;
        private readonly IRepository<ReadModule> _readModuleRepository;
        private readonly IRepository<Trial> _trialRepository;
        private readonly IRepository<ReadingPeriodSet> _readingPeriodSetRepository;
        private readonly IRepository<NoneDicomStudy> _noneDicomStudyRepository;
        private readonly IRepository<DicomInstance> _dicomInstanceRepository;
        private readonly IRepository<VisitTask> _visitTaskRepository;
        private readonly IRepository<ReadingTableAnswerRowInfo> _readingTableAnswerRowInfoRepository;
        private readonly IRepository<NoneDicomStudyFile> _noneDicomStudyFileRepository;
        private readonly IRepository<ReadingPeriodPlan> _readingPeriodPlanRepository;
        private readonly IRepository<Subject> _subjectRepository;

        private readonly IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository;

        public SubjectVisitService(IRepository<SubjectVisit> subjectVisitRepository,
              IRepository<ClinicalDataTrialSet> clinicalDataTrialSetRepository,
            IRepository<ReadingClinicalData> readingClinicalDataRepository,
            IRepository<ReadModule> readModuleRepository,
             IRepository<Trial> trialRepository,
             IRepository<ReadingPeriodSet> readingPeriodSetRepository,
              IRepository<NoneDicomStudy> noneDicomStudyRepository,
                 IRepository<DicomInstance> dicomInstanceRepository,
                IRepository<VisitTask> visitTaskRepository,
                   IRepository<ReadingTableAnswerRowInfo> readingTableAnswerRowInfoRepository,
              IRepository<NoneDicomStudyFile> noneDicomStudyFileRepository,
              IRepository<ReadingPeriodPlan> readingPeriodPlanRepository,
            IRepository<Subject> subjectRepository,
            IRepository<ReadingQuestionCriterionTrial> trialReadingCriterionRepository
           
            )
        {
            _subjectVisitRepository = subjectVisitRepository;
            this._clinicalDataTrialSetRepository = clinicalDataTrialSetRepository;
            this._readingClinicalDataRepository = readingClinicalDataRepository;
            this._readModuleRepository = readModuleRepository;
            this._trialRepository = trialRepository;
            this._readingPeriodSetRepository = readingPeriodSetRepository;
            this._noneDicomStudyRepository = noneDicomStudyRepository;
            this._dicomInstanceRepository = dicomInstanceRepository;
            this._visitTaskRepository = visitTaskRepository;
            this._readingTableAnswerRowInfoRepository = readingTableAnswerRowInfoRepository;
            this._noneDicomStudyFileRepository = noneDicomStudyFileRepository;
            this._readingPeriodPlanRepository = readingPeriodPlanRepository;
            _subjectRepository = subjectRepository;
            _trialReadingCriterionRepository = trialReadingCriterionRepository;


        }

        [HttpPost]
        [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
        [UnitOfWork]
        //[Authorize(Policy = IRaCISPolicy.CRC)]
        public async Task<IResponseOutput<string>> AddOrUpdateSV(SubjectVisitCommand svCommand)
        {

            var verifyExp1 = new EntityVerifyExp<SubjectVisit>()
            {
                VerifyExp = t => t.VisitNum == svCommand.VisitNum && t.SubjectId == svCommand.SubjectId,
                //---该受试者的访视计划中已经包含一个具有相同访视号的访视。
                VerifyMsg = _localizer["Visit_DuplicateVisitNo"]
            };

            var verifyExp2 = new EntityVerifyExp<SubjectVisit>()
            {
                VerifyExp = t => t.SubjectId == svCommand.SubjectId && t.IsFinalVisit,
                //---该受试者已经有访视设置为末次访视，不允许将当前访视设置为末次访视。
                VerifyMsg = _localizer["Visit_LastVisitNoChange"],
                IsVerify = svCommand.IsFinalVisit
            };

            var verifyExp3 = new EntityVerifyExp<SubjectVisit>()
            {
                VerifyExp = t => t.SubjectId == svCommand.SubjectId && t.VisitName == svCommand.VisitName,
                //---该受试者的访视计划中已经包含一个具有相同访视名称的访视。
                VerifyMsg = _localizer["Visit_DuplicateVisitName"]

            };

            var triconfig = await _trialRepository.Where(t => t.Id == svCommand.TrialId).Select(u => new { u.IsEnrollementQualificationConfirm, u.IsPDProgressView }).FirstOrDefaultAsync();

            svCommand.BlindName = "B" + ((int)(svCommand.VisitNum * 10)).ToString("D3");
            svCommand.VisitExecuted = svCommand.IsLostVisit ? VisitExecutedEnum.Executed : svCommand.VisitExecuted;


          

            SubjectVisit? dbBeforeEntity = null;
            //Add
            if (svCommand.Id == null)
            {

                //设置末次评估后，不允许添加计划外访视
                if (svCommand.InPlan == false)
                {
                    if (await _subjectVisitRepository.AnyAsync(t => t.SubjectId == svCommand.SubjectId && t.IsFinalVisit))
                    {
                //---设置末次评估后，不允许添加计划外访视。
                        throw new BusinessValidationFailedException(_localizer["Visit_NoExtraVisitAfterLast"]);
                    }

                    if (await _repository.AnyAsync<VisitTask>(t => t.SubjectId == svCommand.SubjectId && t.TaskState == TaskState.Effect && t.VisitTaskNum > svCommand.VisitNum && t.SignTime != null && t.TrialReadingCriterion.IsReadingTaskViewInOrder))
                    {
                //---该受试者后续访视已有任务完成阅片(有序阅片标准)，不允许在此添加，如果确实需要，请回退
                        throw new BusinessValidationFailedException(_localizer["Visit_FinishedTasksNoAdd"]);
                    }

                }

                svCommand.PDState = svCommand.IsBaseLine == false && triconfig.IsPDProgressView ? PDStateEnum.PDProgress : PDStateEnum.None;

                dbBeforeEntity = await _subjectVisitRepository.InsertFromDTOAsync(svCommand, false, verifyExp1, verifyExp2, verifyExp3);

            }

            else
            {
                dbBeforeEntity = await _subjectVisitRepository.UpdateFromDTOAsync(svCommand, false, false, verifyExp1, verifyExp2, verifyExp3);


                if (svCommand.PDState != dbBeforeEntity.PDState && dbBeforeEntity.SubmitState == SubmitStateEnum.Submitted)
                {
                //---当前访视影像提交后，不允许修改PD确认状态。
                    throw new BusinessValidationFailedException(_localizer["Visit_NoPDStatusChangeAfterSubmission"]);
                }

                if (svCommand.PDState != dbBeforeEntity.PDState && dbBeforeEntity.RequestBackState == RequestBackStateEnum.PM_AgressBack)
                {
                //---当前访视影像提交后，不允许修改PD确认状态。
                    throw new BusinessValidationFailedException(_localizer["Visit_NoPDStatusChangeAfterSubmission"]);
                }

                if (svCommand.IsLostVisit)
                {
                    if (await _subjectVisitRepository.AnyAsync(t => t.Id == svCommand.Id && t.SubmitState == SubmitStateEnum.ToSubmit))
                    {
                //---当前访视已经有影像上传，不允许设置为失访。
                        throw new BusinessValidationFailedException(_localizer["Visit_UploadedNoLost"]);
                    }
                }

                dbBeforeEntity = await _subjectVisitRepository.UpdateFromDTOAsync(svCommand, true, false, verifyExp1, verifyExp2, verifyExp3);
            }


            //更新受试者 访视基准日期 是否入组确认
            if (svCommand.SubjectFirstGiveMedicineTime != null && svCommand.IsBaseLine)
            {

                await _subjectRepository.UpdatePartialFromQueryAsync(svCommand.SubjectId, t => new Subject()
                {
                    FirstGiveMedicineTime = svCommand.SubjectFirstGiveMedicineTime
                });

            }


            await _subjectVisitRepository.SaveChangesAsync();

            return ResponseOutput.Ok(dbBeforeEntity.Id.ToString());


        }




        [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{isUrgent:bool}")]
        [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
        //[Authorize(Policy = IRaCISPolicy.PM_IQC)]
        public async Task<IResponseOutput> SetSubjectVisitUrgent(Guid subjectVisitId, bool isUrgent)
        {
            await _subjectVisitRepository.UpdatePartialFromQueryAsync(subjectVisitId, u => new SubjectVisit() { IsUrgent = isUrgent }, true);

            return ResponseOutput.Ok();
        }



        [HttpDelete, Route("{trialId:guid}/{id:guid}")]
        [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
        //[Authorize(Policy = IRaCISPolicy.CRC)]
        public async Task<IResponseOutput> DeleteSV(Guid id)
        {
            if (await _repository.AnyAsync<DicomStudy>(t => t.SubjectVisitId == id))
            {
                //---当前访视已经有影像上传，不允许删除。
                return ResponseOutput.NotOk(_localizer["Visit_UploadedNoDelete"]);
            }
            if (await _subjectVisitRepository.AnyAsync(t => t.Id == id && t.InPlan))
            {
                //---计划内的访视不允许删除。
                return ResponseOutput.NotOk(_localizer["Visit_PlanVisitNoDelete"]);
            }
            if (await _subjectVisitRepository.AnyAsync(t => t.OutPlanPreviousVisitId == id))
            {
                //---当前访视已经被设置为另一访视的上一访视，不允许删除。
                return ResponseOutput.NotOk(_localizer["Visit_PreviousVisitNoDelete"]);
            }

            await _subjectVisitRepository.DeleteFromQueryAsync(s => s.Id == id, true);

            return ResponseOutput.Ok();
        }



        /// <summary>
        /// 获取访视下的Dicom 检查信息   分所有的， 阅片的 不阅片   isReading : 0 查询所有 1 查询仅仅阅片的
        /// </summary>
        /// <param name="trialId"></param>
        /// <param name="sujectVisitId"></param>
        /// <param name="isReading"></param>
        /// <returns></returns>
        [HttpGet, Route("{trialId:guid}/{sujectVisitId:guid}/{isReading}")]
        [AllowAnonymous]
        public async Task<List<VisitStudyDTO>> GetVisitStudyList(Guid trialId, Guid sujectVisitId, int isReading)
        {
            var studyList = await _repository.Where<DicomStudy>(t => t.TrialId == trialId && t.SubjectVisitId == sujectVisitId).Select(k => new VisitStudyDTO()
            {
                InstanceCount = k.InstanceCount,
                Modalities = k.Modalities,
                SeriesCount = k.SeriesCount,
                StudyCode = k.StudyCode,
                StudyId = k.Id
            }).ToListAsync();
            var studyIds = studyList.Select(t => t.StudyId).ToList();

            var instanceList = await _repository.Where<DicomInstance>(t => studyIds.Contains(t.StudyId))
                   .Select(t => new { t.SeriesId, t.Id, t.InstanceNumber, t.Path, t.NumberOfFrames }).ToListAsync();

            foreach (var t in studyList)
            {
                t.SeriesList = await _repository.Where<DicomSeries>(s => s.StudyId == t.StudyId)
               .WhereIf(isReading == 1, s => s.IsReading).OrderBy(s => s.SeriesNumber).
               ThenBy(s => s.SeriesTime)
               .ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).ToListAsync();


                t.SeriesList.ForEach(series =>
                {
                    series.InstanceList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k => k.Id).ToList();

                    //series.InstancePathList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k => k.Path).ToList();

                    //处理多帧
                    series.InstancePathList = instanceList.OrderBy(t => t.InstanceNumber).Where(s => s.SeriesId == series.Id)
                        .SelectMany(u =>
                        {

                            if (u.NumberOfFrames > 1)
                            {
                                var pathList = new List<string>();

                                for (int i = 1; i <= u.NumberOfFrames; i++)
                                {
                                    pathList.Add(u.Path + "?frame=" + (i - 1));
                                }
                                return pathList;
                            }
                            else
                            {
                                return new List<string> { u.Path };

                            }
                        })
                        .ToList();

                }

                );

                //设置为阅片与否 不更改数据库检查  的instance数量 和 SeriesCount  所以这里要实时统计
                t.SeriesCount = t.SeriesList.Count();
                t.InstanceCount = t.SeriesList.SelectMany(t => t.InstanceList).Count();
            }




            return studyList;

            //return ResponseOutput.Ok(studyList.Where(t => t.SeriesList.Count > 0).ToList());

        }

        /// <summary>
        /// 获取访视下的Dicom 检查信息   分所有的， 阅片的 不阅片   isReading : 0 查询所有 1 查询仅仅阅片的
        /// </summary>
        /// <param name="indto"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<List<VisitStudyDTO>> GetReadingVisitStudyList(GetReadingVisitStudyListIndto indto)
        {
            var result = new List<VisitStudyDTO>();

            var thisRowinfo = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == indto.VisitTaskId && x.StudyId != null).OrderBy(x => x.ReadingQuestionTrial.ShowOrder).ThenBy(x => x.RowIndex).Select(x => new
            {
                x.ReadingQuestionTrial.ShowOrder,
                x.RowIndex,
                x.SeriesId,
                x.StudyId,
                x.InstanceId,
            }).ToListAsync();


            var taskInfo = await _visitTaskRepository.Where(x => x.Id == indto.VisitTaskId).FirstNotNullAsync();

            if (taskInfo.ReadingTaskState == ReadingTaskState.HaveSigned)
            {

                var thisStudyIds = thisRowinfo.OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).Select(x => x.StudyId).Distinct().ToList();
                var thisSeriesIdIds = thisRowinfo.Where(x => x.SeriesId != null).OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).Select(x => x.SeriesId).Distinct().ToList();
                if (thisRowinfo.Count > 0)
                {
                    var thisVisitTaskStudy = await _repository.Where<DicomStudy>(t => thisStudyIds.Contains(t.Id)).Select(k => new VisitStudyDTO()
                    {
                        InstanceCount = k.InstanceCount,

                        SeriesCount = k.SeriesCount,

                        StudyId = k.Id,
                        IsCriticalSequence = true,

                    }).FirstOrDefaultAsync();

                    if (thisVisitTaskStudy != null)
                    {
                        thisVisitTaskStudy.StudyId = default(Guid);
                        var item = await _repository.Where<DicomSeries>(s => thisSeriesIdIds.Contains(s.Id)).OrderBy(s => s.SeriesNumber).
                            ThenBy(s => s.SeriesTime)
                            .ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();

                        if (item != null)
                        {
                            item.SeriesInstanceUid = string.Empty;

                            item.InstanceList = thisRowinfo.Where(y => y.InstanceId != null).OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).Select(y => y.InstanceId.Value).Distinct().ToList();

                            var tempInstanceList = await _repository.Where<DicomInstance>(t => item.InstanceList.Contains(t.Id)).OrderBy(t => t.InstanceNumber)
                                .Select(t => new TempInstance
                                {

                                    Id = t.Id,
                                    Path = t.Path,
                                    NumberOfFrames = t.NumberOfFrames,
                                    InstanceNumber = t.InstanceNumber
                                }).ToListAsync();


                            tempInstanceList.ForEach(x =>
                            {

                                var item = thisRowinfo.FirstOrDefault(y => y.InstanceId == x.Id);
                                if (item != null)
                                {
                                    x.ShowOrder = item.ShowOrder;
                                    x.RowIndex = item.RowIndex;
                                }

                            });


                            item.InstancePathList = tempInstanceList.OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).SelectMany(u =>
                        {

                            if (u.NumberOfFrames > 1)
                            {
                                var pathList = new List<string>();

                                for (int i = 1; i <= u.NumberOfFrames; i++)
                                {
                                    pathList.Add(u.Path + "?frame=" + (i - 1));
                                }
                                return pathList;
                            }
                            else
                            {
                                return new List<string> { u.Path };

                            }
                        })
                        .ToList();

                            item.InstanceCount = item.InstanceList.Count();

                            thisVisitTaskStudy.SeriesList.Add(item);
                            thisVisitTaskStudy.SeriesCount = thisVisitTaskStudy.SeriesList.Count();



                        }
                        result.Add(thisVisitTaskStudy);

                    }

                }
            }


            //如果是手动生成的标准，需要过滤检查和序列数据

            var isManualGenerate = await _trialReadingCriterionRepository.AnyAsync(t => t.Id == taskInfo.TrialReadingCriterionId && t.IsAutoCreate == false);

            var studyList = await _repository.Where<DicomStudy>(t => t.TrialId == indto.TrialId && t.SubjectVisitId == indto.SujectVisitId)
                .Select(k => new VisitStudyDTO()
            {
                InstanceCount = k.InstanceCount,
                Modalities = k.Modalities,
                SeriesCount = k.SeriesCount,
                StudyCode = k.StudyCode,
                StudyId = k.Id,

            }).ToListAsync();
            var studyIds = studyList.Select(t => t.StudyId).ToList();

            var instanceList = await _repository.Where<DicomInstance>(t => studyIds.Contains(t.StudyId))
                   .Select(t => new { t.SeriesId, t.Id, t.InstanceNumber, t.Path, t.NumberOfFrames,t.WindowCenter,t.WindowWidth }).ToListAsync();



            List<DicomSeriesDTO> seriesLists = await _repository.Where<DicomSeries>(s => studyIds.Contains(s.StudyId) /*&& s.IsReading*/)
                 .WhereIf(isManualGenerate==false, t => t.IsReading)
                .OrderBy(s => s.SeriesNumber).
              ThenBy(s => s.SeriesTime)
              .ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).ToListAsync();
            foreach (var t in studyList)
            {
                t.SeriesList = seriesLists.Where(s => s.StudyId == t.StudyId).OrderBy(s => s.SeriesNumber).
               ThenBy(s => s.SeriesTime).ToList();

                t.SeriesList.ForEach(series =>
                {
                    series.InstanceList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k => k.Id).ToList();


                    //series.InstancePathList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k => k.Path).ToList();

                    //处理多帧
                    series.InstancePathList = instanceList.Where(s => s.SeriesId == series.Id).OrderBy(t => t.InstanceNumber)
                        .SelectMany(u =>
                        {

                            if (u.NumberOfFrames > 1)
                            {
                                var pathList = new List<string>();

                                for (int i = 1; i <= u.NumberOfFrames; i++)
                                {
                                    pathList.Add(u.Path + "?frame=" + (i - 1));
                                }
                                return pathList;
                            }
                            else
                            {
                                return new List<string> { u.Path };

                            }
                        })
                        .ToList();

                    series.WindowWidth = instanceList.FirstOrDefault()?.WindowWidth;
                    series.WindowCenter = instanceList.FirstOrDefault()?.WindowCenter;
                });

                //设置为阅片与否 不更改数据库检查  的instance数量 和 SeriesCount  所以这里要实时统计
                t.SeriesCount = t.SeriesList.Count();
                t.InstanceCount = t.SeriesList.SelectMany(t => t.InstanceList).Count();
            }


            // 非Dicom

            var noDicomList = await _noneDicomStudyRepository.Where(x => x.TrialId == indto.TrialId && x.SubjectVisitId == indto.SujectVisitId).ToListAsync();


            List<VisitStudyDTO> noDicomStudyList = noDicomList.Select(x => new VisitStudyDTO()
            {
                InstanceCount = x.FileCount,
                StudyId = x.Id,
                Modalities = x.Modality,
                SeriesCount = 1,
                StudyCode = x.StudyCode,
                IsDicom = false,

            }).ToList();

            foreach (var item in noDicomStudyList)
            {
                var nodicom = noDicomList.Where(x => x.Id == item.StudyId).FirstOrDefault();
                item.SeriesList = new List<DicomSeriesDTO>()
                {
                    new DicomSeriesDTO (){
                    IsDicom=false,
                    Id=item.StudyId,
                    InstanceCount=await _noneDicomStudyFileRepository.Where(x=>x.NoneDicomStudyId==item.StudyId).CountAsync(),
                    Modality=item.Modalities,
                    StudyId=item.StudyId,
                    TrialId=nodicom.TrialId,
                    SiteId=nodicom.SiteId,
                    SubjectVisitId=nodicom.SubjectVisitId,
                    SubjectId=nodicom.SubjectId,
                    SeriesNumber=1,
                    NoneDicomFileFirstFile=await _noneDicomStudyFileRepository.Where(x=>x.NoneDicomStudyId==item.StudyId).Select(x=>x.Path).FirstOrDefaultAsync(),
                    }

                };
            }


            if (studyList == null || studyList.Count == 0)
            {
                studyList = new List<VisitStudyDTO>();
            }

            studyList.AddRange(noDicomStudyList);

            studyList.ForEach(x =>
            {
                x.SeriesList.ForEach(y =>
                {
                    y.IsBeMark = thisRowinfo.Any(z => z.SeriesId == y.Id);
                });

            });
            result.AddRange(studyList);
            result = result.Where(x => x.SeriesCount > 0).ToList();
            return result;


        }




        /// <summary>
        /// 设置受试者访视已执行  也就是将studyUploaded状态置为true  为了那些没有影像 人工设置准备
        /// </summary>
        /// <param name="subjectVisitId"></param>
        /// <returns></returns>
        [HttpPut("{trialId:guid}/{subjectVisitId:guid}")]
        [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
        [Obsolete]
        public async Task<IResponseOutput> SetSVExecuted(Guid subjectVisitId)
        {
            await _subjectVisitRepository.UpdatePartialFromQueryAsync(subjectVisitId, u => new SubjectVisit() { VisitExecuted = VisitExecutedEnum.Executed }, true);
            return ResponseOutput.Ok();
        }
    }
}
