using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Siger.ApiChangLing.Result;
using Siger.Middlelayer.CncRepository.Repositories.Interface;
using Siger.Middlelayer.CncRepository.Request;
using Siger.Middlelayer.CncRepository.Response;
using Siger.Middlelayer.Common;
using Siger.Middlelayer.Common.Configuration;
using Siger.Middlelayer.Common.Extensions;
using Siger.Middlelayer.Common.Helpers;
using Siger.Middlelayer.Common.ModuleEnum;
using Siger.Middlelayer.Repository.Repositories.Interface;
using Siger.Middlelayer.Repository.Response;
using Siger.Middlelayer.Share.Models;
using Siger.Middlelayer.Share.Utilities;

namespace Siger.ApiChangLing.Controllers
{
    public class SchedulingController : BaseController
    {
        private readonly IProduceScheduleRepository _scheduleRepository;
        private readonly IProductionBeatSetRepository _beatSetRepository;
        private readonly IProductPlanRepository _planRepository;
        private readonly IProductionTimeRepository _timeRepository;
        private readonly ISigerProjectMachineRepository _machineRepository;
        private readonly IProductPlanDetailRepository _planDetailRepository;
        private readonly IProductionAllocationRepository _allocationRepository;
        private readonly string Server = ConfigManager.GetValue("WinAPI", "Server", "");

        public SchedulingController(IProduceScheduleRepository scheduleRepository, IProductionBeatSetRepository beatSetRepository,
            IProductPlanRepository planRepository, IProductionTimeRepository timeRepository, ISigerProjectMachineRepository machineRepository,
            IProductPlanDetailRepository planDetailRepository, IProductionAllocationRepository allocationRepository)
        {
            _scheduleRepository = scheduleRepository;
            _beatSetRepository = beatSetRepository;
            _planRepository = planRepository;
            _timeRepository = timeRepository;
            _machineRepository = machineRepository;
            _planDetailRepository = planDetailRepository;
            _allocationRepository = allocationRepository;
        }

        [HttpGet]
        public IActionResult GetRoutes(int planId, int num)
        {
            var plan = _planRepository.Get(planId);
            if (plan == null)
            {
                throw new BadRequestException(CncEnum.PlanNotFound);
            }

            var responses = new List<ResponseChanglingGetProductRouteInfo>();
            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();

            var routes = beats.Select(m => m.route_number).Distinct().ToList();
            var machineIds = beats.Select(m => m.machineID).Distinct().ToList();
            var machines = _machineRepository.GetList(q => machineIds.Contains(q.id) && q.status == (int)RowState.Valid).ToList();

            for (var i = 0; i < num; i++)
            {
                foreach (var route in routes)
                {
                    var routeBeat = beats.FirstOrDefault(q => q.route_number == route);
                    if (routeBeat == null)
                    {
                        continue;
                    }
                    var response = new ResponseChanglingGetProductRouteInfo
                    {
                        route_name = routeBeat.route_name,
                        route_number = routeBeat.route_number,
                        batch = i
                    };
                    var routeBeats = beats.Where(q => q.route_number == route);
                    foreach (var beatSet in routeBeats)
                    {
                        var machine = machines.FirstOrDefault(q => q.id == beatSet.machineID);
                        if (machine == null)
                        {
                            continue;
                        }

                        response.machines.Add(new MachineBeatset
                        {
                            beat_id = beatSet.id,
                            machine_id = beatSet.machineID,
                            machine_name = machine.title,
                            cycle_time = beatSet.standard_besat
                        });
                    }
                    responses.Add(response);
                }
            }

            return new ObjectResult(responses);
        }

