using AutoMapper;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Filter;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Infrastructure.Extention;
using MediatR;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace IRaCIS.Core.API.Controllers
{
    [ApiExplorerSettings(GroupName = "Trial")]
    [ApiController]
    public class UploadController : ControllerBase
    {
        public IMapper _mapper { get; set; }
        public IUserInfo _userInfo { get; set; }
        private readonly IMediator _mediator;

        private readonly IWebHostEnvironment _hostEnvironment;

        private readonly IServiceProvider _serviceProvider;


        public UploadController(IMapper mapper, IUserInfo userInfo, IMediator mediator, IWebHostEnvironment hostEnvironment, IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
            _hostEnvironment = hostEnvironment;
            _mediator = mediator;
            _mapper = mapper;
            _userInfo = userInfo;
        }

        [HttpPost("TrialDocument/UploadTrialDoc/{trialId:guid}")]
        [DisableRequestSizeLimit]
        [DisableFormValueModelBinding]
        public async Task<IResponseOutput> UploadTrialDoc(Guid trialId)
        {
            var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;

            var reader = new MultipartReader(boundary, HttpContext.Request.Body);

            var section = await reader.ReadNextSectionAsync();

            while (section != null)
            {
                var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);

                if (hasContentDispositionHeader)
                {

                    DealTrialStorePath(trialId, contentDisposition.FileName.Value, out string serverFilePath, out string relativePath );

                    await WriteFileAsync(section.Body, serverFilePath);

                    //仅仅返回一个文件，如果多文件上传  在最后返回多个路径
                    return ResponseOutput.Ok(new
                    {
                        FilePath = relativePath,
                        FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken
                    });

                }
                section = await reader.ReadNextSectionAsync();
            }
            return ResponseOutput.Ok();
        }


        [HttpPost("TrialDocument/UploadSystemDoc")]
        [DisableRequestSizeLimit]
        [DisableFormValueModelBinding]
        public async Task<IResponseOutput> UploadSysTemDoc( )
        {
            var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;

            var reader = new MultipartReader(boundary, HttpContext.Request.Body);

            var section = await reader.ReadNextSectionAsync();

            while (section != null)
            {
                var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);

                if (hasContentDispositionHeader)
                {

                    DealSysTemStorePath( contentDisposition.FileName.Value, out string serverFilePath, out string relativePath);

                    await WriteFileAsync(section.Body, serverFilePath);

                    //仅仅返回一个文件，如果多文件上传  在最后返回多个路径
                    return ResponseOutput.Ok(new
                    {
                        FilePath = relativePath,
                        FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken
                    });

                }
                section = await reader.ReadNextSectionAsync();
            }
            return ResponseOutput.Ok();
        }


        /// <summary>
        /// 上传通用文档  比如一致性核查的 比如导出的excel 模板
        /// </summary>
        /// <returns></returns>
        [HttpPost("CommonDocument/UploadCommonDoc")]
        [DisableRequestSizeLimit]
        [DisableFormValueModelBinding]
        public async Task<IResponseOutput> UploadCommonDoc(/*string fileType, string moduleType*/)
        {
            var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;

            var reader = new MultipartReader(boundary, HttpContext.Request.Body);

            var section = await reader.ReadNextSectionAsync();

            while (section != null)
            {
                var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);

                if (hasContentDispositionHeader)
                {

                    DealCommonStorePath(/*fileType, moduleType,*/ contentDisposition.FileName.Value, out string serverFilePath, out string relativePath);

                    await WriteFileAsync(section.Body, serverFilePath);

                    //仅仅返回一个文件，如果多文件上传  在最后返回多个路径
                    return ResponseOutput.Ok(new
                    {
                        FilePath = relativePath,
                        FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken
                    });

                }
                section = await reader.ReadNextSectionAsync();
            }
            return ResponseOutput.Ok();
        }




        /// <summary>
        /// 上传系统通知文档
        /// </summary>
        /// <returns></returns>
        [HttpPost("SystemNotice/UploadSystemNoticeDoc")]
        [DisableRequestSizeLimit]
        [DisableFormValueModelBinding]
        public async Task<IResponseOutput> UploadSystemNoticeDoc()
        {
            var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;

            var reader = new MultipartReader(boundary, HttpContext.Request.Body);

            var section = await reader.ReadNextSectionAsync();

            while (section != null)
            {
                var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);

                if (hasContentDispositionHeader)
                {

                    DealSystemNoticePath( contentDisposition.FileName.Value, out string serverFilePath, out string relativePath);

                    await WriteFileAsync(section.Body, serverFilePath);

                    //仅仅返回一个文件，如果多文件上传  在最后返回多个路径
                    return ResponseOutput.Ok(new
                    {
                        FilePath = relativePath,
                        FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken
                    });

                }
                section = await reader.ReadNextSectionAsync();
            }
            return ResponseOutput.Ok();
        }


        private void DealCommonStorePath(/*string fileType, string moduleType,*/ string fileRealName, out string serverFilePath, out string relativePath)
        {
            var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).FullName;
            //上传根路径
            var _fileStorePath = Path.Combine(rootPath, StaticData.SystemDataFolder);

            //文件类型路径处理
            var uploadFolderPath = Path.Combine(_fileStorePath, StaticData.DataTemplate);
            if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);


            //var fileNameEX = Path.GetExtension(fileRealName);
            //var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX;

            var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileRealName;

            relativePath = $"/{StaticData.SystemDataFolder}/{StaticData.DataTemplate}/{trustedFileNameForFileStorage}";

            serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
        }


        private void DealSystemNoticePath(string fileRealName, out string serverFilePath, out string relativePath)
        {
            var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).FullName;
            //上传根路径
            var _fileStorePath = Path.Combine(rootPath, StaticData.SystemDataFolder);

            //文件类型路径处理
            var uploadFolderPath = Path.Combine(_fileStorePath, StaticData.NoticeAttachment);
            if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);


            //var fileNameEX = Path.GetExtension(fileRealName);
            //var fileName=Path.GetFileName(fileRealName);
            //var trustedFileNameForFileStorage = fileName + Guid.NewGuid().ToString() + fileNameEX;

            var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileRealName;

            relativePath = $"/{StaticData.SystemDataFolder}/{StaticData.NoticeAttachment}/{trustedFileNameForFileStorage}";

            serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
        }


        private void DealSysTemStorePath(  string fileRealName,  out string serverFilePath, out string relativePath)
        {
            var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).FullName;
            //上传根路径
            var _fileStorePath = Path.Combine(rootPath, StaticData.SystemDataFolder);

            //文件类型路径处理
            var uploadFolderPath = Path.Combine(_fileStorePath, StaticData.SignDocumentFolder);
            if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);


            //var fileNameEX = Path.GetExtension(fileRealName);
            //var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX;

            var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileRealName;

            relativePath = $"/{StaticData.SystemDataFolder}/{ StaticData.SignDocumentFolder}/{trustedFileNameForFileStorage}";

            serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
        }


        private void DealTrialStorePath(Guid trialId,string fileRealName,  out string serverFilePath, out string relativePath)
        {
            var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).FullName;
            //上传根路径
            var _fileStorePath = Path.Combine(rootPath, StaticData.TrialDataFolder);

            //文件类型路径处理
            var uploadFolderPath = Path.Combine(_fileStorePath, trialId.ToString(), StaticData.SignDocumentFolder);
            if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);


            //var fileNameEX = Path.GetExtension(fileRealName);
            //var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX;

            var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileRealName;

            relativePath = $"/{StaticData.TrialDataFolder}/{trialId}/{StaticData.SignDocumentFolder}/{trustedFileNameForFileStorage}";

             serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);               
        }



        /// <summary>
        /// 上传临床数据
        /// </summary>
        /// <param name="subjectVisitId"></param>
        /// <param name="uploadType"> 1:DICOM DTF 2:非DIOM DTF  3: 受试者临床数据</param>
        /// <param name="_subjectVisitRepository"></param>
        /// <returns></returns>
        [HttpPost("ClinicalData/UploadVisitClinicalData/{trialId:guid}/{subjectVisitId:guid}/{type}")]
        [DisableRequestSizeLimit]
        [DisableFormValueModelBinding]
        [Obsolete]
        public async Task<IResponseOutput> UploadVisitData(Guid subjectVisitId, [FromRoute] UploadFileTypeEnum uploadType, [FromServices] IRepository<SubjectVisit> _subjectVisitRepository)
        {

            var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;

            var reader = new MultipartReader(boundary, HttpContext.Request.Body);

            var section = await reader.ReadNextSectionAsync();
            while (section != null)
            {
                var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);

                if (hasContentDispositionHeader)
                {

                    DealStorePath(subjectVisitId, contentDisposition.FileName.Value, uploadType, out string serverFilePath, out string relativePath, _subjectVisitRepository);

                    await WriteFileAsync(section.Body, serverFilePath);

                    //仅仅返回一个文件，如果多文件上传  在最后返回多个路径
                    return ResponseOutput.Ok(new
                    {
                        FilePath = relativePath,
                        FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken
                    });

                }
                section = await reader.ReadNextSectionAsync();
            }
            return ResponseOutput.Ok();

        }

        public enum UploadFileTypeEnum
        {
            DICOM_DTF = 1,

            NonDICOM_DTF = 2,

            SubjectTreatement = 3
        }

        private  void DealStorePath(Guid subjectVisitId, string realName, UploadFileTypeEnum typeEnum, out string serverFilePath, out string relativePath, IRepository<SubjectVisit> _subjectVisitRepository)
        {

            var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).FullName;

            //上传根路径
            var _fileStorePath = Path.Combine(rootPath, StaticData.TrialDataFolder);

            var sv = _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefault();

            //处理存储的文件夹 
            var typeFolder = typeEnum == UploadFileTypeEnum.SubjectTreatement ? StaticData.TreatmenthistoryFolder
                : typeEnum == UploadFileTypeEnum.NonDICOM_DTF ? StaticData.NoneDicomFolder
                : /*typeEnum == UploadFileTypeEnum.DICOM_DTF ?*/ StaticData.DicomFolder;

            string uploadFolderPath = Path.Combine(_fileStorePath, sv.TrialId.ToString(), sv.SiteId.ToString(), sv.SubjectId.ToString(), subjectVisitId.ToString(), typeFolder);

            if (!Directory.Exists(uploadFolderPath))
            {
                Directory.CreateDirectory(uploadFolderPath);
            }

            var fileNameEX = Path.GetExtension(realName);

            var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX;

            relativePath = $"/{StaticData.TrialDataFolder}/{sv.TrialId}/{sv.SiteId}/{sv.SubjectId}/{subjectVisitId}/{typeFolder}/{trustedFileNameForFileStorage}";

            serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);

            //处理 是上传文件返回路径 还是上传文件后，需要保存数据库


            if (typeEnum == UploadFileTypeEnum.SubjectTreatement)
            {
                var repository = _serviceProvider.GetService(typeof(IRepository<PreviousPDF>)) as IRepository<PreviousPDF>;

               _=  repository.InsertOrUpdateAsync(new PreviousPDFAddOrEdit() { FileName = realName, Path = relativePath, SubjectVisitId = subjectVisitId }, true).Result;
            }
            else if (typeEnum == UploadFileTypeEnum.NonDICOM_DTF)
            {

            }
            else if (typeEnum == UploadFileTypeEnum.NonDICOM_DTF)
            {

            }
        }

        /// <summary>
        /// 写文件导到磁盘
        /// </summary>
        /// <param name="stream">流</param>
        /// <param name="path">文件保存路径</param>
        /// <returns></returns>
        private static async Task<int> WriteFileAsync(System.IO.Stream stream, string path)
        {
            const int FILE_WRITE_SIZE = 84975;//写出缓冲区大小
            int writeCount = 0;
            using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write, FILE_WRITE_SIZE, true))
            {
                byte[] byteArr = new byte[FILE_WRITE_SIZE];
                int readCount = 0;
                while ((readCount = await stream.ReadAsync(byteArr, 0, byteArr.Length)) > 0)
                {
                    await fileStream.WriteAsync(byteArr, 0, readCount);
                    writeCount += readCount;
                }
            }
            return writeCount;
        }
    }
}
