using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Datory;
using GxPress.Common.AppOptions;
using GxPress.Common.Exceptions;
using GxPress.Common.Tools;
using GxPress.Entity.WorkProcess;
using GxPress.EnumConst;
using GxPress.Repository.Interface;
using GxPress.Repository.Interface.WorkProcess;
using GxPress.Result.Process;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using ProcessNode = GxPress.Entity.WorkProcess.ProcessNode;


namespace GxPress.Repository.Implement.WorkProcess
{
    public partial class ProcessRepository : IProcessRepository
    {
        private readonly Repository<Process> _repository;
        private readonly IMapper _mapper;
        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;
        public ProcessRepository(IOptionsMonitor<DatabaseOptions> dbOptionsAccessor, IMapper mapper,
            IDistributedCache cache,
            IProcessFieldRepository processFieldRepository,
            IProcessGroupRepository processGroupRepository,
            IProcessRequestLimitRepository processRequestLimitRepository,
            IProcessNodeRepository processNodeRepository,
            IRuleConditionRepository conditionRuleRepository,
            IRuleCarbonCopyRepository ruleCarbonCopyRepository,
            IRuleApproverCheckRepository ruleApproverCheckRepository,
            IDepartmentRepository departmentRepository,
            IRoleRepository roleRepository,
            IUserRepository userRepository)
        {
            var databaseType = StringUtils.ToEnum<DatabaseType>(dbOptionsAccessor.CurrentValue.DatabaseType, DatabaseType.MySql);
            var database = new Database(databaseType, dbOptionsAccessor.CurrentValue.ConnectionString);
            _repository = new Repository<Process>(database, cache);
            _mapper = mapper;

            _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 string CacheKey(int id) => $"{nameof(ProcessRepository)}:{id}";

        public async Task<int> InsertAsync(Process process)
        {
            process.Id = 0;
            return await _repository.InsertAsync(process);
        }

        public async Task UpdateAsync(Process process)
        {
            await _repository.UpdateAsync(process, Q.CachingSet(CacheKey(process.Id)));
        }

        public async Task<Process> GetAsync(int processId)
        {
            return await _repository.GetAsync(Q
                .Where(nameof(Process.Id), processId)
            // .CachingGet(CacheKey(processId))
            );
        }

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

        public async Task<IEnumerable<Process>> GetListAsync(string keyword)
        {
            var result = await _repository.GetAllAsync(Q
                .OrderByDesc(nameof(Process.Sort))
                .OrderBy(nameof(Process.Id)));
            foreach (var item in result)
                item.IconUrl = StringUtils.AddDomain(item.IconUrl);
            return result;
        }
        public async Task<IEnumerable<Process>> GetAllAsync(SqlKata.Query query)
        {
            return await _repository.GetAllAsync(query);
        }
        public async Task<IEnumerable<Process>> GetListByGroupIdAsync(int groupId)
        {
            var result = await _repository.GetAllAsync(Q
                .Where(nameof(Process.GroupId), groupId)
                .OrderByDesc(nameof(Process.Sort))
                .OrderBy(nameof(Process.Id))
            );
            foreach (var item in result)
                item.IconUrl = StringUtils.AddDomain(item.IconUrl);
            return result;
        }

        public async Task<IEnumerable<Process>> GetListByDepartmentIdAsync(int departmentId)
        {
            var departmentIds = await GetDepartmentIdsAsync(departmentId, new List<int>());
            var processIdList = await _processRequestLimitRepository.GetProcessIdListByDepartmentIdAsync(departmentIds);
            var result = await _repository.GetAllAsync(Q
                .WhereIn(nameof(Process.Id), processIdList)
                .OrWhere(nameof(Process.LimitType), nameof(LimitTypeConst.NoLimit))
                .OrderByDesc(nameof(Process.Sort))
                .OrderBy(nameof(Process.Id))
            );
            foreach (var item in result)
                item.IconUrl = StringUtils.AddDomain(item.IconUrl);
            return result;
        }


        public async Task<List<int>> GetDepartmentIdsAsync(int departmentId, List<int> departmentIds)
        {
            var department = await _departmentRepository.GetAsync(departmentId);
            departmentIds.Add(department.Id);
            if (department != null && department.ParentId > 0)
            {
                department = await _departmentRepository.GetAsync(department.ParentId);
                departmentIds.Add(department.Id);
                if (department.ParentId > 0)
                    await GetDepartmentIdsAsync(department.ParentId, departmentIds);
            }
            return departmentIds;
        }

        public async Task<ProcessBaseInfoResult> GetBaseInfoAsync(int id)
        {
            var process = await _repository.GetAsync(id);
            if (process == null) throw new BusinessException("该流程不存在");
            var result = _mapper.Map<ProcessBaseInfoResult>(process);
            return result;
        }

        public async Task<List<ProcessFormSettingResult>> GetFormSettingAsync(int id)
        {
            var fields = await _processFieldRepository.GetListAsync(id);

            var result = _mapper.Map<List<ProcessFormSettingResult>>(fields);

            return result;
        }

        public async Task<ProcessNodeTreeResult> GetNodeTreeAsync(int id)
        {
            var nodeList = await _processNodeRepository.GetListAsync(id, 0);
            return await ListToTreeAsync(nodeList.FirstOrDefault(x => x.ParentId == 0), nodeList);
        }

        private async Task<ProcessNodeTreeResult> ListToTreeAsync(ProcessNode node, IEnumerable<ProcessNode> all)
        {
            var model = new ProcessNodeTreeResult
            {
                Id = node.Id,
                Name = node.Name,
                Type = node.Type
            };

            var children = all.Where(m => m.ParentId == model.Id);
            foreach (var child in children)
            {
                model.Children.Add(await ListToTreeAsync(child, all));
            }

            return model;
        }

        public async Task<bool> DeleteAsync(int id)
        {
            return await _repository.DeleteAsync(Q
                .Where(nameof(Process.Id), id)
                .CachingRemove(CacheKey(id))
            ) == 1;
        }
    }
}