        [HttpPost]
        public IActionResult ExecuteSchedule([FromBody]RequestChanglingExecuteSchedule request)
        {
            if (request.orders == null || !request.orders.Any())
            {
                throw new BadRequestException(CncEnum.RoutesNotFound);
            }
            var plan = _planRepository.Get(request.planId);
            if (plan == null)
            {
                throw new BadRequestException(CncEnum.PlanNotFound);
            }

            if (plan.status != (int)PlanProcess.UnPlan && plan.status != (int)PlanProcess.Stop && plan.status != (int)PlanProcess.HasPlan)
            {
                throw new BadRequestException(CncEnum.PlanHasExist);
            }

            var machineIds = request.orders.Select(m => m.machine_id).Distinct();
            var sections = _machineRepository.GetNCMachinSectionInfos(machineIds, ProjectId);
            var sectionIds = sections.Select(m => m.id).Distinct();

            var times = GetRestTimesBySections(sectionIds).ToList(); //全部的休息时间
            var transfers = _allocationRepository.GetList(m =>
                sectionIds.Contains(m.section_id) && m.status == (int)RowState.Valid && m.projectid == ProjectId).ToList();

            var schedules = _scheduleRepository.GetList(m =>
                machineIds.Contains(m.sectionId) && m.status == (int)RowState.Valid && m.projectId == ProjectId).ToList(); //当前的排产

            var beats = _beatSetRepository.GetList(q =>
                q.product_name == plan.product_id.ToString() && q.projectID == ProjectId &&
                q.status == (int)RowState.Valid && q.route_number != "").OrderBy(q => q.route_number.ToInt()).ToList();

            var allmachineIds = beats.Select(m => m.machineID).Distinct().ToList();
            var machines = _machineRepository.GetList(q => allmachineIds.Contains(q.id) && q.status == (int)RowState.Valid && q.category==(int)MachineCategory.NC).ToList();

            var responses = new List<ResponseChanglingExecuteSchedule>();
            var now = UnixTimeHelper.GetNow();

            var orderNumbers = request.orders.Select(m => m.orderNumber).Distinct();
            foreach (var orderNumber in orderNumbers)
            {
                var orders = request.orders.Where(q => q.orderNumber == orderNumber);
                var dtStart = now;
                foreach (var order in orders)
                {
                    var transfer = transfers.FirstOrDefault(q => q.machine_id == order.machine_id);
                    var response = new ResponseChanglingExecuteSchedule
                    {
                        batch = order.batch,
                        count = order.count,
                        cycleTime = order.cycleTime,
                        orderNumber = order.orderNumber,
                        machine_id = order.machine_id,
                        machine_name = order.machine_name,
                        route_name = order.route_name,
                        route_type = order.route_type,
                        route_number = order.route_number
                    };
                    var routeBeats = beats.Where(q => q.route_number == order.route_number);
                    foreach (var beatSet in routeBeats)
                    {
                        var machine = machines.FirstOrDefault(q => q.id == beatSet.machineID);
                        if (machine == null)
                        {
                            continue;
                        }
                        response.machines.Add(new MachineBeatset
                        {
                            beat_id = beatSet.id,
                            machine_id = beatSet.machineID,
                            machine_name = machine.title,
                            cycle_time = beatSet.standard_besat
                        });
                    }

                    var sectionStartTime = now; //设备最早可用时间

                    //不同的产线出现同一设备的情形
                    var tempOrder = responses.Where(q => q.machine_id == order.machine_id).OrderByDescending(q => q.endTime).FirstOrDefault();
                    if (tempOrder != null)
                    {
                        //dtStart = dtStart > tempOrder.endTime ? dtStart : tempOrder.endTime;
                        dtStart = tempOrder.endTime;
                        sectionStartTime = sectionStartTime > tempOrder.endTime ? sectionStartTime : tempOrder.endTime;
                    }
                    //同一个工令号的结束时间
                    tempOrder = responses.Where(q => q.orderNumber == order.orderNumber).OrderByDescending(q => q.endTime).FirstOrDefault();
                    if (tempOrder != null)
                    {
                        dtStart = dtStart > tempOrder.endTime ? dtStart : tempOrder.endTime;
                        var oos = responses.Where(q => q.orderNumber == tempOrder.orderNumber).OrderBy(q => q.route_number).ToList();
                        var oo = oos.FirstOrDefault(q => q.route_number == tempOrder.route_number);
                        if (oo != null)
                        {
                            var tr = transfers.FirstOrDefault(q => q.machine_id == oo.machine_id);
                            if (tr != null)
                            {
                                dtStart += tr.transfer_time;
                            }
                        }
                    }

                    var lastSchedule = schedules.Where(m => m.sectionId == order.machine_id).OrderByDescending(m => m.endTime).FirstOrDefault();
                    if (lastSchedule != null)
                    {
                        dtStart = dtStart > lastSchedule.endTime ? dtStart : lastSchedule.endTime;
                        sectionStartTime = sectionStartTime > lastSchedule.endTime ? sectionStartTime : lastSchedule.endTime;
                    }

                    //换型时间，该设备前一个工单生产的产品是否与当前的产品相同
                    if (tempOrder == null) //第二个section不需要再加换型时间
                    {
                        var sch = _scheduleRepository.GetList(q => q.sectionId == order.machine_id && q.status == (int)RowState.Valid && q.projectId == ProjectId)
                            .OrderByDescending(q => q.endTime).FirstOrDefault();
                        if (sch != null)
                        {
                            var planDetail = _planDetailRepository.Get(q => q.id == sch.plandetail_id && q.status == (int)RowState.Valid && q.projectId == ProjectId);
                            if (planDetail != null)
                            {
                                var lastPlan = _planRepository.Get(q => q.id == planDetail.planId && q.status == (int)RowState.Valid && q.projectid == ProjectId);
                                if (lastPlan != null)
                                {
                                    if (lastPlan.product_id != plan.product_id)
                                    {
                                        var beat = _beatSetRepository.Get(q => q.projectID == ProjectId && q.machineID == order.machine_id
                                                                     && q.drawing_number == plan.draw_number && q.status == (int)RowState.Valid);
                                        if (beat != null)
                                        {
                                            dtStart += beat.changemodeltime;
                                            sectionStartTime += beat.changemodeltime;
                                        }
                                    }
                                }
                            }
                        }
                    }

                    //工序转移时间
                    double totlaCycleTime = order.cycleTime * order.count;

                    if (transfer != null && transfer.rate > 0)
                    {
                        totlaCycleTime = totlaCycleTime / 100 * transfer.rate;
                    }
                    var total = int.Parse(totlaCycleTime.ToString(CultureInfo.InvariantCulture));

                    var dtEnd = dtStart + total;
                    if (request.excludeRestTime == 1) //是否排除休息时间
                    {
                        var sectionTimes = times.Where(q => q.MachineId == order.machine_id).ToList();
                        RestTimeManager.GetStartTime(sectionTimes, ref dtStart); // 重置开始时间
                        dtEnd = RestTimeManager.GetEndTime(sectionTimes, dtStart, total);
                    }

                    response.sectionStartTime = sectionStartTime;
                    response.startTime = dtStart;
                    response.endTime = order.route_type == 1 ? dtEnd : dtStart;
                    responses.Add(response);

                    dtStart = response.endTime;
                }
            }

            //赋值产线最早和最晚时间
            var result = new List<ResponseChanglingExecuteSchedule>();
            foreach (var response in responses)
            {
                var entity = Mapper<ResponseChanglingExecuteSchedule, ResponseChanglingExecuteSchedule>.Map(response);
                entity.levelStartTime = responses.Where(q => q.orderNumber == response.orderNumber).Min(q => q.startTime);
                entity.levelEndTime = responses.Where(q => q.orderNumber == response.orderNumber).Max(q => q.endTime);
                result.Add(entity);
            }

            return new ObjectResult(result);
        }

