using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using IRaCIS.Application.Interfaces;
using IRaCIS.Application.Contracts;
using IRaCIS.Core.API.Utility;
using IRaCIS.Core.Application.Contracts.Image;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using IRaCIS.Core.Application.Filter;
using IRaCIS.Core.Application.Contracts;

namespace IRaCIS.Api.Controllers
{
    /// <summary>
    /// 文件上传
    /// </summary>
    [Route("file")]
    [ApiController, Authorize, ApiExplorerSettings(GroupName = "Common")]
    public class FileController : ControllerBase
    {
        private readonly IFileService _fileService;
        private readonly IWebHostEnvironment _webHostEnvironment;
        private readonly IHostEnvironment _hostEnvironment;
        private ILogger<FileController> _logger;

        private string _targetFilePath;
        private readonly long _fileSizeLimit;
        private readonly string[] _permittedExtensions = { ".pdf", ".doc", ".docx" };

        public string trustedFileNameForFileStorage = "";

        // Get the default form options so that we can use them to set the default 
        // limits for request body data.
        private static readonly FormOptions _defaultFormOptions = new FormOptions();
        private string defaultUploadFilePath = string.Empty;
        public FileController(IFileService fileService, Microsoft.Extensions.Configuration.IConfiguration config,
            IHostEnvironment hostEnvironment, ILogger<FileController> logger,
            IWebHostEnvironment webHostEnvironment)
        {
            _fileService = fileService;
            _hostEnvironment = hostEnvironment;
            _webHostEnvironment = webHostEnvironment;
            _fileSizeLimit = config.GetValue<long>("FileSizeLimit");
            defaultUploadFilePath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).FullName;

            _logger = logger;

            _logger.LogWarning("File Path:" + defaultUploadFilePath);

        }


