using DocumentFormat.OpenXml.Office2010.Excel;
using Exam.Application.Interface.Exam;
using Exam.Infrastructure.Data.Entity;
using Exam.Infrastructure.Enums;
using Exam.Infrastructure.Extensions;
using Exam.Share.ViewResponses.Exam;
using Hotline.Application.Exam.Core.Extensions;
using Hotline.Application.Exam.Core.Utilities;
using Hotline.Application.Exam.Extensions;
using Hotline.Application.Exam.Interface.ExamManages;
using Hotline.Application.Exam.QueryExtensions.ExamManages;
using Hotline.Exams.ExamManages;
using Hotline.Exams.Questions;
using Hotline.Repository.SqlSugar;
using Hotline.Repository.SqlSugar.DataPermissions;
using Hotline.Repository.SqlSugar.Exam.Core.Constants;
using Hotline.Repository.SqlSugar.Exam.Extensions;
using Hotline.Repository.SqlSugar.Exam.Interfaces.ExamManages;
using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
using Hotline.Repository.SqlSugar.Exam.Repositories;
using Hotline.Repository.SqlSugar.Exam.Service;
using Hotline.Share.Dtos.TestPapers;
using Hotline.Share.Requests.Exam;
using Hotline.Share.Tools;
using Hotline.Share.ViewResponses.Exam;
using JiebaNet.Segmenter.Common;
using MapsterMapper;
using SqlSugar;
using XF.Domain.Authentications;
using XF.Domain.Dependency;
using ExamQuestion = Hotline.Exams.Questions.ExamQuestion;

namespace Hotline.Application.Exam.Service.ExamManages
{
    public class ExtractRuleService : ApiService<ExamExtractRule, AddExtractRuleDto, UpdateExtractRuleDto, HotlineDbContext>, IExtractRuleService, IScopeDependency
    {
        private readonly IExtractRuleRepository _repository;
        private readonly ITagQuestionRepository _tagQuestionRepository;
        private readonly IRuleTagRepository _ruleTagRepository;
        private readonly ISessionContext _sessionContext;
        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
        private readonly IServiceProvider _serviceProvider;
        private readonly IMapper _mapper;
        private AddExtractRuleDto _addExtractRuleDto;
        public ExtractRuleService(IExtractRuleRepository repository,
            ITagQuestionRepository tagQuestionRepository,
            IRuleTagRepository ruleTagRepository,
             ISessionContext sessionContext,
            IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
            IMapper mapper) : base(repository, mapper, sessionContext)
        {
            _repository = repository;
            _tagQuestionRepository = tagQuestionRepository;
            _ruleTagRepository = ruleTagRepository;
            this._sessionContext = sessionContext;
            _dataPermissionFilterBuilder = dataPermissionFilterBuilder;
            _serviceProvider = serviceProvider;
            _mapper = mapper;
        }

        #region public method
        /// <summary>
        /// 
        /// </summary>
        /// <param name="entityQueryRequest"></param>
        /// <returns></returns>
        public async Task<ExtractRuleDto> GetAsync(EntityQueryRequest entityQueryRequest)
        {
            var entity = await _repository.GetAsync(entityQueryRequest.Id);

            var extractRuleDto = _mapper.Map<ExtractRuleDto>(entity);

            if (extractRuleDto != null)
            {
                extractRuleDto.RuleTagDtos = await GetRuleTagDtos(entityQueryRequest);

                extractRuleDto.TagQuestionDtos = await GetTagQuestions(entityQueryRequest);

                var tagQuestionCounts = await GetTagQuestionCountAsync(new TagQuestionRequest
                {
                    TagIds = extractRuleDto.RuleTagDtos.Select(x=>x.TagId).ToList()
                });

                extractRuleDto.TagQuestionDtos.ForEach(item =>
                {
                    var tagQuestionCount = tagQuestionCounts.FirstOrDefault(m => m.TagId == item.TagId && m.QuestionType == item.QuestionType);

                    item.TotalCount = tagQuestionCount?.TotalCount ?? 0;
                });
            }


            return extractRuleDto;
        }

