using Siger.Middlelayer.Common;
using Siger.Middlelayer.Common.Helpers;
using Siger.Middlelayer.DashboardRepository.Entities;
using Siger.Middlelayer.TpmRepository.Entities;
using Siger.Middlelayer.DashboardRepository.Repositories.Interface;
using Siger.Middlelayer.DashboardRepository.Response;
using Siger.Middlelayer.Share.Enum.ModuleEnum;
using Siger.Middlelayer.Utility.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Siger.Middlelayer.TpmRepository.Response;
using Siger.Middlelayer.Repository.Entities;
using System.Linq.Expressions;
using NPOI.SS.Formula.Functions;

namespace Siger.Middlelayer.DashboardRepository.Repositories
{
    internal class DashboardLevelRespository : DashboardRepositoryBase<siger_dashboard_level_config>, IDashboardLevelRepository
    {
        private readonly ApiDashboardDbContext _context;

        public DashboardLevelRespository(ApiDashboardDbContext context) : base(context)
        {
            _context = context;
        }

        /// <summary>
        /// 瓶颈工位计划停机安灯数据源  
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="sections">工站</param>
        /// <param name="dtStart"></param>
        /// <param name="dtEnd"></param>
        /// <returns></returns>
        public IEnumerable<siger_andon_info> AndonPlanStopData(int projectId, List<int> sections, DateTime dtStart, DateTime dtEnd)
        {
            var planStop = new List<siger_andon_info>();
            var starttime = UnixTimeHelper.ConvertDataTimeLong(dtStart);
            var endtime = UnixTimeHelper.ConvertDataTimeLong(dtEnd);

            var query = from cfg in _context.siger_andon_station_config
                        join attr in _context.siger_project_machine_attribution on cfg.machine_id equals attr.machine
                        where cfg.project_id == projectId && sections.Contains(attr.station) && cfg.status == (int)RowState.Valid
                        select new ResponseLevelSectionMachine
                        {
                            Id = cfg.id,
                            MachineId = cfg.machine_id,
                            Section = attr.station,
                            Title = attr.name
                        };
            //瓶颈工位
            var bottleSection = query.GroupBy(g => g.Id).Select(s => s.FirstOrDefault()).Select(s => s.Section).ToList();
            if (!bottleSection.Any())
                return planStop;
            //瓶颈安灯
            var andonInfo = _context.siger_andon_info.Where(f => f.create_time > starttime && f.create_time < endtime && bottleSection.Contains(f.station));
            if (!andonInfo.Any())
                return planStop;
            //瓶颈安灯异常类型(计划停机)
            var types = andonInfo.Select(s => s.expection_type).ToList();
            var andoExpectionType = _context.siger_andon_expection_type.Where(f => f.projectid == projectId && f.is_caloee == 0 && types.Contains(f.id));

            var planEtype = andoExpectionType.Select(s => s.id);
            planStop = andonInfo.Where(f => planEtype.Contains(f.expection_type)).ToList();

            return planStop;
        }
        /// <summary>
        /// 安灯切片
        /// </summary>
        /// <param name="dtStart">开始时间</param>
        /// <param name="datasource">计划停机数据源</param>
        /// <returns></returns>
        public IEnumerable<ResponseAndonSlice> GetAndonSlice(DateTime dtStart, IEnumerable<siger_andon_info> datasource)
        {
            var andondata = new List<ResponseAndonSlice>();

            var starttime = UnixTimeHelper.ConvertDataTimeLong(dtStart);
            var now = UnixTimeHelper.GetNow();
            var andoninfos = datasource.Select(s => new { start = s.create_time, end = s.complete_time == 0 ? now : s.complete_time });
            #region //generate list
            foreach (var andoninfo in andoninfos)
            {
                if (andondata.Any())
                {
                    //all
                    var info = andondata.FirstOrDefault(f => andoninfo.start < f.start && andoninfo.end > f.end);
                    if (info != null)
                    {
                        info.start = andoninfo.start;
                        info.end = andoninfo.end;
                        continue;
                    }
                    //not all
                    var uninfo = andondata.FirstOrDefault(f => andoninfo.start >= f.start && andoninfo.end <= f.end);
                    if (uninfo != null)
                    {
                        continue;
                    }
                    //left
                    var left_info = andondata.FirstOrDefault(f => andoninfo.start < f.start && andoninfo.end >= f.start && andoninfo.end <= f.end);
                    if (left_info != null)
                    {
                        left_info.start = andoninfo.start;
                        continue;
                    }
                    //right
                    var right_info = andondata.FirstOrDefault(f => andoninfo.start >= f.start && andoninfo.start <= f.end && andoninfo.end > f.end);
                    if (right_info != null)
                    {
                        right_info.end = andoninfo.end;
                        continue;
                    }
                    //out
                    andondata.Add(new ResponseAndonSlice
                    {
                        start = andoninfo.start,
                        end = andoninfo.end,
                        status = 1
                    });
                    continue;
                }
                andondata.Add(new ResponseAndonSlice
                {
                    start = andoninfo.start,
                    end = andoninfo.end,
                    status = 1
                });
            }
            var start = andondata.FirstOrDefault()?.start ?? starttime;
            //init start
            if (start < starttime)
            {
                andondata.First().start = starttime;
                start = starttime;
            }
            #endregion

            return andondata;
        }