        private IEnumerable<MachineRestInfo> GetRestTimesBySections(IEnumerable<int> sectionIds)
        {
            var result = new List<MachineRestInfo>();
            foreach (var sectionId in sectionIds)
            {
                var machine = _machineRepository.GetNCMachineBySectionId(sectionId, ProjectId);
                if (machine != null)
                {
                    var times = _timeRepository.GetList(m => m.machineid == machine.id && m.status == (int)RowState.Valid && m.projectid == ProjectId).ToList();
                    foreach (var time in times)
                    {
                        result.Add(new MachineRestInfo
                        {
                            BingTime = time.bing_time,
                            EndTime = time.end_time,
                            MachineId = machine.id,
                            SectionId = sectionId,
                            TimeType = (TimeType)time.typeid
                        });
                    }
                }
            }

            return result;
        }

        [HttpPost]
        public IActionResult AddSchedule([FromBody]RequestChanglingAddSchedule request)
        {
            if (request.orders == null || !request.orders.Any())
            {
                throw new BadRequestException(CncEnum.RoutesNotFound);
            }
            var plan = _planRepository.Get(request.planId);
            if (plan == null)
            {
                throw new BadRequestException(CncEnum.PlanNotFound);
            }

            if (plan.status != (int)PlanProcess.UnPlan && plan.status != (int)PlanProcess.Stop && plan.status != (int)PlanProcess.HasPlan)
            {
                throw new BadRequestException(CncEnum.PlanHasExist);
            }

            var result = _scheduleRepository.AddChangLingSchedule(request, plan, ProjectId);
            if (result)
            {
                return new ObjectResult(CommonEnum.Succefull);
            }
            throw new BadRequestException(CommonEnum.Fail);
        }

