using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Siger.ApiCNC.Result;
using Siger.ApiCommon.Filters;
using Siger.ApiCommon.Result;
using Siger.ApiCommon.Utilities;
using Siger.Middlelayer.CncRepository.Repositories.Interface;
using Siger.Middlelayer.CncRepository.Response;
using Siger.Middlelayer.Common;
using Siger.Middlelayer.Common.AppSettings;
using Siger.Middlelayer.Common.Extensions;
using Siger.Middlelayer.Common.Helpers;
using Siger.Middlelayer.Common.ModuleEnum;
using Siger.Middlelayer.Dapper;
using Siger.Middlelayer.Dapper.Utilities.Oee;
using Siger.Middlelayer.Log;
using Siger.Middlelayer.Repository.Repositories.Interface;
using Siger.Middlelayer.Utility.Helpers;
using Siger.Middlelayer.Utility.ImportEntities;

namespace Siger.ApiCNC.Controllers
{
    public class WorkOrderController : BaseController
    {
        private readonly ISigerProjectProductReport _productionReportRepository;
        private readonly ISigerProjectMachineRepository _machineRepository;
        private readonly IProductionTimeRepository _timeRepository;
        private readonly IProductPlanDetailRepository _planDetailRepository;
        private readonly IProductPlanRepository _planRepository;
        private readonly IProductionBeatSetRepository _productionBeatSetRepository;
        private readonly ISigerProjectProductRepository _productRepository;
        private readonly IProductionBeatSetRepository _beatSetRepository;
        private readonly IProductionTimeAllocationRepository _timeAllocationRepository;

        public WorkOrderController(ISigerProjectProductReport productionReportRepository, ISigerProjectMachineRepository machineRepository,
            IProductionTimeRepository timeRepository, IProductPlanDetailRepository planDetailRepository, IProductPlanRepository planRepository
            , IProductionBeatSetRepository productionBeatSetRepository, ISigerProjectProductRepository productRepository, IProductionTimeAllocationRepository timeAllocationRepository,
            IProductionBeatSetRepository beatSetRepository)
        {
            _productionReportRepository = productionReportRepository;
            _machineRepository = machineRepository;
            _timeRepository = timeRepository;
            _planRepository = planRepository;
            _planDetailRepository = planDetailRepository;
            _productionBeatSetRepository = productionBeatSetRepository;
            _productRepository = productRepository;
            _beatSetRepository = beatSetRepository;
            _timeAllocationRepository = timeAllocationRepository;
        }

        /// <summary>
        /// 获取当前设备OEE和质量合格率
        /// </summary>
        /// <param name="sectionId"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> GetMachineReportOee(int sectionId)
        {
            var stime = UnixTimeHelper.GetUnixByDate(DateTime.Now.ToShortDateString());
            var etime = UnixTimeHelper.GetUnixByDate(DateTime.Now.ToString());

            var machineId = _machineRepository.GetNCMahcineIdBySection(sectionId);

            var query = _productionReportRepository.GetList(q => q.projectid == ProjectId && q.time >= stime &&
                                                                 q.time < etime && q.machineid == machineId);
            var output = 0;
            var nokNumber = 0;
            foreach (var report in query.ToList())
            {
                output += report.actual_output;
                nokNumber += report.nok_number;
            }

            var result = new GetMachineStateResult();

            var total = output + nokNumber;
            result.quantityRate = total == 0 ? "100" : Math.Round((double)output / total * 100, 2).ToString();

            //oee
            var repositoey = new SliceSateRepository(CompanyId, ProjectId);
            var sliceSates = await repositoey.GetCncSliceSatesAsync(new List<int> { machineId }, DateTime.Now.ToString(ParameterConstant.DateFormat), DateTime.Now.ToString(ParameterConstant.DateTimeFormat));

            //var times = _timeRepository.GetRestTimesByMachine(new List<int> { machineId }, ProjectId);
            var times = _timeAllocationRepository.GetRestTimesByMachine(new List<int> { machineId }, ProjectId);
            var reports = _productionBeatSetRepository.GetProductReport(stime, etime, new List<int> { machineId }, ProjectId);

            var oeeEntity = OeeManager.GetOeeByTime(DateTime.Parse(DateTime.Now.ToShortDateString()), DateTime.Now,
                sliceSates, new List<int> { machineId }, reports, times);
            result.oee = oeeEntity.OEE.ToString();

            return new ObjectResult(result);
        }

