using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Performance.DtoModels;
using Performance.DtoModels.AppSettings;
using Performance.EntityModels;
using Performance.Infrastructure;
using Performance.Services;
using Performance.Services.ExtractExcelService;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace Performance.Api.Controllers
{
    [Route("api/[controller]")]
    public class TemplateController : Controller
    {
        private readonly ILogger logger;
        private readonly IHostingEnvironment env;
        private readonly ClaimService claim;
        private readonly WebapiUrl url;
        private readonly Application application;
        private readonly TemplateService templateService;
        private readonly ExtractIncomeService extractIncomeService;
        private readonly ExConfigService configService;
        private readonly HospitalService hospitalService;
        private readonly AllotService allotService;
        private readonly LogManageService logService;
        private readonly IServiceScopeFactory serviceScopeFactory;

        public TemplateController(
            ILogger<ExceptionsFilter> logger,
            IHostingEnvironment env,
            ClaimService claim,
            IOptions<WebapiUrl> url,
            IOptions<Application> options,
            TemplateService templateService,
            ExtractIncomeService extractIncomeService,
            ExConfigService configService,
            HospitalService hospitalService,
            AllotService allotService,
            LogManageService logService,
            IServiceScopeFactory serviceScopeFactory)
        {
            this.logger = logger;
            this.env = env;
            this.claim = claim;
            this.url = url.Value;
            this.application = options.Value;
            this.templateService = templateService;
            this.extractIncomeService = extractIncomeService;
            this.configService = configService;
            this.hospitalService = hospitalService;
            this.allotService = allotService;
            this.logService = logService;
            this.serviceScopeFactory = serviceScopeFactory;
        }

        /// <summary>
        /// 从WebAPI下载模板 1、医院绩效模板  2、医院二次分配绩效模板 3、医院绩效模板(无执行科室)
        /// </summary>
        /// <returns></returns>
        [Route("downtemplate")]
        [HttpGet]
        [AllowAnonymous]
        public IActionResult DownFile(int type = 1)
        {
            string path = "";
            switch (type)
            {
                case 1:
                    path = Path.Combine(env.ContentRootPath, "Template", "医院绩效模板.xlsx");
                    break;

                case 2:
                    path = Path.Combine(env.ContentRootPath, "Template", "医院二次分配绩效模板.xlsx");
                    break;

                case 3:
                    path = Path.Combine(env.ContentRootPath, "Template", "医院绩效模板(无执行科室).xlsx");
                    break;

                case 4:
                    path = Path.Combine(env.ContentRootPath, "Template", "医院人员绩效模板.xlsx");
                    break;

                case 5:
                    path = Path.Combine(env.ContentRootPath, "Template", "工作量数据导入模板.xlsx");
                    break;

                case 6:
                    path = Path.Combine(env.ContentRootPath, "Template", "导入数据模板.xlsx");
                    break;
            }

            var memoryStream = new MemoryStream();
            using (var stream = new FileStream(path, FileMode.Open))
            {
                stream.CopyToAsync(memoryStream).Wait();
            }
            memoryStream.Seek(0, SeekOrigin.Begin);
            var provider = new FileExtensionContentTypeProvider();

            var memi = provider.Mappings[".xlsx"];
            return File(memoryStream, memi, Path.GetFileName(path));
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="form"></param>
        /// <returns></returns>
        [Route("import")]
        [HttpPost]
        public ApiResponse Import([FromForm] IFormCollection form)
        {
            var userId = claim.GetUserId();
            var hospitalid = form.ToDictionary().GetValue("hospitalid", 0);
            if (hospitalid <= 0)
                return new ApiResponse(ResponseType.Fail, "参数错误", "hospitalid无效");

            var file = ((FormFileCollection)form.Files).FirstOrDefault();
            if (file == null)
                return new ApiResponse(ResponseType.Fail, "参数错误", "文件无效");

            if (!ExtractHelper.IsXlsxFile(file.FileName))
                return new ApiResponse(ResponseType.Fail, "文件格式错误", "文件暂只支持xlsx文件");

            var hospital = hospitalService.GetHopital(hospitalid);
            if (hospital == null)
                return new ApiResponse(ResponseType.Fail, "hospitalid不存在");

            var name = FileHelper.GetFileNameNoExtension(file.FileName) + DateTime.Now.ToString("yyyyMMddHHmmssfff");
            var ext = FileHelper.GetExtension(file.FileName);
            var dpath = Path.Combine(env.ContentRootPath, "Files", $"{hospitalid}", "first");
            FileHelper.CreateDirectory(dpath);

            var path = Path.Combine(dpath, $"{name}{ext}");

            using (var stream = file.OpenReadStream())
            {
                byte[] bytes = new byte[stream.Length];
                stream.Read(bytes, 0, bytes.Length);
                if (!FileHelper.CreateFile(path, bytes))
                    return new ApiResponse(ResponseType.Fail, $"{file.FileName}上传失败");
                var template = new per_first
                {
                    HospitalId = hospitalid,
                    CreateUser = userId,
                    CreateDate = DateTime.Now,
                    Path = path,
                    UploadDate = DateTime.Now,
                    Remark = "上传成功"
                };
                if (templateService.InsertFirst(template))
                {
                    templateService.SendEmail(application.Receiver.ToList(), path, $"{hospital.HosName}首次上传模板", "上传成功");
                }
            }
            return new ApiResponse(ResponseType.OK);
        }

        #region 新版提取

        /// <summary>
        /// 提取前检查
        /// </summary>
        /// <returns></returns>
        [HttpPost("Prejudge/{allotId}")]
        public ApiResponse Prejudge([FromRoute] int allotId)
        {
            string filePath = configService.LastAllotFilePath(allotId);
            if (!string.IsNullOrEmpty(filePath) && FileHelper.IsExistFile(filePath))
            {
                var data = configService.CheckHasNewDepartmentOrCategory(allotId);
                return new ApiResponse(ResponseType.OK, new { haserror = (data != null && data.Any()), data });
            }
            return new ApiResponse(ResponseType.OK, new
            {
                haserror = true,
                data = new Dictionary<string, string> { { "未发现历史文件，是否直接上传", "" } }
            });
        }

        /// <summary>
        /// 提取绩效数据
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("NewExtractData")]
        [HttpPost]
        public ApiResponse NewExtractData([CustomizeValidator, FromBody] ExtractRequest request)
        {
            var allot = allotService.GetAllot(request.AllotId);
            if (allot == null)
                return new ApiResponse(ResponseType.ParameterError, "AllotID错误");
            // 判断是那种抽取
            try
            {
                bool isSingle = false;
                string message = configService.Judge(request.AllotId, request.HospitalId, request.UseScheme, ref isSingle, out string filePath);
                if (!string.IsNullOrEmpty(message))
                    return new ApiResponse(ResponseType.Fail, message);

                //if (!string.IsNullOrEmpty(filePath))
                //{
                //    var data = configService.CheckHasNewDepartmentOrCategory(request.AllotId);
                //    return new ApiResponse(ResponseType.Fail, data);
                //}

                //检验科室、费用类型是否需要补充

                allot.IsExtracting = allot.IsExtracting ?? 0;
                if (allot.IsExtracting == 1)
                    return new ApiResponse(ResponseType.OK, "正在提取数据，请稍等！", new { IsExtracting = true });
                allot.IsExtracting = 1;
                allotService.Update(allot);
                string email = claim.GetUserClaim(JwtClaimTypes.Mail);

                if (isSingle)
                {
                    Task.Run(() =>
                    {
                        using (var scope = serviceScopeFactory.CreateScope())
                        {
                            var scopedServices = scope.ServiceProvider.GetRequiredService<ExtractService>();
                            string extractFilePath = scopedServices.Main(allot.ID, allot.HospitalId, email, "User" + claim.GetUserId(), filePath, isSingle);
                        }
                    });
                }
                else
                {
                    var http = new RestSharpHelper();
                    var extractUrl = http.SetUrl(url.HttpPost, "extract/extract");

                    var obj = new ExtractRequest
                    {
                        AllotId = request.AllotId,
                        HospitalId = request.HospitalId,
                        Email = email,
                        UserId = claim.GetUserId()
                    };

                    string json = JsonHelper.Serialize(obj);
                    logger.LogInformation("提取绩效数据参数:" + json);
                    var parameter = JsonHelper.Deserialize<Dictionary<string, object>>(json);
                    var restRequest = string.IsNullOrEmpty(filePath) ? http.CreatePostRequest(json) : http.CreateFileRequest(new string[] { filePath }, parameter);
                    Task.Run(() => http.GetResponse(extractUrl, restRequest));
                }

                return new ApiResponse(ResponseType.OK, "HIS绩效数据提取任务正在执行，稍后我们将以邮件的通知您！", new { IsExtracting = false });
            }
            catch (Exception ex)
            {
                if (allot != null)
                {
                    allot.IsExtracting = 3;
                    allotService.Update(allot);
                }
                logger.LogError("提取绩效数据:" + ex.ToString());
                throw new Exception(ex.Message);
            }
            // A 使用上传绩效作为模板
            // A-1 判断上传绩效是否存在，并执行成功
            // A-2 医院人员名单、1.0.1 额外收入（写出列头）、2.1 成本支出统计表（写出列头）、4.1 临床科室医护绩效测算表、4.2 特殊核算单元绩效测算表（数量、考核得分率、奖罚、其他）
            // A-3 收入 根据收入配置sheet名称获取抽取SQL，执行填充结果
            // A-4 工作量 根据配置项获取抽取SQL，执行填充结果

            // B 使用配置作为模板
        }

        #endregion 新版提取

        /// <summary>
        /// 从WebAPI下载文件
        /// </summary>
        /// <returns></returns>
        [Route("down")]
        [AllowAnonymous]
        [HttpGet]
        public IActionResult DownFile([FromQuery] AllotRequest request)
        {
            var allot = allotService.GetAllot(request.ID);
            if (allot == null || string.IsNullOrWhiteSpace(allot.ExtractPath) || !FileHelper.IsExistFile(allot.ExtractPath))
            {
                return new ObjectResult(new ApiResponse(ResponseType.Fail, "文件不存在"));
            }

            var memoryStream = new MemoryStream();
            using (var stream = new FileStream(allot.ExtractPath, FileMode.Open))
            {
                stream.CopyToAsync(memoryStream).Wait();
            }
            memoryStream.Seek(0, SeekOrigin.Begin);
            string fileExt = Path.GetExtension(allot.ExtractPath);
            var provider = new FileExtensionContentTypeProvider();
            var memi = provider.Mappings[fileExt];
            return File(memoryStream, memi, Path.GetFileName(allot.ExtractPath));
        }

        /// <summary>
        /// 保存提取文件
        /// </summary>
        /// <returns></returns>
        [Route("savefile")]
        [HttpPost]
        [AllowAnonymous]
        public ApiResponse SaveFile()
        {
            string json = JsonHelper.Serialize(Request.Form.ToDictionary());
            var request = JsonHelper.Deserialize<ExtractRequest>(json);

            if (Request.Form.Files == null || !Request.Form.Files.Any())
            {
                logger.LogError($"返回文件为空！");
                return new ApiResponse(ResponseType.Error, "上传文件无效");
            }

            logger.LogInformation("保存提取文件请求参数：" + json);
            try
            {
                var file = Request.Form.Files[0];
                var dpath = Path.Combine(env.ContentRootPath, "Files", $"{request.HospitalId}", "autoextract");
                FileHelper.CreateDirectory(dpath);

                var path = Path.Combine(dpath, FileHelper.GetFileName(file.FileName));
                logger.LogInformation($"保存提取文件保存路径：" + path);

                using (var stream = file.OpenReadStream())
                {
                    byte[] bytes = new byte[stream.Length];
                    stream.Read(bytes, 0, bytes.Length);
                    if (!FileHelper.CreateFile(path, bytes))
                    {
                        logger.LogError($"保存提取文件保存失败");
                        return new ApiResponse(ResponseType.Error, "保存失败");
                    }
                }
                var allot = allotService.GetAllot(request.AllotId);
                allot.ExtractPath = path;
                allot.IsExtracting = 2;

                string success = "上传成功！";
                string message = (string.IsNullOrEmpty(path) || !FileHelper.IsExistFile(path))
                    ? "保存提取文件文件未保存成功，保存文件不存在！"
                    : (!allotService.Update(allot)) ? "保存提取文件更新文件路径失败！" : success;

                if (message == success)
                    return new ApiResponse(ResponseType.OK, message);
                else
                    return new ApiResponse(ResponseType.Fail, message);
            }
            catch (Exception ex)
            {
                logger.LogError($"保存提取文件保存失败：" + ex.ToString());
                return new ApiResponse(ResponseType.Error, ex.Message);
            }
        }

        /// <summary>
        /// 返回日志
        /// </summary>
        /// <param name="request"></param>
        [Route("returnlog")]
        [HttpPost]
        [AllowAnonymous]
        public void ReturnLog([FromBody] SignalrLogRequest request)
        {
            logger.LogInformation("返回日志：" + JsonHelper.Serialize(request));
            if (request.Type == 3)
            {
                logService.Schedule(request.GroupName, ConvertHelper.To<decimal>(request.Message), request.Level);
            }
            else
            {
                logService.ExtractLog(request.GroupName, request.Tag, request.Message, request.Level);
            }
        }

        /// <summary>
        /// 返回日志
        /// </summary>
        [Route("extractlog")]
        [HttpPost]
        public ApiResponse ReturnLog([FromBody] log_dbug request)
        {
            var allot = allotService.GetAllot(request.AllotID.Value);
            if (null == allot)
                throw new PerformanceException("当前绩效记录不存在");
            var list = allotService.AllotLog(allot, 2);
            return new ApiResponse(ResponseType.OK, list);
        }

        /// <summary>
        /// 返回日志
        /// </summary>
        [Route("schedule")]
        [HttpPost]
        public ApiResponse Schedule([FromBody] log_dbug request)
        {
            var allot = allotService.GetAllot(request.AllotID.Value);
            if (null == allot)
                throw new PerformanceException("当前绩效记录不存在");
            var ratio = allotService.AllotLog(allot, 3)?.Max(t => ConvertHelper.TryDecimal(t.Message)) ?? 0;
            return new ApiResponse(ResponseType.OK, new { ratio });
        }

        [Route("extract/income/{allotId}")]
        [AllowAnonymous]
        [HttpGet]
        public IActionResult ExtractIncome(int allotId)
        {
            string filepath = extractIncomeService.Execture(allotId);

            var memoryStream = new MemoryStream();
            using (var stream = new FileStream(filepath, FileMode.Open))
            {
                stream.CopyToAsync(memoryStream).Wait();
            }
            memoryStream.Seek(0, SeekOrigin.Begin);
            string fileExt = Path.GetExtension(filepath);
            var provider = new FileExtensionContentTypeProvider();
            var memi = provider.Mappings[fileExt];
            return File(memoryStream, memi, Path.GetFileName(filepath));
        }
    }
}
