using FellowOakDicom;
using FellowOakDicom.Imaging.Codec;
using FellowOakDicom.Network;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Extention;
using IRaCIS.Core.SCP.Service;
using MassTransit;
using Medallion.Threading;
using Microsoft.AspNetCore.Hosting;
using Serilog.Sinks.File;
using System.Data;
using System.Runtime.Intrinsics.X86;
using System.Text;
using ZiggyCreatures.Caching.Fusion;
using static IRaCIS.Core.SCP.Service.CacheHelper;

namespace IRaCIS.Core.SCP.Service
{
    public class DicomArchiveService(IRepository<SCPPatient> _patientRepository,
        IRepository<SCPStudy> _studyRepository,
        IRepository<SCPSeries> _seriesRepository,
        IRepository<SCPInstance> _instanceRepository
        ) : BaseService, IDicomArchiveService
    {





        /// <summary>
        /// 单个文件接收 归档
        /// </summary>
        /// <param name="dataset"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<Guid> ArchiveDicomFileAsync(DicomFile dicomFile, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE, long fileSize)
        {

            var dataset = dicomFile.Dataset;

            string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
            string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
            string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);

            string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);

            //Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
            Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
            Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString());
            Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString());

            var isStudyNeedAdd = false;
            var isSeriesNeedAdd = false;
            var isInstanceNeedAdd = false;
            var isPatientNeedAdd = false;

            //var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");

            //using (@lock.Acquire())
            {
                var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId == trialSiteId);
                var findStudy = await _studyRepository.FirstOrDefaultAsync(t => t.Id == studyId);
                var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
                var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == instanceId);

                DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay);

                //先传输了修改了患者编号的，又传输了没有修改患者编号的，导致后传输的没有修改患者编号的下面的检查为0
                if (findPatient == null /*&& findStudy==null*/)
                {
                    isPatientNeedAdd = true;


                    findPatient = new SCPPatient()
                    {
                        Id = NewId.NextSequentialGuid(),
                        TrialId = trialId,
                        TrialSiteId = trialSiteId,
                        PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
                        PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
                        PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
                        PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
                        PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),

                        EarliestStudyTime = studyTime,
                        LatestStudyTime = studyTime,
                        LatestPushTime = DateTime.Now,
                    };

                    if (findPatient.PatientBirthDate.Length == 8)
                    {
                        var birthDateStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}-{findPatient.PatientBirthDate[4]}{findPatient.PatientBirthDate[5]}-{findPatient.PatientBirthDate[6]}{findPatient.PatientBirthDate[7]}";

                        var yearStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}";

                        int year = 0;

                        var canParse = int.TryParse(yearStr, out year);

                        if (canParse && year > 1900)
                        {
                            findPatient.PatientBirthDate = birthDateStr;

                            DateTime birthDate;

                            if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate, out birthDate))
                            {
                                var patientAge = studyTime.Value.Year - birthDate.Year;
                                // 如果生日还未到，年龄减去一岁
                                if (studyTime.Value < birthDate.AddYears(patientAge))
                                {
                                    patientAge--;
                                }

                                findPatient.PatientAge = patientAge.ToString();
                            }

                        }
                        else
                        {
                            findPatient.PatientBirthDate = string.Empty;
                        }

                    }
                }
                else
                {
                    if (studyTime < findPatient.EarliestStudyTime)
                    {
                        findPatient.EarliestStudyTime = studyTime;
                    }
                    if (studyTime > findPatient.LatestStudyTime)
                    {
                        findPatient.LatestStudyTime = studyTime;
                    }

                    findPatient.LatestPushTime = DateTime.Now;
                    findPatient.PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
                    findPatient.PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty);
                    findPatient.PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty);
                    findPatient.PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty);

                    if (findPatient.PatientBirthDate.Length == 8)
                    {
                        var birthDateStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}-{findPatient.PatientBirthDate[4]}{findPatient.PatientBirthDate[5]}-{findPatient.PatientBirthDate[6]}{findPatient.PatientBirthDate[7]}";

                        var yearStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}";

                        int year = 0;

                        var canParse = int.TryParse(yearStr, out year);

                        if (canParse && year > 1900)
                        {
                            findPatient.PatientBirthDate = birthDateStr;

                        }
                        else
                        {
                            findPatient.PatientBirthDate = string.Empty;
                        }

                    }
                }

                if (findStudy == null)
                {
                    isStudyNeedAdd = true;
                    findStudy = new SCPStudy
                    {
                        CalledAE = calledAE,
                        CallingAE = callingAE,

                        PatientId = findPatient.Id,
                        Id = studyId,
                        TrialId = trialId,
                        TrialSiteId = trialSiteId,
                        StudyInstanceUid = studyInstanceUid,
                        StudyTime = studyTime,
                        DicomStudyDate = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty),
                        DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty),

                        Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
                        //ModalityForEdit = modalityForEdit,
                        Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
                        InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty),
                        PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
                        PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
                        PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
                        PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
                        BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),

                        StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty),
                        AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty),

                        //需要特殊处理
                        PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),


                        AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
                        AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
                        TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),

                        RadionuclideTotalDose= dataset.GetSingleValueOrDefault(DicomTag.RadionuclideTotalDose, string.Empty),

                        RadionuclideHalfLife= dataset.GetSingleValueOrDefault(DicomTag.RadionuclideHalfLife, string.Empty),
                        RadiopharmaceuticalStartTime = dataset.GetSingleValueOrDefault(DicomTag.RadiopharmaceuticalStartTime, string.Empty),


                        Manufacturer = dataset.GetSingleValueOrDefault(DicomTag.Manufacturer, string.Empty),
                        ManufacturerModelName = dataset.GetSingleValueOrDefault(DicomTag.ManufacturerModelName, string.Empty),
                        DeviceSerialNumber = dataset.GetSingleValueOrDefault(DicomTag.DeviceSerialNumber, string.Empty),
                        DeviceUID = dataset.GetSingleValueOrDefault(DicomTag.DeviceUID, string.Empty),
                        SoftwareVersions = dataset.GetSingleValueOrDefault(DicomTag.SoftwareVersions, string.Empty),
                        PatientWeight = dataset.GetSingleValueOrDefault(DicomTag.PatientWeight, string.Empty),


                        //IsDoubleReview = addtionalInfo.IsDoubleReview,
                        SeriesCount = 0,
                        InstanceCount = 0
                    };


                    if (findStudy.PatientBirthDate.Length == 8)
                    {
                        findStudy.PatientBirthDate = $"{findStudy.PatientBirthDate[0]}{findStudy.PatientBirthDate[1]}{findStudy.PatientBirthDate[2]}{findStudy.PatientBirthDate[3]}-{findStudy.PatientBirthDate[4]}{findStudy.PatientBirthDate[5]}-{findStudy.PatientBirthDate[6]}{findStudy.PatientBirthDate[7]}";
                    }
                }
                else
                {
                    findStudy.DicomStudyDate = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty);
                    findStudy.DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty);
                    findStudy.CalledAE = calledAE;
                    findStudy.CallingAE = callingAE;
                    findStudy.PatientIdStr = patientIdStr;
                    findStudy.PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
                    findStudy.PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty);
                    findStudy.PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty);
                    findStudy.UpdateTime = DateTime.Now;

                    await _patientRepository.BatchUpdateNoTrackingAsync(t => t.Id == findStudy.PatientId, u => new SCPPatient() { LatestPushTime = DateTime.Now });
                }


                if (findSerice == null)
                {
                    isSeriesNeedAdd = true;

                    findSerice = new SCPSeries
                    {
                        Id = seriesId,
                        StudyId = findStudy.Id,

                        StudyInstanceUid = findStudy.StudyInstanceUid,
                        SeriesInstanceUid = seriesInstanceUid,
                        SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1),
                        //SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay),
                        //SeriesTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.SeriesDate) + dataset.GetSingleValue<string>(DicomTag.SeriesTime), out DateTime dt) ? dt : null,
                        SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.SeriesDate).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.SeriesTime).TimeOfDay),
                        DicomSeriesDate = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty),
                        DicomSeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty),

                        Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
                        Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
                        SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),

                        ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty),
                        ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty),
                        BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
                        SequenceName = dataset.GetSingleValueOrDefault(DicomTag.SequenceName, string.Empty),
                        ProtocolName = dataset.GetSingleValueOrDefault(DicomTag.ProtocolName, string.Empty),
                        ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),

                        AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
                        AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
                        TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),

                        RadiopharmaceuticalInformationSequence = dataset.GetSingleValueOrDefault(DicomTag.RadiopharmaceuticalInformationSequence, string.Empty),
                        AcquisitionDate = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionDate, string.Empty),


                        InstanceCount = 0
                    };

                    ++findStudy.SeriesCount;
                }
                else
                {
                    findSerice.DicomSeriesDate = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty);
                    findSerice.DicomSeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty);
                    findSerice.UpdateTime = DateTime.Now;
                }

                var transferSyntaxUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);

                var isEncapsulated = false;
                if (transferSyntaxUID.IsNotNullOrEmpty())
                {
                    isEncapsulated = DicomTransferSyntax.Lookup(DicomUID.Parse(transferSyntaxUID)).IsEncapsulated;
                }

                if (findInstance == null)
                {
                    isInstanceNeedAdd = true;
                    findInstance = new SCPInstance
                    {
                        Id = instanceId,
                        StudyId = findStudy.Id,
                        SeriesId = findSerice.Id,
                        StudyInstanceUid = findStudy.StudyInstanceUid,
                        SeriesInstanceUid = findSerice.SeriesInstanceUid,

                        SopInstanceUid = sopInstanceUid,
                        InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
                        InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.ContentDate).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.ContentTime).TimeOfDay),
                        //InstanceTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.ContentDate) + dataset.GetSingleValue<string>(DicomTag.ContentTime), out DateTime dt) ? dt : null,
                        //InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate,(DateTime?)null)?.Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, TimeSpan.Zero)),
                        //dataset.GetSingleValueOrDefault(DicomTag.ContentDate,DateTime.Now);//, DicomTag.ContentTime)
                        CPIStatus = false,
                        ImageRows = dataset.GetSingleValueOrDefault(DicomTag.Rows, 0),
                        ImageColumns = dataset.GetSingleValueOrDefault(DicomTag.Columns, 0),
                        SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0),

                        SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
                        NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1),
                        PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty),
                        ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
                        FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty),
                        WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty),
                        WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty),

                        PhotometricInterpretation = dataset.GetSingleValueOrDefault(DicomTag.PhotometricInterpretation, string.Empty),
                        BitsAllocated = dataset.GetSingleValueOrDefault(DicomTag.BitsAllocated, 0),
                        PixelRepresentation = dataset.GetSingleValueOrDefault(DicomTag.PixelRepresentation, string.Empty),
                        RescaleIntercept = dataset.GetSingleValueOrDefault(DicomTag.RescaleIntercept, string.Empty),
                        RescaleSlope = dataset.GetSingleValueOrDefault(DicomTag.RescaleSlope, string.Empty),
                        ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty),
                        ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty),
                        SequenceOfUltrasoundRegions = dataset.GetSingleValueOrDefault(DicomTag.SequenceOfUltrasoundRegions, string.Empty),
                        FrameTime = dataset.GetSingleValueOrDefault(DicomTag.FrameTime, string.Empty),
                        CorrectedImage = dataset.GetSingleValueOrDefault(DicomTag.CorrectedImage, string.Empty),
                        Units = dataset.GetSingleValueOrDefault(DicomTag.Units, string.Empty),
                        DecayCorrection = dataset.GetSingleValueOrDefault(DicomTag.DecayCorrection, string.Empty),
                        EncapsulatedDocument = dataset.GetSingleValueOrDefault(DicomTag.EncapsulatedDocument, string.Empty),


                        Path = fileRelativePath,

                        FileSize = fileSize,

                    };

                    ++findStudy.InstanceCount;
                    ++findSerice.InstanceCount;
                }
                else
                {
                    findInstance.SOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
                    findInstance.MediaStorageSOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
                    findInstance.TransferSytaxUID = transferSyntaxUID;
                    findInstance.MediaStorageSOPInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
                    findInstance.IsEncapsulated = isEncapsulated;
                    findInstance.UpdateTime = DateTime.Now;
                }

                if (isPatientNeedAdd)
                {
                    var ss = await _patientRepository.AddAsync(findPatient);
                }
                if (isStudyNeedAdd)
                {
                    var dd = await _studyRepository.AddAsync(findStudy);
                }
                else
                {
                    await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == findStudy.Id, t => new SCPStudy() { IsUploadFinished = false });
                }

                if (isSeriesNeedAdd)
                {
                    await _seriesRepository.AddAsync(findSerice);
                }
                if (isInstanceNeedAdd)
                {
                    await _instanceRepository.AddAsync(findInstance);
                }
                else
                {
                    await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath, FileSize = fileSize });
                }

                await _studyRepository.SaveChangesAsync();

                return findStudy.Id;
            }

        }


        // 从DICOM文件中获取使用的字符集
        private string GetEncodingVaulueFromDicomFile(DicomDataset dataset, DicomTag dicomTag)
        {

            // 获取DICOM文件的特定元素，通常用于指示使用的字符集
            var charset = dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);

            var dicomEncoding = DicomEncoding.GetEncoding(charset);


            var dicomStringElement = dataset.GetDicomItem<DicomStringElement>(dicomTag);

            var bytes = dicomStringElement.Buffer.Data;


            return dicomEncoding.GetString(bytes);


            //// 从DICOM文件中获取使用的字符集
            //string filePath = "C:\\Users\\hang\\Documents\\WeChat Files\\wxid_r2imdzb7j3q922\\FileStorage\\File\\2024-05\\1.2.840.113619.2.80.169103990.5390.1271401378.4.dcm";
            //DicomFile dicomFile = DicomFile.Open(filePath);

            //// 获取DICOM文件的特定元素，通常用于指示使用的字符集
            //var charset = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);

            //var dicomEncoding = DicomEncoding.GetEncoding(charset);

            //var value = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);

            //var dicomStringElement = dicomFile.Dataset.GetDicomItem<DicomStringElement>(DicomTag.PatientName);

            //var bytes = dicomStringElement.Buffer.Data;

            //var aa= dicomEncoding.GetString(bytes);


        }
    }
}