        /// <summary>
        /// 根据工令单获取报工信息
        /// </summary>
        /// <param name="sectionId"></param>
        /// <param name="orderNumber"></param>
        /// <returns></returns>
        [HttpGet]
        public IActionResult GetWorkOrderDetail(int sectionId, string orderNumber)
        {
            var planDetail =
                _planDetailRepository.Get(m => m.orderNumber == orderNumber && m.status == (int)RowState.Valid && m.projectId == ProjectId);
            if (planDetail == null)
            {
                return new ObjectResult(CommonEnum.Fail);
            }

            var machineData = _machineRepository.GetMachineBySectionId(sectionId, ProjectId);
            if (machineData == null)
            {
                throw new BadRequestException(RequestEnum.MachineNotFound);
            }

            //验证工单是否安排在该机器上
            //var beats = _beatSetRepository.GetList(q =>
            //    q.product_name == plan.product_id.ToString() && q.projectID == ProjectId &&
            //    q.status == (int)RowState.Valid && q.route_number != ""
            //    && q.start_time <= DateTime.Now && q.end_time >= DateTime.Now).OrderBy(q => q.route_number.ToInt()).ToList();
            //if (!beats.Select(m => m.machineID).Contains(machineData.id))
            //{
            //    throw new BadRequestException(CncEnum.PlanNotFoundOnMachine);
            //}

            var reports = _productionReportRepository.GetList(q =>
                q.code == orderNumber && q.machineid == machineData.id && q.projectid == ProjectId && q.status == (int)RowState.Valid);

            var plan = _planRepository.Get(m => m.id == planDetail.planId);
            if (plan == null) //计划外报工
            {
                var product = _productRepository.Get(q =>
                    q.id == planDetail.levelId && q.projectid == ProjectId);
                var response = new ResponseGetWorkOrderDetail
                {
                    product_code = product != null ? product.code : "",
                    delivery_time = 0,
                    nok_number = reports.Sum(m => m.nok_number),
                    ok_number = reports.Sum(m => m.actual_output),
                    producted_number = reports.Sum(m => m.nok_number + m.actual_output),
                    orderNumber = orderNumber,
                    product_name = product != null ? product.name : "",
                    quantity = planDetail.quantity,
                    sectionName = machineData.name,
                    machineId = machineData?.id ?? 0,
                    currentUser = "",
                    product_id = planDetail.levelId
                };
                return new ObjectResult(response);
            }
            else
            {
                var response = new ResponseGetWorkOrderDetail
                {
                    product_code = plan.product_code,
                    delivery_time = plan.delivery_time,
                    nok_number = reports.Sum(m => m.nok_number),
                    ok_number = reports.Sum(m => m.actual_output),
                    producted_number = reports.Sum(m => m.nok_number + m.actual_output),
                    orderNumber = orderNumber,
                    product_name = plan.product_name,
                    quantity = planDetail.quantity,
                    sectionName = machineData.name,
                    machineId = machineData?.id ?? 0,
                    currentUser = "",
                    product_id = plan.product_id
                };
                return new ObjectResult(response);
            }
        }

        /// <summary>
        /// 工令单一览
        /// </summary>
        /// <param name="productName"></param>
        /// <param name="code"></param>
        /// <param name="orderNumber"></param>
        /// <param name="status"></param>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <param name="page"></param>
        /// <param name="pagesize"></param>
        /// <returns></returns>
        [HttpGet]
        public IActionResult GetWorkOrderList(string productName, string code, string orderNumber, int status, string startTime, string endTime, int page = 1, int pagesize = 10)
        {
            if (!string.IsNullOrWhiteSpace(startTime))
            {
                var success = DateTime.TryParse(startTime, out var dtStart);
                if (!success)
                {
                    throw new BadRequestException(CncEnum.DateTimeFormatError);
                }
            }
            if (!string.IsNullOrWhiteSpace(endTime))
            {
                var success = DateTime.TryParse(endTime, out var dtEnd);
                if (!success)
                {
                    throw new BadRequestException(CncEnum.DateTimeFormatError);
                }
            }

            var orders = _planDetailRepository.GetPagedWorkOrderList(productName, code, orderNumber, status, startTime,
                endTime, ProjectId, page, pagesize);

            return new PagedObjectResult(orders.Data, orders.Total, page, pagesize);
        }