        public async Task<(int, List<ExtractRuleViewResponse>)> GetListAsync(ExtractRulePagedRequest queryRequest)
        {
            queryRequest.Status = queryRequest.Status ?? Share.Enums.Exams.EPublicStatus.Valid;

            ISugarQueryable<ExtractRuleViewResponse> queryable = QueryResult(queryRequest);

            var result = await queryable.ToListAsync();

            var total = await queryable.CountAsync();

            return (total, result);
        }

        public async Task<PageViewResponse<ExtractRuleViewResponse>> GetPagedListAsync(ExtractRulePagedRequest queryRequest)
        {
            ISugarQueryable<ExtractRuleViewResponse> queryable = QueryResult(queryRequest);

            var list = await queryable.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
            var total = await queryable.CountAsync();

            var result = new ExtractRulePageViewResponse
            {
                Items = list,
                Pagination = new Pagination(queryRequest.PageIndex, queryRequest.PageSize, total)
            };

            return result;
        }

        /// <summary>
        /// 新增抽题规则
        /// </summary>
        /// <param name="actionRequest"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override async Task<string> AddAsync(AddExtractRuleDto actionRequest, CancellationToken cancellationToken)
        {
            base.StartTran();

            var id = await base.AddAsync(actionRequest, cancellationToken);

            ResolveExtractRuleId(actionRequest, id);

            base.Entity.RuleTags = await AddRuleTags(actionRequest, cancellationToken);

            base.Entity.TagQuestions = await AddTagQuestions(actionRequest, cancellationToken);

            await base.Complete(base.Entity, OperationConstant.Create);

            return id;
        }

        /// <summary>
        /// 修改抽题规则
        /// </summary>
        /// <param name="actionRequest"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override async Task UpdateAsync(UpdateExtractRuleDto actionRequest, CancellationToken cancellationToken)
        {
            base.StartTran();

            await base.UpdateAsync(actionRequest, cancellationToken);

            ResolveExtractRuleId(actionRequest, actionRequest.Id);

            ResolveAddExtractRuleDto(actionRequest);

            base.Entity.RuleTags = await ModifyRuleTags(actionRequest, cancellationToken);

            base.Entity.TagQuestions = await ModifyTagQuestions(actionRequest, cancellationToken);

            await base.Complete(base.Entity, OperationConstant.Update);
        }

        private void ResolveAddExtractRuleDto(UpdateExtractRuleDto actionRequest)
        {
            _addExtractRuleDto = _mapper.Map<AddExtractRuleDto>(actionRequest);

            //_addExtractRuleDto.RuleTagDtos = new List<AddRuleTagDto>();

            //actionRequest.RuleTagDtos.ToList().ForEach(item =>
            //{
            //    _addExtractRuleDto.RuleTagDtos.Add(_mapper.Map<AddRuleTagDto>(item));
            //});

            //_addExtractRuleDto.TagQuestionDtos = new List<AddTagQuestionDto>();

            //actionRequest.TagQuestionDtos.ToList().ForEach(item =>
            //{
            //    _addExtractRuleDto.TagQuestionDtos.Add(_mapper.Map<AddTagQuestionDto>(item));
            //});
        }