        [HttpGet]
        public IActionResult GetPlanDetails(int planId, int num, int total)
        {
            if (num == 0)
            {
                throw new BadRequestException(CncEnum.BatchMustGreaterZero);
            }
            var plan = _planRepository.Get(planId);
            if (plan == null)
            {
                throw new BadRequestException(CncEnum.PlanNotFound);
            }

            if (num > total)
            {
                num = total;
            }

            var startIndex = 0;
            var details = _planDetailRepository.GetList(q => q.planId == planId && q.status == (int)RowState.Valid && q.projectId == ProjectId).ToList();
            if (details.Any())
            {
                var orderNumber = details.OrderByDescending(q => q.id).First().orderNumber;
                var orders = orderNumber.Split('-');
                startIndex = orders[orders.Length - 1].ToInt();
            }

            var batch = total / num;
            var residual = total % num;
            var response = new List<ResponseGetWorkOrders>();
            if (residual == 0)
            {
                for (var i = 1; i <= num; i++)
                {
                    response.Add(new ResponseGetWorkOrders
                    {
                        orderNumber = $"{plan.code}-{i + startIndex}",
                        count = batch
                    });
                }
            }
            else
            {
                var count = batch;
                for (var i = 1; i <= num; i++)
                {
                    if (i == num)
                    {
                        count = batch + residual;
                    }
                    response.Add(new ResponseGetWorkOrders
                    {
                        orderNumber = $"{plan.code}-{i + startIndex}",
                        count = count
                    });
                }

            }
            return new ObjectResult(response);
        }

        [HttpPost]
        public IActionResult GetPlanDatailsForPrint([FromBody]RequestplanId requestPlanId)
        {

            if (requestPlanId.planId == null)
            {
                throw new BadRequestException(RequestEnum.ParameterMiss);
            }

            var result = new List<ResponsePlanDatailsForPrint>();
            foreach (var planId in requestPlanId.planId)
            {
                var plan = _planRepository.Get(planId);
                if (plan == null)
                {
                    throw new BadRequestException(CncEnum.PlanNotFound);
                }

                if (plan.status == (int)PlanProcess.UnPlan)
                {
                    throw new BadRequestException(CncEnum.WorkOrderCannotPrint);
                }

                var planDetails = _planDetailRepository.GetList(m => m.planId == planId && m.status == (int)RowState.Valid && m.projectId == ProjectId);
                foreach (var planDetail in planDetails.ToList())
                {
                    var response = new ResponsePlanDatailsForPrint
                    {
                        count = planDetail.quantity,
                        draw_number = plan.draw_number,
                        orderNumber = planDetail.orderNumber,
                        partnumber = plan.partnumber,
                        product_code = plan.product_code,
                        product_name = plan.product_name,
                        delivery_time = plan.delivery_time
                    };

                    var schedules = _scheduleRepository.GetList(m =>
                            m.plandetail_id == planDetail.id && m.status == (int)RowState.Valid &&
                            m.projectId == ProjectId)
                        .ToList();
                    var i = 1;
                    var machineIds = schedules.Select(m => m.sectionId).Distinct().ToList();
                    var machines = _machineRepository.GetList(q => machineIds.Contains(q.id) && q.status == (int)RowState.Valid && q.projectid == ProjectId && q.category == (int)MachineCategory.NC);
                    foreach (var schedule in schedules)
                    {
                        var machineEntity = machines.FirstOrDefault(q => q.id == schedule.sectionId);
                        response.routes.Add(new RouteForPrint
                        {
                            cycletime = schedule.cycle_time,
                            orderNumber = planDetail.orderNumber,
                            plan_end = schedule.endTime,
                            sectionname = machineEntity != null ? machineEntity.title : "",
                            remark = "",
                            serialnumber = i,
                            route_name = schedule.route_name
                        });
                        i++;
                    }

                    result.Add(response);
                }
            }

            return new ObjectResult(result);
        }

