using Siger.ApiDashboard.Tasks.Model;
using Siger.Middlelayer.Common;
using Siger.Middlelayer.Common.Extensions;
using Siger.Middlelayer.Common.Helpers;
using Siger.Middlelayer.Common.ModuleEnum;
using Siger.Middlelayer.Dapper;
using Siger.Middlelayer.Dapper.Utilities;
using Siger.Middlelayer.Dapper.Utilities.Slice;
using Siger.Middlelayer.Log;
using Siger.Middlelayer.Repository.Entities;
using Siger.Middlelayer.Share.Models;
using Siger.Middlelayer.Share.Utilities;
using Siger.Middlelayer.Utility.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static Siger.ApiDashboard.Tasks.MachineStatusSummary;

namespace Siger.ApiDashboard.Tasks.Reports
{
    public class ModuleProductivity
    {
        public ProductivityResult GetProductivityResult(siger_project_monthreport_config config, siger_project_monthreport_config_template template)
        {
            var result = new ProductivityResult
            {
                title = template.title
            };
            try
            {
                var translate = new TranslateHelper(ReportDbHelper.Instance.ProjectLanguage(template.project_id) == 1);
                var now = DateTime.Parse(DateTime.Now.ToShortDateString() + " " + config.send_time);
                var machineIds = ReportDbHelper.Instance.GetMachineIds(template);
                var range = ReportDbHelper.Instance.TimeRange(config);
                var timeRange = range.StartTime + " - " + range.EndTime;
                var data = GetKeyValues(template, config.company_id, config.project_id, machineIds, now, range.StartTime, range.EndTime);

                var echartData = new Productivity_EcharData
                {
                    markingline = template.val
                };
                foreach (var r in data)
                {
                    echartData.xdata.Add(r.Key);
                    echartData.ydata.Add(r.Value);
                    result.tableData.Add(new Productivity_tableData
                    {
                        datetime = r.Key,
                        num1 = r.Value,
                        num2 = Math.Round(r.Value - template.val, 2)
                    });
                }
                result.echartData.Add(echartData);
            }
            catch (Exception e)
            {
                Logger.WriteLineError(e.ToString());
            }
            return result;
        }
        private Dictionary<string, double> GetKeyValues(siger_project_monthreport_config_template template, int companyId, int projectId, IEnumerable<int> machineIds, DateTime now, DateTime beginDate, DateTime endDate)
        {
            var responses = new List<MachineCanvasDataInfo>();

            //var repository = new ProductRepository(companyId, projectId);
            //var sliceRepositoey = new SliceSateRepository(companyId, projectId);
            var dic = new Dictionary<string, double>();
            //var key = string.Empty;
            //var val = 0d;
            switch (template.axios)
            {
                case 1:// 按设备
                    {
                        var machineLocation = ReportDbHelper.Instance.GetMachineLocation(machineIds).ToList();
                        foreach (var m in machineIds)
                        {
                            var machine = machineLocation.Find(f => f.machine_id == m);
                            if (machine == null)
                                continue;
                            var statusYields = GetMachineStatusYield(new List<int> { m }, companyId, projectId, now, beginDate, endDate);
                            dic.Add($"{machine.section_id}-{machine.machine_name}", statusYields.total_rate);
                        }
                        break;
                    }
                case 2: //按天
                    {
                        var patchId = 0;
                        var interal = DateTimeHelper.GetInteral(patchId);
                        var dates = DateTimeHelper.GetDateTimes(beginDate, endDate, patchId);
                        foreach (var date in dates)
                        {
                            DateTime start;
                            DateTime end;
                            string dateTime;
                            if (patchId == 0 || patchId == 1)
                            {
                                start = date;
                                end = date.AddDays(interal).AddSeconds(-1) > DateTime.Now
                                    ? DateTime.Now
                                    : date.AddDays(interal).AddSeconds(-1);
                                dateTime = end.ToString(ParameterConstant.DateFormat);
                            }
                            else
                            {
                                start = date.AddDays(1 - date.Day);
                                end = date.AddDays(1 - date.Day).Date.AddMonths(1).AddSeconds(-1) > DateTime.Now
                                    ? DateTime.Now
                                    : date.AddDays(1 - date.Day).Date.AddMonths(1).AddSeconds(-1); //到月底
                                dateTime = end.ToString(ParameterConstant.YearMonthFormat);
                            }
                            var statusYields = GetMachineStatusYield(machineIds, companyId, projectId, now, start, end);
                            dic.Add(dateTime.ToStr(), statusYields.total_rate);

                        }

                        break;
                    }
                case 3: //按周
                    {
                        var patchId = 1;
                        var interal = DateTimeHelper.GetInteral(patchId);
                        var dates = DateTimeHelper.GetDateTimes(beginDate, endDate, patchId);
                        foreach (var date in dates)
                        {
                            DateTime start;
                            DateTime end;
                            string dateTime;
                            if (patchId == 0 || patchId == 1)
                            {
                                start = date;
                                end = date.AddDays(interal).AddSeconds(-1) > DateTime.Now
                                    ? DateTime.Now
                                    : date.AddDays(interal).AddSeconds(-1);
                                dateTime = end.ToString(ParameterConstant.DateFormat);
                            }
                            else
                            {
                                start = date.AddDays(1 - date.Day);
                                end = date.AddDays(1 - date.Day).Date.AddMonths(1).AddSeconds(-1) > DateTime.Now
                                    ? DateTime.Now
                                    : date.AddDays(1 - date.Day).Date.AddMonths(1).AddSeconds(-1); //到月底
                                dateTime = end.ToString(ParameterConstant.YearMonthFormat);
                            }
                            var statusYields = GetMachineStatusYield(machineIds, companyId, projectId, now, start, end);
                            dic.Add(dateTime.ToStr(), statusYields.total_rate);
                        }
                        break;
                    }
                case 4:// 按自定义分组
                    {
                        var groups = template.param.Split(',').Select(t => t.ToInt()).Distinct();
                        foreach (var g in groups)
                        {
                            var group = ReportDbHelper.Instance.ConfigDbContext.siger_project_dashboard_machine_group.FirstOrDefault(f => f.id == g);
                            //var machines = group.machineid.Split(',').Select(m => m.ToInt());
                            var machines = ReportDbHelper.Instance.GetMachineIdsByGroup(template.project_id, group.machineid);
                            var statusYields = GetMachineStatusYield(machines, companyId, projectId, now, beginDate, endDate);
                            dic.Add(group.group, statusYields.total_rate);
                        }
                        break;
                    }

            }
            return dic;
        }
        private MachineStatusYield GetMachineStatusYield(IEnumerable<int> machineIds, int companyId, int projectId, DateTime now, DateTime sTime, DateTime eTime)
        {
            var result = new MachineStatusYield();
            var machineCount = machineIds.Count();
            if (machineCount == 0)
                return result;
            var beates = ReportDbHelper.Instance.CncDbContext.siger_project_beat_set.Where(q => machineIds.Contains(q.machineID) && q.projectID == projectId && q.status == (int)RowState.Valid).ToList();
            var yieldRepository = new ProductRepository(companyId, projectId);
            var yileds = yieldRepository.GetYileds(sTime.ToString(ParameterConstant.DateTimeFormat), eTime.ToString(ParameterConstant.DateTimeFormat), machineIds).ToList();

            for (var i = 6; i >= 0; i--)
            {
                var dtDay = now.AddDays(-i);
                result.xdata.Add(dtDay.ToString(ParameterConstant.MonthDayFormay));

                var dtStart = DateTime.Parse(dtDay.ToShortDateString());
                var dtEnd = DateTime.Parse(dtDay.ToString(ParameterConstant.DateToEnd));
                var dayYields = yileds.Where(q => q.dataTime >= dtStart && q.dataTime <= dtEnd);

                var totalYield = 0;
                foreach (var yiled in dayYields)
                {
                    var beat = beates.FirstOrDefault(q =>
                        q.process_number == yiled.programCode && q.machineID == yiled.machineID);
                    var rate = beat?.yieldrate ?? 1;

                    totalYield += yiled.yield * rate;
                }
                result.ydata.Add(totalYield);
            }

            var list = new List<MachineStatusCount>();
            var dtstart = sTime.ToString(ParameterConstant.DateTimeFormat);
            var dtend = eTime.ToString(ParameterConstant.DateTimeFormat);
            var stations = ReportDbHelper.Instance.GetLevelSectionNames(machineIds, projectId).ToList();
            var times = GetRestTimesBySections(machineIds);
            var repositoey = new SliceSateRepository(companyId, projectId);
            var daySliceSates = repositoey.GetCncSliceSates(machineIds, dtstart, dtend);
            var machineProgramNos = YieldMangaer.GetMachineProgramNos(yileds, dtstart, machineIds, UnixTimeHelper.GetUnixByShortDate(dtend));
            if (!machineProgramNos.Any())
            {
                return result;
            }

            double totalDebug = 0;
            double totalFault = 0;
            double totalShutdown = 0;
            double totalEfficiency = 0;
            var count = 0;
            foreach (var machineProgramNo in machineProgramNos)
            {
                var station = stations.FirstOrDefault(q => q.machine_id == machineProgramNo.machineId);

                double totalSeconds = 0;
                double rest = 0;
                var rests = new List<RestTimeInfo>();
                var machineTimes = times.Where(q => q.MachineId == machineProgramNo.machineId);
                var restTimes = RestTimeManager.GetMachineRestSeconds(machineTimes, dtstart.ToDateTime(), dtend.ToDateTime());
                //

                //
                foreach (var timeRange in machineProgramNo.TimeRanges)
                {
                    totalSeconds += timeRange.EndTime.Subtract(timeRange.StartTime).TotalSeconds;
                    foreach (var restTime in restTimes)
                    {
                        if (timeRange.StartTime >= restTime.EndTime) continue;
                        if (timeRange.EndTime <= restTime.StartTime) continue;
                        if (restTime.StartTime <= timeRange.StartTime && restTime.EndTime <= timeRange.EndTime && restTime.EndTime >= timeRange.StartTime)
                        {
                            rests.Add(new RestTimeInfo(dtstart.ToDateTime(), timeRange.StartTime, restTime.EndTime));
                        }

                        else if (restTime.StartTime >= timeRange.StartTime && restTime.EndTime <= timeRange.EndTime)
                        {
                            rests.Add(new RestTimeInfo(dtstart.ToDateTime(), restTime.StartTime, restTime.EndTime));
                        }

                        else if (restTime.StartTime >= timeRange.StartTime && restTime.EndTime >= timeRange.EndTime && restTime.StartTime <= timeRange.EndTime)
                        {
                            rests.Add(new RestTimeInfo(dtstart.ToDateTime(), restTime.StartTime, timeRange.EndTime));
                        }

                        else if (restTime.StartTime <= timeRange.StartTime && restTime.EndTime >= timeRange.EndTime)
                        {
                            rests.Add(new RestTimeInfo(dtstart.ToDateTime(), timeRange.StartTime, timeRange.EndTime));
                        }
                    }
                }
                //20200707 begin
                //foreach (var restTime in rests.Distinct())
                //{
                //    rest += restTime.EndTime.Subtract(restTime.StartTime).TotalSeconds;
                //}
                //var runTimes = totalSeconds - rest;

                var runTimes = RestTimeManager.GetNewRun(machineProgramNo.machineId, dtstart.ToDateTime(), dtend.ToDateTime(), times);
                //20200707 end


                var machineYield = yileds.Where(q => q.machineID == machineProgramNo.machineId && q.programCode == machineProgramNo.programNo).Sum(s => s.yield);
                var beat = beates.FirstOrDefault(q => q.machineID == machineProgramNo.machineId && q.process_number == machineProgramNo.programNo);
                var response = new MachineStatusCount
                {
                    program_no = machineProgramNo.programNo,
                    location = station != null ? station.lastSecondSectionTitle + "-" + station.lastSectionTitle : "",
                    actual_output = machineYield,
                    total_efficiency = machineYield > 0 ? "100%(0.00h)" : "0%(0.00h)",
                    fault_loss = "0%(0.00h)",
                    idle_loss = "0%(0.00h)",
                    debug_loss = "0%(0.00h)",
                    shutdown_loss = "0%(0.00h)"
                };
                if (beat == null)
                {
                    response.product_name = "NA";
                    response.route_name = "NA";
                    response.theory_yield = "NA";
                    response.total_efficiency = "NA";
                    response.idle_loss = "NA";
                }
                else if (runTimes != 0)
                {
                    response.actual_output = response.actual_output * beat.yieldrate;
                    response.product_name = beat.product_name_text;
                    response.route_name = beat.route_name;

                    var machineSlice = daySliceSates.Where(q => q.MachineID == machineProgramNo.machineId);
                    //0 关机 1 运行 2 调试 3 空闲 4 故障
                    var shutdown = SliceManager.GetResetTimesBySlice(0, machineProgramNo.TimeRanges, machineSlice, rests);
                    var debug = SliceManager.GetResetTimesBySlice(2, machineProgramNo.TimeRanges, machineSlice, rests);
                    var fault = SliceManager.GetResetTimesBySlice(4, machineProgramNo.TimeRanges, machineSlice, rests);
                    var run = SliceManager.GetResetTimesBySlice(1, machineProgramNo.TimeRanges, machineSlice, null);

                    runTimes = runTimes < 0 ? run : runTimes;

                    totalDebug += debug / runTimes;
                    totalFault += fault / runTimes;
                    totalShutdown += shutdown / runTimes;

                    var debug_loss_value = Math.Round(debug / runTimes * 100, 2);
                    response.debug_loss = debug_loss_value.ToStr() + "%(" + Math.Round(debug / 3600, 2) + "h)";

                    var fault_loss_value = Math.Round(fault / runTimes * 100, 2);
                    response.fault_loss = fault_loss_value.ToStr() + "%(" + Math.Round(fault / 3600, 2) + "h)";

                    var shutdown_loss_value = Math.Round(shutdown / runTimes * 100, 2);
                    response.shutdown_loss = shutdown_loss_value.ToStr() + "%(" + Math.Round(shutdown / 3600, 2) + "h)";

                    var beatInfo = beat.standard_besat + beat.updown_besat;
                    response.theory_yield = beatInfo <= 0 ? "0" : (Math.Round(runTimes / beatInfo, 0) * beat.yieldrate).ToString();

                    var idle = runTimes - shutdown - debug - fault - beatInfo * machineYield;
                    var userIdle = idle < 0 ? 0 : idle;

                    var idle_loss_value = Math.Round(userIdle / runTimes * 100, 2);
                    response.idle_loss = idle_loss_value.ToStr() + "%(" + Math.Round(userIdle / 3600, 2) + "h)";

                    totalEfficiency += beatInfo * machineYield / runTimes;
                    var total_efficiency_value = Math.Round(beatInfo * machineYield / runTimes * 100, 2);
                    response.total_efficiency = total_efficiency_value.ToStr() + "%(" + Math.Round(beatInfo * machineYield / 3600, 2) + "h)";
                }
                list.Add(response);
                count++;
            }

            result.total_rate = (totalEfficiency / count * 100).FormatDouble();
            result.fault_rate = (totalFault / count * 100).FormatDouble();
            result.debug_rate = (totalDebug / count * 100).FormatDouble();
            result.shutdown_rate = (totalShutdown / count * 100).FormatDouble();
            result.free_rate = (100 - (result.total_rate + result.debug_rate + result.fault_rate + result.shutdown_rate)).FormatDouble();

            result.data = list;
            return result;
        }
        private IEnumerable<MachineRestInfo> GetRestTimesBySections(IEnumerable<int> machineIds)
        {
            var result = new List<MachineRestInfo>();

            var times = ReportDbHelper.Instance.CncDbContext.siger_project_production_time.Where(m => machineIds.Contains(m.machineid) && m.status == (int)RowState.Valid).ToList();
            foreach (var machineId in machineIds)
            {
                var machineTimes = times.Where(q => q.machineid == machineId);
                foreach (var time in machineTimes)
                {
                    result.Add(new MachineRestInfo
                    {
                        BingTime = time.bing_time,
                        EndTime = time.end_time,
                        MachineId = machineId,
                        TimeType = (TimeType)time.typeid
                    });
                }
            }

            return result;
        }
    }
}
