using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Siger.Middlelayer.CncRepository.Entities;
using Siger.Middlelayer.CncRepository.Repositories.Interface;
using Siger.Middlelayer.CncRepository.Response;
using Siger.Middlelayer.Common;
using Siger.Middlelayer.Common.Extensions;
using Siger.Middlelayer.Common.Helpers;
using Siger.Middlelayer.Repository.Extensions;
using Siger.Middlelayer.Repository.Paged;

namespace Siger.Middlelayer.CncRepository.Repositories
{
    internal class WorkingSignRepository : CncRepositoryBase<siger_project_working_sign>, IWorkingSignRepository
    {
        private readonly ApiCncDbContext _context;
        public WorkingSignRepository(ApiCncDbContext context) : base(context)
        {
            _context = context;
        }

        private IEnumerable <UserWorkGroups> GetWorkGroups(int projectid)
        {
            var date = DateTime.Now;
            var time = UnixTimeHelper.GetNow();
            var thisTime = date.Hour * 3600 + date.Minute * 60 + date.Millisecond;
            var query = _context.siger_project_working_group.Where(t => t.project_id == projectid && t.status == (int)RowState.Valid && 
                t.start_date <= date).ToList();
            var shifts = (from t in _context.siger_project_shift
                          where t.projectid == projectid && t.status == (int)RowState.Valid &&
                              (t.start_time < t.end_time ? (t.start_time <= thisTime && thisTime <= t.end_time) :
                              (t.start_time <= thisTime || thisTime <= t.end_time))
                          select t).ToList();
            var res = new List<UserWorkGroups>();
            var userMids = query.Select(t => t.user_mids).ToList();
            var userIds = new List<int>();
            foreach(var userMid in userMids)
            {
                if (!string.IsNullOrEmpty(userMid))
                {
                    userIds.AddRange(userMid.Split(',').Select(t => t.ToInt()).ToList());
                }
            }
            userIds = userIds.Distinct().ToList();
            foreach(var userId in userIds)
            {
                var groups = query.Where(t => t.user_mids.Split(',').ToList().Contains(userId.ToString())).ToList();
                var groupName = new List<string>();
                var groupIds = new List<int>();
                var shiftIds = new List<int>();
                foreach(var group in groups)
                {
                    var shiftids = group.shift_ids.Split(',').ToList();
                    var groupShifts = shifts.Where(t => shiftids.Contains(t.id.ToString())).ToList();
                    if (!groupShifts.Any())
                    {
                        continue;
                    }
                    groupName.Add(group.name);
                    groupIds.Add(group.id);
                    shiftIds.AddRange(groupShifts.Select(t => t.id));
                }
                var shift = shifts.Where(t => shiftIds.Contains(t.id)).ToList();
                if (groupName.Any())
                {
                    res.Add(new UserWorkGroups
                    {
                        mid = userId,
                        workgroupname = string.Join(";", groupName.Distinct()),
                        workgroupids = groupIds,
                        shiftids = shift.Select(t => t.id).ToList(),
                        shiftname = string.Join(";", shift.Select(t => t.title).ToList())
                    });
                }
            }
            return res;
        }