        /// <summary>
        /// 获取瓶颈工位 :设备id ,sectionId
        /// </summary>
        /// <param name="sections">产线/工位 层级</param>
        /// <param name="projectId">项目ID</param>
        /// <param name="mid">返回 设备ID </param>
        /// <returns></returns>
        public IEnumerable<int> GetBottleSection(List<int> sections, int projectId,bool mid=true)
        {
            var query = from cfg in _context.siger_andon_station_config
                        join attr in _context.siger_project_machine_attribution on cfg.machine_id equals attr.machine
                        where cfg.project_id == projectId && sections.Contains(attr.station) &&
                        attr.attribution == 1 &&
                        cfg.station_type == 1 &&
                        cfg.status == (int)RowState.Valid && attr.status == (int)RowState.Valid
                        select new ResponseLevelSectionMachine
                        {
                            Id = cfg.id,
                            MachineId = cfg.machine_id,
                            Section = attr.station,
                            Title = attr.name
                        };
            if (mid)
                return query.Select(s => s.MachineId);
            else
                return query.Select(s => s.Section);

        }

        /// <summary>
        /// 获取瓶颈工位 计划停机
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="time"></param>
        /// <param name="mid"></param>
        /// <param name="begin"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        public double GetStopTime(int projectId, double time, int mid, DateTime begin, DateTime end)
        {
            //计划停机类型
            var types = _context.siger_andon_expection_type.Where(f => f.projectid == projectId && f.is_passprocess == 1 && f.is_caloee == 0).Select(s => s.id).ToList();
            var _stime = UnixTimeHelper.ConvertDataTimeLong(begin);
            var _etime = UnixTimeHelper.ConvertDataTimeLong(end);

            //时间之前未关闭
            Expression<Func<siger_andon_info, bool>> FunUnClosed = f => f.projectid == projectId && types.Contains(f.expection_type) && f.machine == mid && f.create_time < _stime && f.approve_time == 0;
            var andonUnClosed = _context.siger_andon_info.FirstOrDefault(FunUnClosed);
            if (andonUnClosed != null)
                return 0;
            //时间段之内未关闭
            Expression<Func<siger_andon_info, bool>> FunUnClosedBet = f => f.projectid == projectId && types.Contains(f.expection_type) && f.machine == mid && f.create_time > _stime && f.create_time < _etime && f.approve_time == 0;
            var andoUnClosedBet = _context.siger_andon_info.FirstOrDefault(FunUnClosedBet);
            if (andoUnClosedBet != null)
            {
                var ctime = UnixTimeHelper.ConvertStringDateTime(andoUnClosedBet.create_time.ToString());
                var attotal = (end - ctime).TotalSeconds;
                return time - attotal;
            }
            //覆盖时间段 已关闭
            Expression<Func<siger_andon_info, bool>> FunBL = f => f.projectid == projectId && types.Contains(f.expection_type) && f.machine == mid && f.create_time <= _stime && f.approve_time >= _etime && f.approve_time != 0;
            var andonBl = _context.siger_andon_info.FirstOrDefault(FunBL);
            if (andonBl != null)
                return 0;

            //时间段之内 已关闭
            Expression<Func<siger_andon_info, bool>> FunClosed = f => f.projectid == projectId && types.Contains(f.expection_type) && f.machine == mid && f.create_time >= _stime && f.approve_time <= _etime && f.approve_time != 0;
            var andonClosedlst = _context.siger_andon_info.Where(FunClosed);
            var attotallst = 0d;

            foreach (var andon in andonClosedlst)
            {
                var createtime = UnixTimeHelper.ConvertStringDateTime(andon.create_time.ToString());
                var comptime = UnixTimeHelper.ConvertStringDateTime(andon.approve_time.ToString());
                var tspan = (comptime - createtime).TotalSeconds;
                attotallst += tspan;

            }

            //时间端左侧
            Expression<Func<siger_andon_info, bool>> FunClosed2 = f => f.projectid == projectId && types.Contains(f.expection_type) && f.machine == mid && f.create_time < _stime && f.approve_time > _stime && f.approve_time <= _etime && f.approve_time != 0;
            var andonClosedlst2 = _context.siger_andon_info.Where(FunClosed2);
            foreach (var andon in andonClosedlst2)
            {
                var comptime = UnixTimeHelper.ConvertStringDateTime(andon.approve_time.ToString());
                var tspan = (comptime - begin).TotalSeconds;
                attotallst += tspan;
            }
            //时间端右侧
            Expression<Func<siger_andon_info, bool>> FunClosed3 = f => f.projectid == projectId && types.Contains(f.expection_type) && f.machine == mid && f.create_time > _stime && f.create_time <= _etime && f.approve_time > _etime && f.approve_time != 0;
            var andonClosedlst3 = _context.siger_andon_info.Where(FunClosed3);
            foreach (var andon in andonClosedlst3)
            {
                var createtime = UnixTimeHelper.ConvertStringDateTime(andon.create_time.ToString());
                var tspan = (end - createtime).TotalSeconds;
                attotallst += tspan;
            }

            return time - attotallst;
        }