        [HttpGet]
        public IActionResult GetPlanExecute(string dateTime, string productName, int page = 1, int pagesize = 10, int toexcel = 0)
        {
            var dateTimes = dateTime.Split(" - ");
            if (dateTimes.Length != 2)
            {
                throw new BadRequestException(CncEnum.DateTimeFormatError);
            }

            var success = DateTime.TryParse(dateTimes[0], out var dtStart);
            if (!success)
            {
                throw new BadRequestException(CncEnum.DateTimeFormatError);
            }

            success = DateTime.TryParse(dateTimes[1], out var dtEnd);
            if (!success)
            {
                throw new BadRequestException(CncEnum.DateTimeFormatError);
            }

            var start = (int)UnixTimeHelper.ConvertDataTimeLong(dtStart);
            var end = (int)UnixTimeHelper.ConvertDataTimeLong(dtEnd.AddDays(1).AddSeconds(-1));

            var data = _planDetailRepository.GetPlanExecuteList(start, end, productName.ToInt(), ProjectId, page, pagesize, toexcel);
            if (toexcel == 1)
            {
                if (data.Total == 0)
                {
                    throw new BadRequestException(CommonEnum.NoDataForExport);
                }

                return ExportData(data.Data);
            }

            var plans = _planRepository.GetList(q => q.projectid == ProjectId && q.status != (int)PlanProcess.Stop
                                                                              && q.delivery_time >= start && q.delivery_time <= end);
            if (productName.ToInt() != 0)
            {
                plans = plans.Where(q => q.product_id == productName.ToInt());
            }
            var summary = new List<object>();
            if (!plans.Any())
            {
                summary = new List<object> { 0, 0, 0, 0, 0, 0 };
            }
            else
            {
                double okNumber = plans.Sum(m => m.producted_number); //完成总数
                double nokNumber = plans.Sum(m => m.nok_number); //不合格数量

                double planToal = plans.Sum(m => m.quantity);

                summary.Add(planToal);//计划完成数量
                summary.Add(okNumber + nokNumber); //实际完成数量
                summary.Add(plans.Count()); //计划工单数量
                summary.Add(Math.Round(planToal <= 0 ? 0 : okNumber / planToal * 100, 2).FormatDouble()); // 产品达成率

                //计划达成率
                double finishedPlan = plans.Count(q => q.finish_time > 0 && q.delivery_time >= q.finish_time);
                summary.Add(Math.Round(finishedPlan / plans.Count() * 100, 2).FormatDouble());

                //质量合格率
                summary.Add(okNumber <= 0 ? 0 : Math.Round(okNumber / (okNumber + nokNumber) * 100, 2).FormatDouble());
            }

            var result = new GetPlanExecuteResult
            {
                page = page,
                pagesize = pagesize,
                data = data.Data,
                total = data.Total,
                summary = summary
            };
            return result;
        }