        public IPagedCollectionResult<GetSignRecords> GetPagedList(IEnumerable<int> machineIds, IEnumerable<int> sectionIds, int departId, int groupId, int stationId, int userId, string startTime, string endTime
            ,int projectId , int page, int pagesize, int toexcel = 0)
        {
            var workGroups = GetWorkGroups(projectId);
            var query = from q in _context.siger_project_working_sign
                join u in _context.siger_project_user on q.user_mid equals u.mid
                join s in _context.siger_project_section on u.sectionid equals s.id
                join g in workGroups on q.user_mid equals g.mid
                    into gg
                from g in gg.DefaultIfEmpty()
                join ug in _context.siger_project_usergroup on u.usergroupid.ToInt() equals ug.id
                where q.project_id == projectId && q.status == (int) RowState.Valid
                select new GetSignRecords
                {
                    workcode = u.work_code,
                    departname = s.title,
                    endworktime = q.sign_outtime == DateTime.MinValue ? "" : q.sign_outtime.ToString(ParameterConstant.DateTimeFormat),
                    stationname = ug.title,
                    startworktime = q.sign_intime.ToString(ParameterConstant.DateTimeFormat),
                    username = u.name,
                    signintime = q.sign_intime,
                    userid = u.mid,
                    departid = u.sectionid,
                    workgroupids = g != null ? g.workgroupids : new List<int>(),
                    machineid = q.machine_id,
                    shiftids = g != null ? g.shiftids : new List<int>(),
                    shiftname = g != null ? g.shiftname : "",
                    groupname = g != null ? g.workgroupname : "",
                    stationid = ug.id
                };

            Expression<Func<GetSignRecords, bool>> timeExpression = q => true;
            if (!string.IsNullOrWhiteSpace(startTime) && !string.IsNullOrWhiteSpace(endTime))
            {
                timeExpression = q => ((q.signintime <= endTime.ToDateTime() && q.signintime >= startTime.ToDateTime()) ||
                                       (q.endworktime.ToDateTime() <= endTime.ToDateTime() &&
                                       q.endworktime.ToDateTime() >= startTime.ToDateTime()));
            }

            Expression<Func<GetSignRecords, bool>> machineExpression = q => true;
            if (machineIds.Any())
            {
                machineExpression = q => machineIds.Contains(q.machineid);
            }

            Expression<Func<GetSignRecords, bool>> departExpression = q => true;
            if (departId > 0)
            {
                departExpression = q => q.departid == departId;
            }

            Expression<Func<GetSignRecords, bool>> groupExpression = q => true;
            if (groupId > 0)
            {
                groupExpression = q => q.workgroupids.Contains(groupId);
            }

            Expression<Func<GetSignRecords, bool>> userExpression = q => true;
            if (userId > 0)
            {
                userExpression = q => q.userid == userId;
            }
            var expression = timeExpression.And(machineExpression).And(departExpression).And(groupExpression).And(userExpression);

            if (toexcel == 1)
            {
                var dataList = query.Where(expression).AsNoTracking().ToList();
                return new PagedCollectionResult<GetSignRecords>(dataList, 0);
            }
            var totalCount = query.Count(expression);
            var entities = query.Where(expression).Skip((page - 1) * pagesize).Take(pagesize).AsNoTracking().ToList();

            return new PagedCollectionResult<GetSignRecords>(entities, totalCount);
        }

        public IEnumerable<GetSignRecords> GetSignList(IEnumerable<int> machineIds, IEnumerable<int> sectionIds, int departId, int groupId, int stationId, 
            int userId, string startTime, string endTime, int projectId)
        {
            var workGroups = GetWorkGroups(projectId);
            var query = from q in _context.siger_project_working_sign
                        join u in _context.siger_project_user on q.user_mid equals u.mid
                        join s in _context.siger_project_section on u.sectionid equals s.id
                        join g in workGroups on q.user_mid equals g.mid
                        into gg
                        from g in gg.DefaultIfEmpty()
                        join ug in _context.siger_project_usergroup on u.usergroupid.ToInt() equals ug.id
                        where q.project_id == projectId && q.status == (int)RowState.Valid
                        select new GetSignRecords
                        {
                            workcode = u.work_code,
                            departname = s.title,
                            endworktime = q.sign_outtime == DateTime.MinValue ? "" : q.sign_outtime.ToString(ParameterConstant.DateTimeFormat),
                            stationname = ug.title,
                            startworktime = q.sign_intime.ToString(ParameterConstant.DateTimeFormat),
                            username = u.name,
                            signintime = q.sign_intime,
                            userid = u.mid,
                            departid = u.sectionid,
                            workgroupids = g != null ? g.workgroupids : new List<int>(),
                            machineid = q.machine_id,
                            shiftids = g != null ? g.shiftids : new List<int>(),
                            shiftname = g != null ? g.shiftname : "",
                            groupname = g != null ? g.workgroupname : "",
                            stationid = ug.id
                        };

            Expression<Func<GetSignRecords, bool>> timeExpression = q => true;
            if (!string.IsNullOrWhiteSpace(startTime) && !string.IsNullOrWhiteSpace(endTime))
            {
                timeExpression = q => ((q.signintime <= endTime.ToDateTime() && q.signintime >= startTime.ToDateTime()) ||
                                       (q.endworktime.ToDateTime() <= endTime.ToDateTime() &&
                                       q.endworktime.ToDateTime() >= startTime.ToDateTime()));
            }

            Expression<Func<GetSignRecords, bool>> machineExpression = q => true;
            if (machineIds.Any())
            {
                machineExpression = q => machineIds.Contains(q.machineid);
            }

            Expression<Func<GetSignRecords, bool>> departExpression = q => true;
            if (departId > 0)
            {
                departExpression = q => q.departid == departId;
            }

            Expression<Func<GetSignRecords, bool>> groupExpression = q => true;
            if (groupId > 0)
            {
                groupExpression = q => q.workgroupids.Contains(groupId);
            }

            Expression<Func<GetSignRecords, bool>> userExpression = q => true;
            if (userId > 0)
            {
                userExpression = q => q.userid == userId;
            }

            var expression = timeExpression.And(machineExpression).And(departExpression).And(groupExpression).And(userExpression);
            var entities = query.Where(expression).AsNoTracking().ToList();

            return entities;
        }
    }
}
