using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using GxPress.Common.AppOptions;
using GxPress.Common.Tools;
using GxPress.Entity.WorkFlow;
using GxPress.Repository.Interface;
using GxPress.Repository.Interface.WorkFlow;
using GxPress.Repository.Interface.WorkProcess;
using GxPress.Request.App.Flow;
using Microsoft.Extensions.Options;
using Datory;
using GxPress.EnumConst;
using Dapper;

namespace GxPress.Repository.Implement.WorkFlow
{
    public class FlowRepository : IFlowRepository
    {
        private readonly Repository<Flow> _repository;
        private readonly IMapper _mapper;
        private readonly IFlowFieldValueRepository _flowFieldValueRepository;
        private readonly IFlowTodoRepository _flowTodoRepository;
        private readonly IProcessRepository _processRepository;
        private readonly IProcessFieldRepository _processFieldRepository;
        private readonly IProcessGroupRepository _processGroupRepository;
        private readonly IProcessNodeRepository _processNodeRepository;
        private readonly IProcessRequestLimitRepository _processRequestLimitRepository;
        private readonly IRuleConditionRepository _conditionRuleRepository;
        private readonly IRuleCarbonCopyRepository _ruleCarbonCopyRepository;
        private readonly IRuleApproverCheckRepository _ruleApproverCheckRepository;
        private readonly IDepartmentRepository _departmentRepository;
        private readonly IRoleRepository _roleRepository;
        private readonly IUserRepository _userRepository;
        private readonly string _connectionString;
        private readonly string _databaseTypeStr;
        public FlowRepository(IOptionsMonitor<DatabaseOptions> dbOptionsAccessor, IMapper mapper,
            IFlowFieldValueRepository flowFieldValueRepository,
            IFlowTodoRepository flowTodoRepository,
            IProcessRepository processRepository,
            IProcessFieldRepository processFieldRepository,
            IProcessGroupRepository processGroupRepository,
            IProcessRequestLimitRepository processRequestLimitRepository,
            IProcessNodeRepository processNodeRepository,
            IRuleConditionRepository conditionRuleRepository,
            IRuleCarbonCopyRepository ruleCarbonCopyRepository,
            IRuleApproverCheckRepository ruleApproverCheckRepository,
            IDepartmentRepository departmentRepository,
            IRoleRepository roleRepository,
            IUserRepository userRepository)
        {
            _databaseTypeStr = dbOptionsAccessor.CurrentValue.DatabaseType;
            _connectionString = dbOptionsAccessor.CurrentValue.ConnectionString;
            var databaseType = StringUtils.ToEnum<DatabaseType>(dbOptionsAccessor.CurrentValue.DatabaseType, DatabaseType.MySql);
            var database = new Database(databaseType, dbOptionsAccessor.CurrentValue.ConnectionString);
            _repository = new Repository<Flow>(database);
            _mapper = mapper;
            _flowFieldValueRepository = flowFieldValueRepository;
            _flowTodoRepository = flowTodoRepository;
            _processRepository = processRepository;
            _processFieldRepository = processFieldRepository;
            _processGroupRepository = processGroupRepository;
            _processRequestLimitRepository = processRequestLimitRepository;
            _processNodeRepository = processNodeRepository;
            _conditionRuleRepository = conditionRuleRepository;
            _ruleCarbonCopyRepository = ruleCarbonCopyRepository;
            _ruleApproverCheckRepository = ruleApproverCheckRepository;
            _departmentRepository = departmentRepository;
            _roleRepository = roleRepository;
            _userRepository = userRepository;
        }

        public IDatabase Database => _repository.Database;
        public string TableName => _repository.TableName;
        public List<TableColumn> TableColumns => _repository.TableColumns;

        public async Task<IEnumerable<Flow>> GetListAsync(int processId)
        {
            return await _repository.GetAllAsync(Q
                .Where(nameof(Flow.ProcessId), processId)
            );
        }

        public async Task<int> InsertAsync(Flow flow)
        {
            var id = await _repository.InsertAsync(flow);
            var no = StringUtils.GetFlowNo(DateTime.Now, id);
            await _repository.UpdateAsync(Q
                .Set(nameof(Flow.No), no)
                .Where(nameof(Flow.Id), id)
            );
            return id;
        }

        public async Task<Flow> GetAsync(int flowId)
        {
            return await _repository.GetAsync(flowId);
        }

