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 Performance.DtoModels;
using Performance.Infrastructure;
using Performance.Services;
using Performance.Services.AllotCompute;
using Performance.Services.ExtractExcelService;
using Performance.Services.Queues;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Web;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace Performance.Api.Controllers
{
    [Route("api/[controller]")]
    public class AllotController : Controller
    {
        private AllotService _allotService;
        private ResultComputeService _resultComputeService;
        private ConfigService _configService;
        private IHostingEnvironment _evn;
        private ILogger<AllotController> _logger;
        private ClaimService _claim;
        private LogManageService _logManageService;
        private IBackgroundTaskQueue _backgroundTaskQueue;
        private IServiceScopeFactory _serviceScopeFactory;

        public AllotController(AllotService allotService,
            ResultComputeService resultComputeService,
            ConfigService configService,
            ILogger<AllotController> logger,
            IHostingEnvironment evn,
            IBackgroundTaskQueue backgroundTaskQueue,
            IServiceScopeFactory serviceScopeFactory,
            ClaimService claim,
            LogManageService logManageService)
        {
            _allotService = allotService;
            _resultComputeService = resultComputeService;
            _logger = logger;
            _evn = evn;
            _claim = claim;
            _logManageService = logManageService;
            _configService = configService;
            _backgroundTaskQueue = backgroundTaskQueue;
            _serviceScopeFactory = serviceScopeFactory;
        }

        /// <summary>
        /// 绩效列表
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("list")]
        [HttpPost]
        public ApiResponse List([FromBody] AllotRequest request)
        {
            List<AllotResponse> allots = _allotService.GetAllotList(request.HospitalId);
            return new ApiResponse(ResponseType.OK, allots);
        }

        /// <summary>
        /// 生成成功或归档绩效记录
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("list/success")]
        [HttpPost]
        public ApiResponse Success([FromBody] AllotRequest request)
        {
            List<AllotResponse> allots = _allotService.GetSuccAllotList(request.HospitalId);
            return new ApiResponse(ResponseType.OK, allots);
        }

        /// <summary>
        /// 新增绩效
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("insert")]
        [HttpPost]
        public ApiResponse Insert([CustomizeValidator(RuleSet = "Insert"), FromBody] AllotRequest request)
        {
            var userId = _claim.GetUserId();
            var result = _allotService.InsertAllot(request, userId);
            _configService.Copy(result);
            return new ApiResponse(ResponseType.OK, result);
        }

        /// <summary>
        /// 修改绩效
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("update")]
        [HttpPost]
        public ApiResponse<AllotResponse> Update([CustomizeValidator(RuleSet = "Update"), FromBody] AllotRequest request)
        {
            var result = _allotService.UpdateAllot(request);
            return new ApiResponse<AllotResponse>(ResponseType.OK, result);
        }

        /// <summary>
        /// 删除绩效
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("delete")]
        [HttpPost]
        public ApiResponse Delete([CustomizeValidator(RuleSet = "Delete"), FromBody] AllotRequest request)
        {
            bool result = _allotService.DeleteAllot(request.ID);
            return new ApiResponse(ResponseType.OK);
        }

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

            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 allot = _allotService.GetAllot(allotid);
            if (allot == null)
                return new ApiResponse(ResponseType.Fail, "allotid不存在");

            var name = FileHelper.GetFileNameNoExtension(file.FileName) + DateTime.Now.ToString("yyyyMMddHHmmssfff");
            var ext = FileHelper.GetExtension(file.FileName);

            var dpath = Path.Combine(_evn.ContentRootPath, "Files", $"{allot.HospitalId}", $"{allot.Year}{allot.Month.ToString().PadLeft(2, '0')}");
            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}上传失败");
                allot.Path = path;
                allot.States = (int)AllotStates.FileUploaded;
                allot.Remark = EnumHelper.GetDescription(AllotStates.FileUploaded);
                allot.UploadDate = DateTime.Now;
                allot.Generate = (int)AllotGenerate.Init;
                if (!_allotService.Update(allot))
                    return new ApiResponse(ResponseType.Fail, $"{file.FileName}上传成功，修改状态失败");
                _configService.Clear(allot.ID);
            }

            //var email = _claim.GetUserClaim(JwtClaimTypes.Mail);
            //if (allot.States == (int)AllotStates.FileUploaded)
            //    _backgroundTaskQueue.QueueBackgroundWorkItem(async token =>
            //    {
            //        using (var scope = _serviceScopeFactory.CreateScope())
            //        {
            //            var scopedServices = scope.ServiceProvider.GetRequiredService<AllotService>();
            //            scopedServices.Generate(allot, email);
            //            await Task.Delay(TimeSpan.FromSeconds(5), token);
            //        }
            //    });

            return new ApiResponse(ResponseType.OK, true);
        }


        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="allotId"></param>
        /// <returns></returns>
        [Route("ImportExtraction/{allotId}")]
        [HttpPost]
        public ApiResponse ImportExtraction(int allotId)
        {
            var allot = _allotService.GetAllot(allotId);
            if (allot == null)
                return new ApiResponse(ResponseType.Fail, "allotid不存在");

            var extract = allot.ExtractPath.Split("\\").Last();
            var fileName = System.Text.RegularExpressions.Regex.Replace(extract, @"\d", "");
            //var file = ((FormFileCollection)allot.ExtractPath).FirstOrDefault();
            //if (file == null)
            //    return new ApiResponse(ResponseType.Fail, "参数错误", "文件无效");

            var name = FileHelper.GetFileNameNoExtension(fileName) + DateTime.Now.ToString("yyyyMMddHHmmssfff");
            var ext = FileHelper.GetExtension(fileName);
            var dpath = Path.Combine(_evn.ContentRootPath, "Files", $"{allot.HospitalId}", $"{allot.Year}{allot.Month.ToString().PadLeft(2, '0')}");
            FileHelper.CreateDirectory(dpath);

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

            using (var stream = System.IO.File.OpenRead(allot.ExtractPath))
            {
                byte[] bytes = new byte[stream.Length];
                stream.Read(bytes, 0, bytes.Length);
                if (!FileHelper.CreateFile(path, bytes))
                    return new ApiResponse(ResponseType.Fail, $"上传失败");
                allot.Path = path;
                allot.States = (int)AllotStates.FileUploaded;
                allot.Remark = EnumHelper.GetDescription(AllotStates.FileUploaded);
                allot.UploadDate = DateTime.Now;
                allot.Generate = (int)AllotGenerate.Init;
                if (!_allotService.Update(allot))
                    return new ApiResponse(ResponseType.Fail, $"上传成功，修改状态失败");
                _configService.Clear(allot.ID);
            }

            return new ApiResponse(ResponseType.OK);
        }


        /// <summary>
        /// 绩效生成
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("generate")]
        [HttpPost]
        public ApiResponse Generate([CustomizeValidator(RuleSet = "Delete"), FromBody] AllotRequest request)
        {
            var allot = _allotService.GetAllot(request.ID);
            if (null == allot || string.IsNullOrEmpty(allot.Path))
                throw new PerformanceException("当前绩效记录不存在或没有上传数据文件");
            if (allot.States == (int)AllotStates.Wait)
                return new ApiResponse(ResponseType.OK, "当前绩效正在等待生成");

            _logManageService.WriteMsg("生成绩效准备中", $"准备生成{allot.Year}-{allot.Month.ToString().PadLeft(2, '0')}月份绩效，请稍等!", 1, allot.ID, "ReceiveMessage", true);
            _allotService.UpdateAllotStates(allot.ID, (int)AllotStates.Wait, EnumHelper.GetDescription(AllotStates.Wait), allot.Generate);
            if (_evn.IsEnvironment("Localhost"))
            {
                _allotService.Generate(allot);
            }
            else
            {
                //BackgroundJob.Schedule(() => _allotService.Generate(allot, email), TimeSpan.FromSeconds(1));

                _backgroundTaskQueue.QueueBackgroundWorkItem(async token =>
                {
                    using (var scope = _serviceScopeFactory.CreateScope())
                    {
                        var scopedServices = scope.ServiceProvider.GetRequiredService<AllotService>();
                        scopedServices.Generate(allot);
                        await Task.Delay(TimeSpan.FromSeconds(5), token);
                    }
                });
            }

            _logManageService.WriteMsg("等待绩效生成", $"等待绩效生成{allot.Year}-{allot.Month.ToString().PadLeft(2, '0')}月份绩效!", 1, allot.ID, "ReceiveMessage");
            //_allotService.Generate(allot, email);
            ////BackgroundJob.Enqueue(() => _allotService.Generate(allot, email));
            return new ApiResponse(ResponseType.OK);
        }


        /// <summary>
        /// 绩效生成报表
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("genreport")]
        [HttpPost]
        public ApiResponse GenerateReport([CustomizeValidator(RuleSet = "Delete"), FromBody] AllotRequest request)
        {
            var allot = _allotService.GetAllot(request.ID);
            var states = new[] { (int)AllotStates.Archive, (int)AllotStates.GenerateSucceed, (int)AllotStates.GenerateAccomplish };
            if (null == allot || !states.Contains(allot.States))
                throw new PerformanceException("当前绩效暂未生成，无法统计报表数据。");

            _backgroundTaskQueue.QueueBackgroundWorkItem(async token =>
            {
                using (var scope = _serviceScopeFactory.CreateScope())
                {
                    var scopedServices = scope.ServiceProvider.GetRequiredService<AllotService>();
                    scopedServices.GenerateReport(allot);
                    await Task.Delay(TimeSpan.FromSeconds(5), token);
                }
            });
            return new ApiResponse(ResponseType.OK, "统计报表数据任务开始");
        }

        /// <summary>
        /// 验证科室核算单元、工号
        /// </summary>
        /// <param name="allotId"></param>
        /// <returns></returns>
        [Route("accounting/verify/{allotId}")]
        [HttpPost]
        public ApiResponse AccountingVerify([FromRoute] int allotId)
        {
            _backgroundTaskQueue.QueueBackgroundWorkItem(async token =>
            {
                using (var scope = _serviceScopeFactory.CreateScope())
                {
                    var scopedServices = scope.ServiceProvider.GetRequiredService<AllotService>();
                    scopedServices.AccoungtingVerify(allotId);
                    await Task.Delay(TimeSpan.FromSeconds(5), token);
                }
            });
            return new ApiResponse(ResponseType.OK, "核算单元及组别数据验证任务开始");
        }

        /*
        [Route("recalculation")]
        [HttpPost]
        public ApiResponse Recalculation([FromBody] RecalculationRequest request)
        {
            if (request.Money.HasValue && request.Money.Value > 0)
                _allotService.Recalculation(request.AllotId, request.Money.Value);
            return new ApiResponse(ResponseType.OK);
        }
        */

        /// <summary>
        /// 重新计算院领导绩效
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("recalculation")]
        [HttpPost]
        public ApiResponse Recalculation([FromBody] RecalculationRequest request)
        {
            if (request.Money.HasValue)
                _allotService.Recalculation(request.AllotId, request.Money.Value);
            return new ApiResponse(ResponseType.OK);
        }

        /// <summary>
        /// 归档绩效记录
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("pigeonhole")]
        [HttpPost]
        public ApiResponse Pigeonhole([CustomizeValidator(RuleSet = "Delete"), FromBody] AllotRequest request)
        {
            var allot = _allotService.GetAllot(request.ID);
            if (null == allot)
                throw new PerformanceException("当前绩效记录不存在或没有上传数据文件");
            _allotService.Pigeonhole(allot);
            return new ApiResponse(ResponseType.OK);
        }

        /// <summary>
        /// 归档时检索数据是否合格
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("checkrecord")]
        [HttpPost]
        public ApiResponse CheckRecord([CustomizeValidator(RuleSet = "Delete"), FromBody] AllotRequest request)
        {
            var allot = _allotService.GetAllot(request.ID);
            if (null == allot)
                throw new PerformanceException("当前绩效记录不存在或没有上传数据文件");
            var list = _allotService.GetAgainAllotNotSucceed(allot);
            return new ApiResponse(ResponseType.OK, list);
        }

        /// <summary>
        /// 绩效校验结果
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("allotcheckresult")]
        [HttpPost]
        public ApiResponse AllotCheckResult([CustomizeValidator(RuleSet = "Delete"), FromBody] AllotRequest request)
        {
            var allot = _allotService.GetAllot(request.ID);
            if (null == allot)
                throw new PerformanceException("当前绩效记录不存在");
            var list = _allotService.AllotCheckResult(allot);
            return new ApiResponse(ResponseType.OK, list);
        }

        /// <summary>
        /// 绩效历史日志
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("allotlog")]
        [HttpPost]
        public ApiResponse AllotLog([CustomizeValidator(RuleSet = "Delete"), FromBody] AllotRequest request)
        {
            var allot = _allotService.GetAllot(request.ID);
            if (null == allot)
                throw new PerformanceException("当前绩效记录不存在");
            var list = _allotService.AllotLog(allot, 1);
            return new ApiResponse(ResponseType.OK, list);
        }

        /// <summary>
        /// 绩效审核通过，绩效下发
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("issued")]
        [HttpPost]
        public ApiResponse Issued([FromBody] AllotRequest request)
        {
            var allot = _allotService.GetAllot(request.ID);
            if (null == allot)
                throw new PerformanceException("当前绩效记录不存在");
            _allotService.UpdateAllotStates(allot.ID, (int)AllotStates.GenerateSucceed, EnumHelper.GetDescription(AllotStates.GenerateSucceed));
            // 科室下发
            _resultComputeService.GenerateSecondAllot(allot);
            return new ApiResponse(ResponseType.OK);
        }

        /// <summary>
        /// 绩效详情计算公式显示/隐藏
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [Route("update/showformula")]
        [HttpPost]
        public ApiResponse UpdateAllotShowFormula([FromBody] AllotRequest request)
        {
            if (request.ID < 1)
                return new ApiResponse(ResponseType.ParameterError, "绩效信息无效");
            var result = _allotService.UpdateAllotShowFormula(request.ID);
            return new ApiResponse(ResponseType.OK, result);
        }

        /// <summary>
        /// 获取
        /// </summary>
        /// <returns></returns>
        [Route("reserved")]
        [HttpPost]
        public ApiResponse Reserved([FromBody] ReservedRequest request)
        {
            if (request.HospitalId < 1)
                return new ApiResponse(ResponseType.ParameterError, "绩效信息无效");

            var userid = _claim.GetUserId();

            var reserveds = _allotService.GetReserved(request.HospitalId, request.Year, userid);

            #region 格式转换

            var result = reserveds?.Select(w => new
            {
                w.HospitalId,
                w.Year,
                w.UnitType,
                w.AccountingUnit,
                w.EmployeeName,
                w.JobNumber,

                JanReseFee = w.JanFee * w.JanRatio,
                JanGiveFee = w.JanFee * (1 - w.JanRatio),

                FebReseFee = w.FebFee * w.FebRatio,
                FebGiveFee = w.FebFee * (1 - w.FebRatio),

                MarReseFee = w.MarFee * w.MarRatio,
                MarGiveFee = w.MarFee * (1 - w.MarRatio),

                AprReseFee = w.AprFee * w.AprRatio,
                AprGiveFee = w.AprFee * (1 - w.AprRatio),

                MayReseFee = w.MayFee * w.MayRatio,
                MayGiveFee = w.MayFee * (1 - w.MayRatio),

                JunReseFee = w.JunFee * w.JunRatio,
                JunGiveFee = w.JunFee * (1 - w.JunRatio),

                JulReseFee = w.JulFee * w.JulRatio,
                JulGiveFee = w.JulFee * (1 - w.JulRatio),

                AugReseFee = w.AugFee * w.AugRatio,
                AugGiveFee = w.AugFee * (1 - w.AugRatio),

                SepReseFee = w.SepFee * w.SepRatio,
                SepGiveFee = w.SepFee * (1 - w.SepRatio),

                OctReseFee = w.OctFee * w.OctRatio,
                OctGiveFee = w.OctFee * (1 - w.OctRatio),

                NovReseFee = w.NovFee * w.NovRatio,
                NovGiveFee = w.NovFee * (1 - w.NovRatio),

                DecReseFee = w.DecFee * w.DecRatio,
                DecGiveFee = w.DecFee * (1 - w.DecRatio),

                TotalReseFee = (w.JanFee * w.JanRatio ?? 0) + (w.FebFee * w.FebRatio ?? 0) + (w.MarFee * w.MarRatio ?? 0)
                             + (w.AprFee * w.AprRatio ?? 0) + (w.MayFee * w.MayRatio ?? 0) + (w.JunFee * w.JunRatio ?? 0)
                             + (w.JulFee * w.JulRatio ?? 0) + (w.AugFee * w.AugRatio ?? 0) + (w.SepFee * w.SepRatio ?? 0)
                             + (w.OctFee * w.OctRatio ?? 0) + (w.NovFee * w.NovRatio ?? 0) + (w.DecFee * w.DecRatio ?? 0),

                TotalGiveFee = (w.JanFee * (1 - w.JanRatio) ?? 0) + (w.FebFee * (1 - w.FebRatio) ?? 0) + (w.MarFee * (1 - w.MarRatio) ?? 0)
                             + (w.AprFee * (1 - w.AprRatio) ?? 0) + (w.MayFee * (1 - w.MayRatio) ?? 0) + (w.JunFee * (1 - w.JunRatio) ?? 0)
                             + (w.JulFee * (1 - w.JulRatio) ?? 0) + (w.AugFee * (1 - w.AugRatio) ?? 0) + (w.SepFee * (1 - w.SepRatio) ?? 0)
                             + (w.OctFee * (1 - w.OctRatio) ?? 0) + (w.NovFee * (1 - w.NovRatio) ?? 0) + (w.DecFee * (1 - w.DecRatio) ?? 0),
            });

            #endregion 格式转换

            return new ApiResponse(ResponseType.OK, result);
        }

        /// <summary>
        /// 下载当前测算表
        /// </summary>
        /// <returns></returns>
        [Route("current/download")]
        [HttpPost]
        [AllowAnonymous]
        public IActionResult DownloadCurrentCalculationTable([FromBody] AllotRequest request)
        {
            var allot = _allotService.GetAllot(request.ID);
            if (null == allot)
                throw new PerformanceException("当前测算表不存在");

            var memoryStream = new MemoryStream();
            using (var stream = new FileStream(allot.Path, FileMode.Open))
            {
                stream.CopyToAsync(memoryStream).Wait();
            }
            memoryStream.Seek(0, SeekOrigin.Begin);
            var provider = new FileExtensionContentTypeProvider();
            FileInfo fileInfo = new FileInfo(allot.Path);
            var memi = provider.Mappings[".xlsx"];
            return File(memoryStream, memi, Path.GetFileName(fileInfo.Name));
        }
    }
}