        public void GetTimeRange(string date, int project_id, out DateTime dtStart, out DateTime dtEnd, int section = 0, DashboardEnum dashboard = DashboardEnum.Chanel)
        {
            dtStart = DateTimeHelper.GetTodayStartTime();
            if (!string.IsNullOrEmpty(date))
            {
                if (DateTime.TryParse(date, out DateTime dt))
                {
                    dtStart = DateTimeHelper.GetTodayStartTime(dt);
                }
            }
            dtEnd = dtStart.AddHours(8);
            var cfg = _context.siger_dashboard_level_config.FirstOrDefault(f => f.ProjectId == project_id && f.PageType == (int)dashboard);
            if (dashboard == DashboardEnum.Chanel)
            {
                cfg = _context.siger_dashboard_level_config.FirstOrDefault(f => f.ProjectId == project_id && f.PageType == (int)dashboard && f.Section == section);
            }
            if (cfg != null)
            {
                dtStart = dtStart.AddHours(cfg.Startime);
                dtEnd = dtStart.AddHours(cfg.Schedu);
            }
        }

        public double ProductTarget(int projectId, double second, string productName)
        {
            var product = _context.siger_project_product.FirstOrDefault(f => f.projectid == projectId && f.name == productName);
            if (product == null)
                return 0;
            var productRoutes = _context.siger_project_product_route.Where(f => f.projectId == projectId && f.productId == product.id && f.status == (int)RowState.Valid).OrderByDescending(d => d.serialNumber);
            if (!productRoutes.Any())
                return 0;
            var maxRote = productRoutes.FirstOrDefault();
            var rated = maxRote.working_hours;
            double count = Math.Abs(second / rated);
            return Math.Round(count);
        }