        public async Task<int> GetCountByUserIdAsync(int userId, bool checking, FlowListRequest request)
        {
            string sql = $@"SELECT 
                                count(1)
                            FROM
                                ccpph.tede_flow a
                            WHERE 1=1 ";
            if (!checking)
                sql += $@" and a.State IN ('Denied','Checked')";
            else
                sql += $@" and a.State Not IN ('Denied','Checked')";
            sql += $@" AND a.UserId = {userId}
                                    AND a.ProcessId IN (SELECT 
                                        id
                                    FROM
                                        ccpph.tede_process)";
            if (!string.IsNullOrWhiteSpace(request.Keyword))
            {
                sql += $@" AND (a.ProcessId IN (SELECT 
                                        Id
                                    FROM
                                        tede_process
                                    WHERE
                                        name LIKE '%{request.Keyword}%')
                                    OR a.Id IN (SELECT 
                                        FlowId
                                    FROM
                                        tede_flow_field_value
                                    WHERE
                                        StringValues LIKE '%{request.Keyword}%') 
                                        OR a.UserId IN (SELECT 
                                        Id
                                    FROM
                                        ccpph.tede_user
                                    WHERE
                                        Name LIKE '%{request.Keyword}%') or a.Id In(SELECT 
                                                                                    FlowId
                                                                                FROM
                                                                                    tede_flow_todo
                                                                                WHERE
                                                                                    UserId IN (SELECT 
                                                                                            Id
                                                                                        FROM
                                                                                            tede_user
                                                                                        WHERE
                                                                                            Name LIKE '%{request.Keyword}%')) 
                                                                        or a.Id In(SELECT 
                                                                    FlowId
                                                                FROM
                                                                    tede_flow_message
                                                                WHERE
                                                                    Message LIKE '%{request.Keyword}%'))";
            }
            if (request.ProcessIds.Count > 0)
            {
                var processIdValue = "";
                foreach (var item in request.ProcessIds)
                {
                    if (item == 0)
                        continue;
                    processIdValue += $"{item},";
                }
                if (!string.IsNullOrWhiteSpace(processIdValue))
                {
                    processIdValue = processIdValue.Remove(processIdValue.Length - 1, 1);
                    sql += $" and a.ProcessId in({processIdValue})";
                }
            }
            sql += "ORDER BY a.CreatedDate DESC";
            var databaseType = StringUtils.ToEnum<DatabaseType>(_databaseTypeStr, DatabaseType.MySql);
            var database = new Database(databaseType, _connectionString);
            var connection = database.GetConnection();
            return await connection.ExecuteScalarAsync<int>(sql);
        }
        /// <summary>
        /// 新增 未读数量
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="checking"></param>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<int> GetUReadCountByUserIdAsync(int userId, bool checking, FlowListRequest request)
        {
            var query = Q
                .Where(nameof(Flow.UserId), userId)
                .WhereNot(nameof(Flow.ProcessId), 0);

            if (checking)
            {
                query.WhereNotIn(nameof(Flow.State), new List<string>
                {
                    //FlowState.Meeting.GetValue(),
                    FlowState.Checked.GetValue(),
                    FlowState.Denied.GetValue()
                });
            }
            else
            {
                query.WhereIn(nameof(Flow.State), new List<string>
                {
                    //FlowState.Meeting.GetValue(),
                    FlowState.Checked.GetValue(),
                    FlowState.Denied.GetValue()
                });
            }

            if (request.ProcessIds != null && request.ProcessIds.Any())
            {
                query.WhereIn(nameof(Flow.ProcessId), request.ProcessIds);
            }

            // if (!string.IsNullOrEmpty(request.Keyword))
            // {
            //     var like = $"%{request.Keyword}%";
            //     query.WhereLike(nameof(Flow.ProcessId), like);
            // }
            #region 新增
            if (!string.IsNullOrEmpty(request.Keyword))
            {
                if (int.TryParse(request.Keyword, out int dd))
                {
                    var like = $"%{request.Keyword}%";
                    query.WhereLike(nameof(Flow.ProcessId), like);
                }
            }
            if (!string.IsNullOrWhiteSpace(request.Keyword))
            {
                //获取用户
                var users = await _userRepository.GetAllAsync(Q.WhereLike(nameof(Entity.User.Name), $"%{request.Keyword}%"));
                query.WhereIn(nameof(FlowTodo.UserId), users.Select(n => n.Id));
            }
            #endregion 
            return await _repository.CountAsync(query);
        }
        public async Task<IEnumerable<Flow>> GetListByUserIdAsync(int userId, bool checking, FlowListRequest request)
        {
            string sql = $@"SELECT 
                                a.*
                            FROM
                                ccpph.tede_flow a  
                            WHERE 1=1 ";
            if (!checking)
                sql += $@" and a.State IN ('Denied','Checked')";
            else
                sql += $@" and a.State Not IN ('Denied','Checked')";
            sql += $@" AND a.UserId = {userId}
                                    AND a.ProcessId IN (SELECT 
                                        id
                                    FROM
                                        ccpph.tede_process)";
            if (!string.IsNullOrWhiteSpace(request.Keyword))
            {
                sql += $@" AND (a.ProcessId IN (SELECT 
                                        Id
                                    FROM
                                        tede_process
                                    WHERE
                                        name LIKE '%{request.Keyword}%')
                                    OR a.Id IN (SELECT 
                                        FlowId
                                    FROM
                                        tede_flow_field_value
                                    WHERE
                                        StringValues LIKE '%{request.Keyword}%')
                                        OR a.UserId IN (SELECT 
                                        Id
                                    FROM
                                        ccpph.tede_user
                                    WHERE
                                        Name LIKE '%{request.Keyword}%') or a.Id In(SELECT 
                                                                                    FlowId
                                                                                FROM
                                                                                    tede_flow_todo
                                                                                WHERE
                                                                                    UserId IN (SELECT 
                                                                                            Id
                                                                                        FROM
                                                                                            tede_user
                                                                                        WHERE
                                                                                            Name LIKE '%{request.Keyword}%')) 
                                                                    or a.Id In(SELECT 
                                                                    FlowId
                                                                FROM
                                                                    tede_flow_message
                                                                WHERE
                                                                    Message LIKE '%{request.Keyword}%'))";
            }
            if (request.ProcessIds.Count > 0)
            {
                var processIdValue = "";
                foreach (var item in request.ProcessIds)
                {
                    if (item == 0)
                        continue;
                    processIdValue += $"{item},";
                }
                if (!string.IsNullOrWhiteSpace(processIdValue))
                {
                    processIdValue = processIdValue.Remove(processIdValue.Length - 1, 1);
                    sql += $" and a.ProcessId in({processIdValue})";
                }
            }
            sql += $"ORDER BY a.CreatedDate DESC limit {(request.Page - 1) * request.PerPage},{request.PerPage}";

            var databaseType = StringUtils.ToEnum<DatabaseType>(_databaseTypeStr, DatabaseType.MySql);
            var database = new Database(databaseType, _connectionString);
            var connection = database.GetConnection();
            var items = await connection.QueryAsync<Flow>(sql);
            //result.Total = await connection.ExecuteScalarAsync<int>(AssembleSqlCount(request));
            return items;
        }

        public async Task<IEnumerable<Flow>> GetListByIdListAsync(IEnumerable<int> flowIdList, int page, int perPage)
        {
            return await _repository.GetAllAsync(Q
                .WhereIn(nameof(Flow.Id), flowIdList)
                .WhereNot(nameof(Flow.ProcessId), 0)
                .ForPage(page, perPage)
                .OrderByDesc(nameof(Flow.Id))
            );
        }

        public async Task<int> GetCountAsync(int processId, string state, string startDate, string endDate)
        {
            var query = Q
                .WhereNot(nameof(Flow.ProcessId), 0);

            if (processId > 0)
            {
                query.Where(nameof(Flow.ProcessId), processId);
            }

            if (!string.IsNullOrEmpty(state) && !StringUtils.EqualsIgnoreCase(state, "all"))
            {
                query.Where(nameof(Flow.State), state);
            }

            if (!string.IsNullOrEmpty(startDate) && !string.IsNullOrEmpty(endDate))
            {
                query.WhereBetween(nameof(Flow.CreatedDate), StringUtils.ToDateTime(startDate), StringUtils.ToDateTime(endDate));
            }

            return await _repository.CountAsync(query);
        }

        public async Task<IEnumerable<Flow>> GetListAllAsync(int page, int perPage, int processId, string state, string startDate, string endDate)
        {
            var query = Q
                .WhereNot(nameof(Flow.ProcessId), 0)
                .ForPage(page, perPage)
                .OrderByDesc(nameof(Flow.Id));

            if (processId > 0)
            {
                query.Where(nameof(Flow.ProcessId), processId);
            }

            if (!string.IsNullOrEmpty(state) && !StringUtils.EqualsIgnoreCase(state, "all"))
            {
                query.Where(nameof(Flow.State), state);
            }

            if (!string.IsNullOrEmpty(startDate) && !string.IsNullOrEmpty(endDate))
            {
                query.WhereBetween(nameof(Flow.CreatedDate), StringUtils.ToDateTime(startDate), StringUtils.ToDateTime(endDate));
            }

            return await _repository.GetAllAsync(query);
        }

        public async Task<bool> DeleteAsync(int flowId)
        {
            return await _repository.DeleteAsync(flowId);
        }
        
  

        public async Task UpdateStateAsync(int flowId, string state)
        {
            await _repository.UpdateAsync(Q
                .Set(nameof(Flow.State), state)
                .Where(nameof(Flow.Id), flowId)
            );
        }

        public async Task UpdateProcessNodeIdAsync(int flowId, int processNodeId)
        {
            await _repository.UpdateAsync(Q
                .Set(nameof(Flow.ProcessNodeId), processNodeId)
                .Where(nameof(Flow.Id), flowId)
            );
        }

        public async Task<int> CountAsync(SqlKata.Query query)
        {
            return await _repository.CountAsync(query);
        }
    }
}