        /// <summary>
        /// 上传文件[FileUpload]
        /// </summary>
        /// <param name="attachmentType">附件类型</param>
        /// <param name="doctorId">医生Id</param>
        /// <returns>返回文件信息</returns>
        [HttpPost, Route("uploadFile/{attachmentType}/{doctorId}")]
        [DisableFormValueModelBinding]
        public async Task<IActionResult> UploadOrdinaryFile(string attachmentType, Guid doctorId)
        {
            #region 官方文档方式 


            if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
            {
                ModelState.AddModelError("File",
                    $"The request couldn't be processed (Error 1).");
                // Log error
                return BadRequest(ModelState);
            }

            var boundary = MultipartRequestHelper.GetBoundary(
                MediaTypeHeaderValue.Parse(Request.ContentType),
                _defaultFormOptions.MultipartBoundaryLengthLimit);
            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)
                {
                    // This check assumes that there's a file
                    // present without form data. If form data
                    // is present, this method immediately fails
                    // and returns the model error.
                    if (!MultipartRequestHelper
                        .HasFileContentDisposition(contentDisposition))
                    {
                        ModelState.AddModelError("File",
                            $"The request couldn't be processed (Error 2).");
                        // Log error
                        return BadRequest(ModelState);
                    }
                    else
                    {
                        // Don't trust the file name sent by the client. To display
                        // the file name, HTML-encode the value.
                        var trustedFileNameForDisplay = WebUtility.HtmlEncode(
                                contentDisposition.FileName.Value);

                        //var trustedFileNameForFileStorage = Path.GetRandomFileName();
                        trustedFileNameForFileStorage = contentDisposition.FileName.Value;

                        // **WARNING!**
                        // In the following example, the file is saved without
                        // scanning the file's contents. In most production
                        // scenarios, an anti-virus/anti-malware scanner API
                        // is used on the file before making the file available
                        // for download or for use by other systems. 
                        // For more information, see the topic that accompanies 
                        // this sample.

                        var streamedFileContent = await FileHelpers.ProcessStreamedFile(
                            section, contentDisposition, ModelState,
                            _permittedExtensions, _fileSizeLimit);

                        if (!ModelState.IsValid)
                        {
                            return BadRequest(ModelState);
                        }


                        //实际文件处理
                        string uploadFolderPath = Path.Combine(defaultUploadFilePath, "UploadFile/" + doctorId + "/");

                        var doctorAttachmentUploadFolder = Path.Combine(uploadFolderPath, attachmentType);

                        _targetFilePath = doctorAttachmentUploadFolder;

                        if (!Directory.Exists(doctorAttachmentUploadFolder)) Directory.CreateDirectory(doctorAttachmentUploadFolder);

                        using (var targetStream = System.IO.File.Create(
                            Path.Combine(_targetFilePath, trustedFileNameForFileStorage)))
                        {
                            await targetStream.WriteAsync(streamedFileContent);

                            var attachmentPath = $"/UploadFile/{doctorId}/{attachmentType}/{trustedFileNameForFileStorage}";

                            return new JsonResult(ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = attachmentPath, FullFilePath =  attachmentPath + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) }));

                        }


                    }
                }

                // Drain any remaining section body that hasn't been consumed and
                // read the headers for the next section.
                section = await reader.ReadNextSectionAsync();
            }


            return Created(nameof(FileController), null);

            #endregion
        }


        /// <summary>
        /// 上传文件（ 不是医生个人的文件）[FileUpload]
        /// 例如：阅片章程等
        /// </summary>
        /// <param name="type">文件类型</param>
        /// <returns></returns>

        [HttpPost, Route("uploadNonDoctorFile/{type}")]
        [DisableFormValueModelBinding]
        public async Task<IActionResult> UploadNonDoctorFile(string type)
        {
            #region New Test  实测OK

            //上传根路径
            string uploadFolderPath = Path.Combine(defaultUploadFilePath, "UploadFile");


            if (uploadFolderPath != null)
            {
                //文件类型路径处理
                var uploadTypePath = Path.Combine(uploadFolderPath, type);
                if (!Directory.Exists(uploadTypePath)) Directory.CreateDirectory(uploadTypePath);


                ////实际文件处理

                _targetFilePath = uploadTypePath;

                //获取boundary

                #region 方式二

                var boundary = MultipartRequestHelper.GetBoundary(
                    MediaTypeHeaderValue.Parse(Request.ContentType),
                    _defaultFormOptions.MultipartBoundaryLengthLimit);

                #endregion

                #region 方式一
                //var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;

                #endregion


                //得到reader
                var reader = new MultipartReader(boundary, HttpContext.Request.Body);
                //{ BodyLengthLimit = 2000 };//
                var section = await reader.ReadNextSectionAsync();

                //读取section
                while (section != null)
                {
                    var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
                    if (hasContentDispositionHeader)
                    {
                        trustedFileNameForFileStorage = contentDisposition.FileName.Value;


                        await WriteFileAsync(section.Body, Path.Combine(_targetFilePath, trustedFileNameForFileStorage));

                        #region 方式二

                        //using (var targetStream = System.IO.File.Create(
                        //    Path.Combine(_targetFilePath, trustedFileNameForFileStorage)))
                        //{

                        //    using (var memoryStream = new MemoryStream())
                        //    {
                        //        await section.Body.CopyToAsync(memoryStream);

                        //        await targetStream.WriteAsync(memoryStream.ToArray());
                        //    }
                        //}


                        #endregion


                        var attachmentPath = $"/UploadFile/{type}/{trustedFileNameForFileStorage}";

                        return new JsonResult(ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = attachmentPath, FullFilePath =  attachmentPath + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) }));
                    }

                    section = await reader.ReadNextSectionAsync();
                }
                return Created(nameof(FileController), null);


            }
            return new JsonResult(ResponseOutput.NotOk("服务器端映射路径操作失败"));
            #endregion
        }



        /// <summary>
        /// 写文件导到磁盘
        /// </summary>
        /// <param name="stream">流</param>
        /// <param name="path">文件保存路径</param>
        /// <returns></returns>
        public 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;

          
        }


       

        #region 上传无影响部分

        /// <summary>
        /// 下载多个医生的所有附件
        /// </summary>
        /// <param name="doctorIds"></param>
        /// <returns></returns>

        [HttpPost, Route("downloadDoctorAttachments")]
        public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachment(Guid[] doctorIds)
        {

            var path = await _fileService.CreateDoctorsAllAttachmentZip(doctorIds);

            return ResponseOutput.Ok(new UploadFileInfoDTO
            {
                FilePath = path,
                FullFilePath =  path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)

            });     
        }

        /// <summary>
        /// 下载医生官方简历
        /// </summary>
        /// <param name="language"></param>
        /// <param name="doctorIds"></param>
        /// <returns></returns>
        [HttpPost, Route("downloadOfficialCV/{language}")]
        public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadOfficialResume(int language, Guid[] doctorIds)
        {

            var path = _fileService.CreateDoctorsAllAttachmentZip(doctorIds);
            return ResponseOutput.Ok(new UploadFileInfoDTO
            {
                FilePath = await _fileService.CreateOfficialResumeZip(language, doctorIds),
                FullFilePath =  path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
            });
        }

        /// <summary>
        /// 下载指定医生的指定附件
        /// </summary>
        /// <param name="doctorId">医生Id</param>
        /// <param name="attachmentIds">要下载的附件Id</param>
        /// <returns></returns>
        [HttpPost, Route("downloadByAttachmentId/{doctorId}")]
        public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachmentById(Guid doctorId, Guid[] attachmentIds)
        {
            var path = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds);
            return ResponseOutput.Ok(new UploadFileInfoDTO
            {
                FilePath = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds),
                FullFilePath =  path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
            });
        }

        #endregion


        #region 废弃

        public class UploadDTFCommand
        {
            public Guid TrialId { get; set; }
            public Guid SiteId { get; set; }
            public Guid SubjectId { get; set; }
            public Guid SubjectVisitId { get; set; }

        }
        /// <summary>
        /// 流式上传 临时文件
        /// </summary>
        /// <returns></returns>
        [HttpPost("UploadDTF/{trialId:guid}/{siteId:guid}/{subjectId:guid}/{subjectVisitId:guid}/{studyId:guid}")]
        [DisableFormValueModelBinding]
        [DisableRequestSizeLimit]
        [Obsolete]
        public async Task<IResponseOutput> UploadingStream(Guid trialId, Guid siteId, Guid subjectId, Guid subjectVisitId, Guid studyId, [FromServices] IStudyDTFService studyDTFService)
        {

            //上传根路径
            string uploadFolderPath = Path.Combine(defaultUploadFilePath, "Dicom");


            var dtfPath = Path.Combine(uploadFolderPath, DateTime.Now.Year.ToString(), trialId.ToString(),
                siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString());

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


            #region 之前DTF 先上传临时文件，再拷贝
            ////上传根路径
            //string uploadFolderPath = Path.Combine(defaultUploadFilePath, "UploadFile");
            ////文件类型路径处理
            //var uploadTempFilePath = Path.Combine(uploadFolderPath, "TempFile");
            //if (!Directory.Exists(uploadTempFilePath)) Directory.CreateDirectory(uploadTempFilePath);


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

            //await WriteFileAsync(section.Body, Path.Combine(uploadTempFilePath, trustedFileNameForFileStorage));

            //var attachmentPath = $"{uploadTempFilePath}/{trustedFileNameForFileStorage}";

            ////多个文件上传，在这里返回就不合适，需要在外层，返回所有的文件路径，实际需要时再更改
            //return ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = attachmentPath });

            #endregion


            //获取boundary
            var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
            //得到reader
            var reader = new MultipartReader(boundary, HttpContext.Request.Body);

            var section = await reader.ReadNextSectionAsync();

            //读取section
            while (section != null)
            {
                var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
                if (hasContentDispositionHeader)
                {
                    var realName = contentDisposition.FileName.Value;

                    var fileNameEX = Path.GetExtension(contentDisposition.FileName.Value);
                    var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX;


                    var relativePath = $"/Dicom/{DateTime.Now.Year.ToString()}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{trustedFileNameForFileStorage}";
                    studyDTFService.AddStudyDTF(new Core.Application.Contracts.Dicom.DTO.StudyDTFAddOrUpdateCommand()
                    {
                        StudyId = studyId,

                        FileName = realName,

                        Path = relativePath
                    });

                    await WriteFileAsync(section.Body, Path.Combine(dtfPath, trustedFileNameForFileStorage));

                    //仅仅返回一个文件，如果多文件上传  在最后返回多个路径
                    return ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = relativePath, FullFilePath = relativePath + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) });
                }
                section = await reader.ReadNextSectionAsync();
            }
            return ResponseOutput.NotOk("Upload error");
        }


        /// <summary>
        /// 流式上传 非Dicom文件
        /// </summary>
        /// <returns></returns>
        [HttpPost("UploadNoneDICOM")]
        [DisableFormValueModelBinding]
        [DisableRequestSizeLimit]
        [Obsolete]
        public async Task<IResponseOutput> UploadingNoneDicomStream([FromForm] ArchiveStudyCommand archiveStudyCommand, [FromServices] IStudyService studyService)
        {
            //上传根路径
            string uploadFolderPath = Path.Combine(defaultUploadFilePath, "Dicom");

            //文件类型路径处理
            //var noneDicomPath = Path.Combine(uploadFolderPath, DateTime.Now.Year.ToString(), archiveStudyCommand.TrialId.ToString(),
            //    archiveStudyCommand.SiteId.ToString(), archiveStudyCommand.SubjectId.ToString(), archiveStudyCommand.SubjectVisitId.ToString(), "Data");

            //var dtfPath = Path.Combine(uploadFolderPath, DateTime.Now.Year.ToString(), archiveStudyCommand.TrialId.ToString(),
            //    archiveStudyCommand.SiteId.ToString(), archiveStudyCommand.SubjectId.ToString(), archiveStudyCommand.SubjectVisitId.ToString());

            var noneDicomPath = string.Empty;

            var dtfPath = string.Empty;

            var tempDtfPath = Path.Combine(defaultUploadFilePath/*, archiveStudyCommand.DTFPath*/);


            if (!Directory.Exists(noneDicomPath))
            {
                Directory.CreateDirectory(noneDicomPath);
            }
            if (!System.IO.File.Exists(tempDtfPath))
            {
                return ResponseOutput.NotOk("DTF file can not found");
            }

            System.IO.File.Move(tempDtfPath, dtfPath);

            var saveFileDic = new Dictionary<string, string>();

            //获取boundary
            var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
            //得到reader
            var reader = new MultipartReader(boundary, HttpContext.Request.Body);

            var section = await reader.ReadNextSectionAsync();

            //读取section
            while (section != null)
            {
                var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
                if (hasContentDispositionHeader)
                {
                    var fileName = contentDisposition.FileName.Value;
                    var fileNameEX = Path.GetExtension(contentDisposition.FileName.Value);
                    var trustedFileNameForFileStorage = Guid.NewGuid() + fileNameEX;



                    await WriteFileAsync(section.Body, Path.Combine(noneDicomPath, trustedFileNameForFileStorage));

                    var attachmentPath = $"{noneDicomPath}/{trustedFileNameForFileStorage}";

                    saveFileDic.Add(attachmentPath, fileName);


                }
                section = await reader.ReadNextSectionAsync();
            }

            //处理数据库操作

            //studyService.DealNonDicomFile(saveFileDic, archiveStudyCommand);
            return ResponseOutput.Ok(saveFileDic);
        }
        #endregion

    }
}