        [HttpGet]
        private IActionResult ExportData(IEnumerable<ResponseGetOrders> responses)
        {
            var rootDir = FileSystemHelper.GetPhysicalFolders(FileSystemHelper.CommonFileSetting.PhysicalFolder, FileSystemHelper.ExportFileName); 
            var temporaryFileName = $"orders_{DateTime.Now:yyyyMMddHHmmss}.xlsx";
            var fileName = Path.Combine(rootDir, temporaryFileName);

            var helper = new EpPlusExcelHelper<WorkOrderList>();
            try
            {
                var machineYields = new List<WorkOrderList>();
                var index = 1;
                foreach (var yield in responses)
                {
                    var machineYield = new WorkOrderList
                    {
                        No = index,
                        OrderCode = yield.code,
                        ProductName = yield.product_name,
                        PlanCount = yield.quantity.ToStr(),
                        FinishCount = yield.producted_number.ToStr(),
                        DeliveryTime = UnixTimeHelper.ConvertIntDateTime(yield.delivery_time),
                        LiftDay = yield.difference.ToStr(),
                        OKNumber = yield.ok_number.ToStr(),
                        NOKNumber = yield.nok_number.ToStr(),
                        Rate = yield.rate.ToStr()
                    };
                    machineYields.Add(machineYield);
                    index++;
                }

                helper.GenerateExcel(machineYields, fileName);
                return new ObjectResult($"{FileSystemHelper.CommonFileSetting.RequestPath}/{FileSystemHelper.ExportFileName}/{temporaryFileName}");
            }
            catch (Exception e)
            {
                Logger.WriteLineError("Export orders failed, error: " + e.Message);
                throw new BadRequestException(RequestEnum.ExportFailed);
            }
            finally
            {
                helper.Dispose();
            }
        }

        /// <summary>
        /// 订单进度一览
        /// </summary>
        /// <param name="productname"></param>
        /// <param name="ordernumber"></param>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        [HttpGet]
        public IActionResult GetWorkOrderProgress(string productname, string ordernumber, string dateTime)
        {
            var dateTimes = dateTime.Split(" - ");
            if (dateTimes.Length != 2)
            {
                throw new BadRequestException(CncEnum.DateTimeFormatError);
            }

            var success = DateTime.TryParse(dateTimes[0], out var dtStart);
            if (!success)
            {
                throw new BadRequestException(CncEnum.DateTimeFormatError);
            }

            success = DateTime.TryParse(dateTimes[1], out var dtEnd);
            if (!success)
            {
                throw new BadRequestException(CncEnum.DateTimeFormatError);
            }

            dtEnd = dtEnd.AddDays(1).AddSeconds(-1);

            var responses = new List<ResponseGetWorkOrderProgress>();

            var orders = _planDetailRepository.GetWorkOrderProgress(productname, ordernumber, dtStart, dtEnd, ProjectId);
            if (!orders.Any())
            {
                return new ObjectResult(responses);
            }

            var ordernumbers = orders.Select(q => q.orderNumber).Distinct().ToList();
            foreach (var order in ordernumbers)
            {
                var workorders = orders.Where(q => q.orderNumber == order).OrderBy(m => m.code).ToList();
                var quantity = workorders.Sum(q => q.quantity);
                var response = new ResponseGetWorkOrderProgress
                {
                    ordernumber = order,
                    total_progress = quantity == 0 ? 0 : Math.Round((double)workorders.Sum(q => q.producted_number) / quantity * 100, 0).FormatDouble(),
                    order_count = workorders.Count()
                };
                foreach (var workorder in workorders)
                {
                    var details = _planDetailRepository.GetList(q => q.planId == workorder.id && q.status == (int)RowState.Valid);
                    response.progresses.Add(new WorkOrderProgress
                    {
                        product_name = workorder.product_name,
                        quantity = workorder.quantity,
                        finished = workorder.producted_number,
                        code = workorder.code,
                        start = details.Any() ? UnixTimeHelper.ConvertIntDate(details.Min(q => q.startTime)) : "",
                        end = details.Any() ? UnixTimeHelper.ConvertIntDate(details.Max(q => q.endTime)) : ""
                    });
                }
                responses.Add(response);
            }

            return new ObjectResult(responses);
        }

        /// <summary>
        /// 根据派工令单号，获取产品信息
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        [HttpGet]
        [NoTokenValidateFilter]
        public IActionResult GetPlanInfo(string code)
        {
            var planDetail = _planDetailRepository.Get(q =>
                 q.orderNumber == code && q.status == (int)RowState.Valid);
            if (planDetail != null)
            {
                var plan = _planRepository.Get(q => q.id == planDetail.planId);
                if (plan != null)
                {
                    var product = new ProductInfo
                    {
                        name = plan.product_name,
                        product_code = plan.product_code,
                        drawingcode = plan.draw_number
                    };
                    return new ObjectResult(product);
                }
            }
            throw new BadRequestException(CommonEnum.RecordNotFound);
        }
    }
}