        public DateTime StartDate(string date)
        {
            if (string.IsNullOrEmpty(date))
            {
                return DateTimeHelper.GetTodayStartTime();
            }
            else
            {
                if (!DateTime.TryParse(date, out DateTime dt))
                    return DateTimeHelper.GetTodayStartTime();
                return DateTimeHelper.GetTodayStartTime(dt);
            }

        }
        /// <summary>
        /// 获取时间段换型产品
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="machineId"></param>
        /// <param name="stime"></param>
        /// <param name="etime"></param>
        /// <returns></returns>
        public IEnumerable<ResponseAndoProductType> GetAndonProductName(int projectId, int machineId, DateTime stime, DateTime etime)
        {
            var _stime = UnixTimeHelper.ConvertDataTimeLong(stime);
            var _etime = UnixTimeHelper.ConvertDataTimeLong(etime);
            var types = _context.siger_andon_expection_type.Where(f => f.projectid == projectId && f.is_passprocess == 1 && f.is_caloee == 1).Select(s => s.id).ToList();

            var query = from a in _context.siger_andon_info
                        join p in _context.siger_project_product on a.product_code equals p.code
                        where a.projectid == projectId &&
                        machineId == a.machine && types.Contains(a.expection_type) &&
                        a.create_time >= _stime && a.create_time <= _etime
                        select new ResponseAndoProductType
                        {
                            hour = UnixTimeHelper.ConvertStringDateTime(a.create_time.ToString()).Hour,
                            productCode = p.code,
                            productName = p.name,
                            datetime = UnixTimeHelper.ConvertStringDateTime(a.create_time.ToString())
                        };
            return query.OrderBy(f => f.datetime);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="machineIds"></param>
        /// <param name="stime"></param>
        /// <param name="etime"></param>
        /// <returns></returns>
        public ResponseAndoProductType GetAndonLastChangeProduct(int projectId, int machineIds, DateTime stime, DateTime etime)
        {
            var types = _context.siger_andon_expection_type.Where(f => f.projectid == projectId && f.is_passprocess == 1 && f.is_caloee == 1).Select(s => s.id).ToList();
            var _stime = UnixTimeHelper.ConvertDataTimeLong(stime);
            var _etime = UnixTimeHelper.ConvertDataTimeLong(etime);
            var query = from a in _context.siger_andon_info
                        join p in _context.siger_project_product on a.product_code equals p.code
                        where a.projectid == projectId && a.machine==machineIds && types.Contains(a.expection_type) &&
                        a.create_time >= _stime && a.create_time <= _etime
                        select new ResponseAndoProductType
                        {
                            hour = UnixTimeHelper.ConvertStringDateTime(a.create_time.ToString()).Hour,
                            productCode = p.code,
                            productName = p.name,
                            datetime = UnixTimeHelper.ConvertStringDateTime(a.create_time.ToString())
                        };
            var data = query.OrderByDescending(o => o.datetime);
            return data.FirstOrDefault();
        }

        public ResponseProductTarget BottleProductTarget(int projectId, int bottleMachine, DateTime stepStartDate, DateTime stepEndDate)
        {
            var _targetVal = 0d;
            var _targetPd = "";
            DateTime dtDay;
            var dtMonth = stepStartDate.AddDays(-60);
            for (dtDay = stepStartDate; dtDay.CompareTo(stepEndDate) < 0; dtDay = dtDay.AddHours(1))
            {
                var currHour = DateTime.Now.Hour;
                var currMin = DateTime.Now.Minute;
                var stepStarTime = dtDay;
                var stepEndTime = dtDay.AddHours(1).AddSeconds(-1);
                if (stepEndTime > DateTime.Now)
                    stepEndTime = DateTime.Now;
                var productAndonHour = GetAndonProductName(projectId, bottleMachine, stepStarTime, stepEndTime);
                var productWorkingTime = (stepEndTime - stepStarTime).TotalSeconds;
                if (stepStarTime > DateTime.Now)
                    continue;
                if (DateTime.Now.Hour == stepStarTime.Date.Hour)
                    productWorkingTime = DateTime.Now.Minute * 60 + DateTime.Now.Second;
                if (productAndonHour.Any()) // 有安灯换型
                {
                    var balaceHour = 0d;
                    foreach (var p in productAndonHour)
                    {
                        if (p.datetime >= stepStarTime)
                        {//取最后一次安灯换型
                            balaceHour = (p.datetime - stepStarTime).TotalSeconds;
                            var preProduct = GetAndonLastChangeProduct(projectId, bottleMachine, dtMonth, stepStarTime);
                            if (preProduct != null)
                            {
                                _targetVal += ProductTarget(projectId, balaceHour, preProduct.productName);
                                _targetPd += $" {preProduct.productName},";
                            }
                            continue;
                        }
                    }
              
                    var ptime = 0d;
                    var endStepTime = stepEndTime;
                    if (stepEndTime > DateTime.Now)
                    {
                        endStepTime = DateTime.Now;
                        ptime = (currMin * 60) - balaceHour;
                    }
                    else
                    {
                        ptime = 3600 - balaceHour;
                    }
                    ptime = GetStopTime(projectId, ptime, bottleMachine, stepStarTime, stepEndTime);
                    if (ptime > 0)
                    {
                        var lastProduct = GetAndonLastChangeProduct(projectId, bottleMachine, dtMonth, endStepTime);
                        if (lastProduct != null)
                        {
                            _targetVal += ProductTarget(projectId, ptime, lastProduct.productName);
                            _targetPd += $"{lastProduct.productName},";
                        }
                    }
                }
                else
                {
                    productWorkingTime = GetStopTime(projectId, productWorkingTime, bottleMachine, stepStarTime, stepEndTime);
                    //取最后一次安灯换型
                    var lastProduct = GetAndonLastChangeProduct(projectId, bottleMachine, dtMonth, stepStarTime);
                    if (lastProduct != null)
                    {
                        _targetVal += ProductTarget(projectId, productWorkingTime, lastProduct.productName);
                        _targetPd += $" {lastProduct.productName},";
                    }
                }
            }
            if (_targetPd.Length > 0)
                _targetPd = _targetPd.Substring(0, _targetPd.Length - 1);
            return new ResponseProductTarget
            {
                Name = _targetPd,
                Val = _targetVal
            };
        }


        public List<ResponseProductTarget> BottleProductTarget(int projectId, List<int> machines, DateTime stepStartDate, DateTime stepEndDate)
        {
            var ret = new List<ResponseProductTarget>();
            foreach (var machineid in machines)
            {
                var model = BottleProductTarget(projectId, machineid, stepStartDate, stepEndDate);
                model.machineid = machineid;
                ret.Add(model);
            }
            return ret;
        }

        public IEnumerable<ResponseLevelSectionMachine> GetBottleSection(int projectId)
        {
            var query = from cfg in _context.siger_andon_station_config
                        join attr in _context.siger_project_machine_attribution on cfg.machine_id equals attr.machine
                        join l in _context.siger_project_level_section on attr.station equals l.id
                        join lp in _context.siger_project_level_section on l.parentid equals lp.id
                        join dcfg in _context.siger_dashboard_factory_chanel on lp.id equals dcfg.section
                        where cfg.project_id == projectId && attr.projectid==projectId &&
                        attr.attribution == 1 &&
                        cfg.station_type == 1 &&
                        cfg.status == (int)RowState.Valid && attr.status == (int)RowState.Valid &&
                        l.status==(int)RowState.Valid && lp.status==(int)RowState.Valid &&
                        dcfg.status==(int)RowState.Valid
                        select new ResponseLevelSectionMachine
                        {
                            Id = cfg.id,
                            MachineId = cfg.machine_id,
                            Section = attr.station,
                            Title = attr.name,
                            ParentId=lp.id,
                            ParentTitle=lp.title,
                            Pic=dcfg.pic
                        };
            return query;
        }
    }
}