        [HttpGet]
        public IActionResult GetStockData(int id)
        {
            ProductPlanInfo resp = new ProductPlanInfo();
            var planData = _planRepository.Get(f => f.id.Equals(id));
            if (planData == null)
            {
                return new ObjectResult(resp);
            }
            var details = _planDetailRepository.GetList(m => m.planId == planData.id && m.status == (int)RowState.Valid && m.projectId == ProjectId);
            resp = new ProductPlanInfo
            {
                product_id = planData.product_id,
                product_name = planData.product_name,
                product_code = planData.product_code,
                draw_number = planData.draw_number,
                workordertype = planData.workordertype,
                partnumber = planData.partnumber,
                code = planData.code,
                ordernumber = planData.ordernumber,
                quantity = planData.quantity,
                delivery_time = planData.delivery_time,
                create_time = planData.create_time,
                status = planData.status,
                ok_number = planData.producted_number,
                nok_number = planData.nok_number,
                autogenerate = planData.autogenerate,
                source = planData.source,
                finish_time = planData.finish_time,
                id = planData.id
            };
            if (details.Any())
            {
                resp.plan_start = details.Min(m => m.startTime);
                resp.plan_end = details.Max(m => m.endTime);
                resp.has_schedule = details.Sum(q => q.quantity);//已排产数量
            }
            var deliveryDate = UnixTimeHelper.ConvertStringDateTime(planData.delivery_time.ToString());
            resp.leaveDays = deliveryDate.Date.Subtract(DateTime.Now.Date).Days;
            //SettlementRepository settlement = new SettlementRepository();
            //var stockData = settlement.GetStockQuantity(resp.partnumber);
            //if (stockData.Any())
            //{
            //    resp.stock_quantity = stockData.First().Quantity;
            //}
            var ret = HttpClientHelper.HttpGet($"{Server}/ChanglingStock/GetStockData?clnvCode={resp.partnumber}");
            var result = (ResponseBase)JsonConvert.DeserializeObject(ret, typeof(ResponseBase));
            var data = result.Data.ToStr();
            if (result.Code != 0 && !string.IsNullOrEmpty(data))
            {
                var stockData = JsonConvert.DeserializeObject<List<StockData>>(data);
                if (stockData.Any())
                {
                    resp.stock_quantity = stockData.First().Quantity;
                }
            }
            resp.difference = resp.has_schedule - resp.ok_number;
            return new ObjectResult(resp);
        }

        [HttpGet]
        public IActionResult GetStockDetails(string id)
        {
            var ret = HttpClientHelper.HttpGet($"{Server}/ChanglingStock/GetStockDetails?clnvCode={id}");
            var result = (ResponseBase)JsonConvert.DeserializeObject(ret, typeof(ResponseBase));
            var data = result.Data.ToStr();
            if (result.Code != 0 && !string.IsNullOrEmpty(data))
            {
                var stockData = JsonConvert.DeserializeObject<List<StockDetail>>(data);
                return new ObjectResult(stockData);
            }
            throw new BadRequestException(CommonEnum.NoData);
        }
    }
}