        /// <summary>
        /// 删除抽题规则
        /// </summary>
        /// <param name="entityQueryRequest"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override async Task DeleteAsync(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
        {
            await base.DeleteAsync(entityQueryRequest, cancellationToken);

            var tempEntityQueryRequest = ExpressionableUtility.CreateExpression<ExamTagQuestion>()
                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.RuleId == entityQueryRequest.Id)
                .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.RuleId)).ToEntityQueryRequest<ExamTagQuestion>();

            await DeleteTagQuestions(tempEntityQueryRequest, cancellationToken);

            tempEntityQueryRequest = ExpressionableUtility.CreateExpression<ExamRuleTag>()
                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.RuleId == entityQueryRequest.Id)
                .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.RuleId)).ToEntityQueryRequest<ExamRuleTag>();


            await DeleteRuleTags(tempEntityQueryRequest, cancellationToken);
        }

        /// <summary>
        /// 获取标题试题数
        /// </summary>
        /// <param name="tagQuestionRequest"></param>
        /// <returns></returns>
        public async Task<List<TagQuestionViewResponse>> GetTagQuestionCount(TagQuestionRequest tagQuestionRequest)
        {
            if (tagQuestionRequest.TagIds.IsNullOrEmpty()) return new List<TagQuestionViewResponse>();
            return await GetTagQuestionCountAsync(tagQuestionRequest);
        }
        #endregion

        #region private method
        /// <summary>
        /// 获取查询结果
        /// </summary>
        /// <param name="queryRequest"></param>
        /// <returns></returns>
        private ISugarQueryable<ExtractRuleViewResponse> QueryResult(ExtractRulePagedRequest queryRequest)
        {
            var expression = queryRequest.GetExpression();
            var query = _repository.Queryable().Where(expression);
            var querable = query.OrderBy(o => o.SortIndex).Select(m => new ExtractRuleViewResponse
            {
                Name = m.Name,
                Id = m.Id,
                Status = m.Status,
                Code = m.Code,
                SortIndex = m.SortIndex,
                Remark = m.Remark,
                ExamType = m.RuleType
            });
            return querable;
        }

        /// <summary>
        /// 新增标签试题
        /// </summary>
        /// <param name="actionRequest"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task<List<ExamTagQuestion>> AddTagQuestions(AddExtractRuleDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.TagQuestionDtos == null) return null;

            actionRequest.TagQuestionDtos.ResolveOperationStatus();

            var tagQuestionDtos = actionRequest.TagQuestionDtos.Where(x => x.OperationStatus == EEOperationStatus.Add && x.Count!=null).ToList();

            var tagQuestions = _mapper.Map<List<ExamTagQuestion>>(tagQuestionDtos);

            tagQuestions.ToInsert(_sessionContext);

            await _tagQuestionRepository.ValidateAddAsync(tagQuestions, cancellationToken);

            return tagQuestions;
        }

        /// <summary>
        /// 新增规则标签
        /// </summary>
        /// <param name="actionRequest"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task<List<ExamRuleTag>> AddRuleTags(AddExtractRuleDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.RuleTagDtos == null) return null;

            actionRequest.RuleTagDtos.ResolveOperationStatus();

            var ruleTagDtos = actionRequest.RuleTagDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();

            var ruleTags = _mapper.Map<List<ExamRuleTag>>(ruleTagDtos);

            ruleTags.ToInsert(_sessionContext);

            await _ruleTagRepository.ValidateAddAsync(ruleTags, cancellationToken);

            return ruleTags;

        }

        /// <summary>
        /// 修改标签试题数
        /// </summary>
        /// <param name="actionRequest"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        private async Task<List<ExamTagQuestion>> ModifyTagQuestions(UpdateExtractRuleDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.TagQuestionDtos == null) return null;

            //var all = await _tagQuestionRepository.Queryable().Where(m => m.RuleId == actionRequest.Id).ToListAsync();

            //actionRequest.TagQuestionDtos.ResolveOperationStatus(all);

            var entityQueyRequest = ExpressionableUtility.CreateExpression<ExamTagQuestion>()
               .AndIF(actionRequest.Id.IsNotNullOrEmpty(), x => x.RuleId == actionRequest.Id).ToEntityQueryRequest<ExamTagQuestion>();

            await DeleteTagQuestions(entityQueyRequest, cancellationToken);

            var tagQuestions = new List<ExamTagQuestion>();

            tagQuestions.AddRangeExt(await AddTagQuestions(_addExtractRuleDto, cancellationToken));

            //tagQuestions.AddRangeExt(await UpdateTagQuestions(actionRequest, all, cancellationToken));

            //var ruleTagDtos = actionRequest.TagQuestionDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete).ToList();
            //var ids = ruleTagDtos.Select(x => x.Id).ToList();
            //var entityQueyRequest = ExpressionableUtility.CreateExpression<TagQuestion>()
            //    .AndIF(ids.IsNotNull(), x => ids.Contains(x.Id)).ToEntityQueryRequest<TagQuestion>();

            await DeleteTagQuestions(entityQueyRequest, cancellationToken);

            return tagQuestions;
        }

        /// <summary>
        /// 修改标签试题数
        /// </summary>
        /// <param name="actionRequest"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        private async Task<List<ExamTagQuestion>> UpdateTagQuestions(UpdateExtractRuleDto actionRequest, List<ExamTagQuestion> all, CancellationToken cancellationToken)
        {
            if (actionRequest.TagQuestionDtos == null) return null;

            var tagQuestionDtos = actionRequest.TagQuestionDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();
            var ids = tagQuestionDtos.Select(x => x.Id).ToList();
            var tagQuestions = all.Where(x => ids.Contains(x.Id)).ToList();

            var entitys = new List<ExamTagQuestion>();
            tagQuestionDtos.ForEach(x =>
            {
                var entity = tagQuestions.FirstOrDefault(x => x.Id == x.Id);
                entity = _mapper.Map(x, entity);
                if (entity != null)
                {
                    entity.RuleId = actionRequest.Id;
                    entitys.Add(entity);
                }
                
            });

            entitys.ToUpdate(_sessionContext);

            await _tagQuestionRepository.ValidateUpdateAsync(entitys, cancellationToken);

            return entitys;
        }

        /// <summary>
        /// 修改规则标签
        /// </summary>
        /// <param name="actionRequest"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        private async Task<List<ExamRuleTag>> ModifyRuleTags(UpdateExtractRuleDto actionRequest, CancellationToken cancellationToken)
        {
            var ruleId = actionRequest.Id;

            if (actionRequest.RuleTagDtos == null) return null;

            //var all = await _ruleTagRepository.Queryable().Where(x => x.RuleId == actionRequest.Id).ToListAsync();

            //actionRequest.RuleTagDtos.ResolveOperationStatus(all);

            var ruleTags = new List<ExamRuleTag>();

            var entityQueyRequest = ExpressionableUtility.CreateExpression<ExamRuleTag>()
                .AndIF(ruleId.IsNotNullOrEmpty(), x => x.RuleId == ruleId).ToEntityQueryRequest<ExamRuleTag>();

            await DeleteRuleTags(entityQueyRequest, cancellationToken);

            ruleTags.AddRangeExt(await AddRuleTags(_addExtractRuleDto, cancellationToken));

            //ruleTags.AddRangeExt(await UpdateRuleTags(actionRequest, cancellationToken));

            //var ruleTagDtos = actionRequest.RuleTagDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete).ToList();
            //var ids = ruleTagDtos.Select(x => x.Id).ToList();
            //var entityQueyRequest = ExpressionableUtility.CreateExpression<RuleTag>()
            //    .AndIF(ids.IsNotNull(), x => ids.Contains(x.Id)).ToEntityQueryRequest<RuleTag>();

            return ruleTags;
        }

        private async Task<List<ExamRuleTag>> UpdateRuleTags(UpdateExtractRuleDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.RuleTagDtos == null) return null;

            var ruleTagDtos = actionRequest.RuleTagDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();
            var ids = ruleTagDtos.Select(x => x.Id).ToList();
            var ruleTags = await _ruleTagRepository.Queryable().Where(x => ids.Contains(x.Id)).ToListAsync();

            var entitys = new List<ExamRuleTag>();
            ruleTagDtos.ForEach(x =>
            {
                var entity = ruleTags.FirstOrDefault(x => x.Id == x.Id);
                entity = _mapper.Map(x, entity);
                if (entity != null)
                {
                    entity.RuleId = actionRequest.Id;
                    entitys.Add(entity);
                }
               
            });

            entitys.ToUpdate(_sessionContext);

            await _ruleTagRepository.ValidateUpdateAsync(entitys, cancellationToken);

            return entitys;

        }


        /// <summary>
        /// 删除规则标签
        /// </summary>
        /// <param name="tempEntityQueryRequest"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task DeleteRuleTags(EntityQueryRequest tempEntityQueryRequest, CancellationToken cancellationToken)
        {
            await _ruleTagRepository.DeleteWithValidateAsync(tempEntityQueryRequest, cancellationToken);
        }

        /// <summary>
        /// 删除标签试题数
        /// </summary>
        /// <param name="tempEntityQueryRequest"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task DeleteTagQuestions(EntityQueryRequest tempEntityQueryRequest, CancellationToken cancellationToken)
        {
            await _tagQuestionRepository.DeleteWithValidateAsync(tempEntityQueryRequest, cancellationToken);
        }

        /// <summary>
        /// 获取规则标签
        /// </summary>
        /// <param name="entityQueryRequest"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        private async Task<List<RuleTagDto>> GetRuleTagDtos(EntityQueryRequest entityQueryRequest)
        {
            var ruleTagTable = _ruleTagRepository.Queryable().Where(x => x.RuleId == entityQueryRequest.Id);

            var examTagTable = new ExamRepository<ExamTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();


            var queryable = ruleTagTable.LeftJoin(examTagTable, (r, e) => r.TagId == e.Id).Select((r, e) => new RuleTagDto
            {
                Id = r.Id,
                TagId = r.TagId,
                Tag = e.Name
            });

            return await queryable.ToListAsync();

        }

        /// <summary>
        /// 获取标签试题数
        /// </summary>
        /// <param name="entityQueryRequest"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        private async Task<List<TagQuestionDto>> GetTagQuestions(EntityQueryRequest entityQueryRequest)
        {
            var tagQuestionTable = _tagQuestionRepository.Queryable().Where(x => x.RuleId == entityQueryRequest.Id);

            var examTagTable = new ExamRepository<ExamTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();


            var tagQuestionDtos = tagQuestionTable.LeftJoin(examTagTable, (t, e) => t.TagId == e.Id).Select((t, e) => new TagQuestionDto
            {
                Id = t.Id,
                TagId = t.TagId,
                QuestionType = t.QuestionType,
                Count = t.Count,
                Tag = e.Name
            });

            return await tagQuestionDtos.ToListAsync();
        }

        /// <summary>
        /// 处理抽题规则Id
        /// </summary>
        /// <param name="actionRequest"></param>
        /// <param name="id"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void ResolveExtractRuleId(AddExtractRuleDto actionRequest, string id)
        {
            actionRequest.TagQuestionDtos.ForEach(x => x.RuleId = id);

            actionRequest.RuleTagDtos.ForEach(x => x.RuleId = id);
        }

        /// <summary>
        /// 处理抽题规则Id
        /// </summary>
        /// <param name="actionRequest"></param>
        /// <param name="id"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void ResolveExtractRuleId(UpdateExtractRuleDto actionRequest, string id)
        {
            actionRequest.TagQuestionDtos.ForEach(x => x.RuleId = id);

            actionRequest.RuleTagDtos.ForEach(x => x.RuleId = id);
        }

        private async Task<List<TagQuestionViewResponse>> GetTagQuestionCountAsync(TagQuestionRequest tagQuestionRequest)
        {
            var expression = tagQuestionRequest.GetExpression();
            var questionTagTable = new ExamRepository<ExamQuestionTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(expression);
            var questionTable = new ExamRepository<ExamQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
            var queryable = questionTagTable.LeftJoin(questionTable, (t, q) => t.QuestionId == q.Id)
                .GroupBy((t, q) => new { t.TagId, q.QuestionType })
                .OrderBy((t, q) => t.TagId)
                .Select((t, q) => new TagQuestionViewResponse
                {
                    TagId = t.TagId,
                    QuestionType = q.QuestionType,
                    TotalCount = SqlFunc.AggregateCount(q.Id != null)
                });

            return await queryable.ToListAsync();
        }
        #endregion
    }
}
