using Exam.Infrastructure.Data.Entity;
using Exam.Infrastructure.Data.Interface;
using Exam.Repository.Sqlsugar;
using Exam.Share;
using Exam.Share.ViewResponses.Exam;
using Exam.Share.ViewResponses.Practices;
using Hotline.Application.Exam.Core.Constants;
using Hotline.Application.Exam.Core.Extensions;
using Hotline.Application.Exam.Core.Utilities;
using Hotline.Application.Exam.QueryExtensions.Practices;
using Hotline.Application.Exam.QueryExtensions.TestPapers;
using Hotline.Exams.Practices;
using Hotline.Exams.Sourcewares;
using Hotline.Exams.TestPapers;
using Hotline.Repository.SqlSugar;
using Hotline.Repository.SqlSugar.DataPermissions;
using Hotline.Repository.SqlSugar.Exam.Core.Constants;
using Hotline.Repository.SqlSugar.Exam.Interfaces.Practices;
using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
using Hotline.Share.Dtos.Practices;
using Hotline.Share.Dtos.Questions;
using Hotline.Share.Dtos.TestPapers;
using Hotline.Share.Requests.Exam;
using Hotline.Share.Requests.TestPaper;
using Hotline.Share.Requests.Train;
using Hotline.Share.ViewResponses;
using Hotline.Share.ViewResponses.Exam;
using Hotline.Share.ViewResponses.Practices;
using MapsterMapper;
using NPOI.OpenXmlFormats.Dml;
using NPOI.SS.Formula.Functions;
using SqlSugar;
using System.Linq;
using Hotline.Application.Exam.Interface.Practices;
using Hotline.Exams.Questions;
using Hotline.Repository.SqlSugar.Exam.Repositories;
using XF.Domain.Authentications;
using XF.Domain.Dependency;
using Hotline.Repository.SqlSugar.Exam.Service;
using Hotline.Repository.SqlSugar.Exam.Extensions;
using Exam.Infrastructure.Extensions;
using Consul;
using J2N.Collections.Generic.Extensions;

namespace Hotline.Application.Exam.Service.Practices
{
    public class PracticeService : ApiService<ExamPractice, AddPracticeDto, UpdatePracticeDto, HotlineDbContext>, IPracticeService, IScopeDependency
    {
        private readonly IPracticeRepository _repository;
        private readonly IPracticeRecordRepository _practiceRecordRepository;
        private readonly IPracticeQuestionOptionsRepository _practiceQuestionOptionsRepository;
        private readonly IPracticeQuestionRepository _practiceQuestionRepository;
        private readonly IPracticeQuestionSourcewareRepository _practiceQuestionSourcewareRepository;
        private readonly IPracticeQuestionKnowladgeRepository _practiceQuestionKnowladgeRepository;
        private readonly IPracticeResultItemRepository _practiceResultItemRepository;
        private readonly IPracticeResultRepository _practiceResultRepository;
        private readonly IPracticeAnswerRepository _practiceAnswerRepository;
        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
        private readonly IServiceProvider _serviceProvider;
        private readonly ISessionContext _sessionContext;
        private readonly IMapper _mapper;
        public PracticeService(IPracticeRepository repository,
            IPracticeRecordRepository practiceRecordRepository,
            IPracticeQuestionOptionsRepository practiceQuestionOptionsRepository,
            IPracticeQuestionRepository practiceQuestionRepository,
            IPracticeQuestionSourcewareRepository practiceQuestionSourcewareRepository,
            IPracticeQuestionKnowladgeRepository practiceQuestionKnowladgeRepository,
            IPracticeResultItemRepository practiceResultItemRepository,
            IPracticeResultRepository practiceResultRepository,
            IPracticeAnswerRepository practiceAnswerRepository,
            IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
            ISessionContext sessionContext,
            IMapper mapper) : base(repository, mapper, sessionContext)
        {
            this._repository = repository;
            this._practiceRecordRepository = practiceRecordRepository;
            this._practiceQuestionOptionsRepository = practiceQuestionOptionsRepository;
            this._practiceQuestionRepository = practiceQuestionRepository;
            this._practiceQuestionSourcewareRepository = practiceQuestionSourcewareRepository;
            this._practiceQuestionKnowladgeRepository = practiceQuestionKnowladgeRepository;
            this._practiceResultItemRepository = practiceResultItemRepository;
            this._practiceResultRepository = practiceResultRepository;
            this._practiceAnswerRepository = practiceAnswerRepository;
            this._dataPermissionFilterBuilder = dataPermissionFilterBuilder;
            this._serviceProvider = serviceProvider;
            this._sessionContext = sessionContext;
            this._mapper = mapper;
        }

        #region public method
        public async Task<PracticeDto> GetAsync(EntityQueryRequest entityQueryRequest)
        {
            throw new NotImplementedException();
        }

        public async Task<(int, List<PracticeViewResponse>)> GetListAsync(PracticePagedRequest queryRequest)
        {
            SqlSugar.ISugarQueryable<PracticeViewResponse> queryResult = QueryResult(queryRequest);

            var total = await queryResult.CountAsync();
            var items = await queryResult.ToListAsync();

            return (total, items);
        }

        public async Task<PageViewResponse<PracticeViewResponse>> GetPagedListAsync(PracticePagedRequest queryRequest)
        {
            SqlSugar.ISugarQueryable<PracticeViewResponse> queryResult = QueryResult(queryRequest);

            var total = await queryResult.CountAsync();
            var items = await queryResult.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);

            return new PracticePageViewResponse
            {
                Items = items,
                Pagination = new Pagination(queryRequest.PageIndex, queryRequest.PageSize, total)
            };
        }

        public override async Task<string> AddAsync(AddPracticeDto actionRequest, CancellationToken cancellationToken)
        {
            var questions = await ExactractQuestion(actionRequest);
            base.StartTran();
            actionRequest.Code = await GenerateCode(ExamBusiConstants.PracticeCode,3);
            var id = await base.AddAsync(actionRequest, cancellationToken);
            var addPracticeQuestionDtos = new List<AddPracticeQuestionDto>();
            questions.ForEach(item =>
            {
                var addPracticeQuestionDto = _mapper.Map<AddPracticeQuestionDto>(item);
                addPracticeQuestionDto.PracticeId = id;
                addPracticeQuestionDto.QuestionId = item.Id;
                addPracticeQuestionDtos.Add(addPracticeQuestionDto);
            });

            var practiceRecord = await AddPracticeRecord(id, cancellationToken);

            base.Entity.PracticeRecords = new List<ExamPracticeRecord>();
                base.Entity.PracticeRecords.Add(practiceRecord); 

            base.Entity.PracticeQuestions = await AddPracticeQuestions(addPracticeQuestionDtos, cancellationToken);

            var practiceQuestionOptions = await AddPracticeQuestionOptions(addPracticeQuestionDtos, cancellationToken);

            var practiceQuestionKnowladges = await AddPracticeQuestionKnowladgeAsync(addPracticeQuestionDtos, cancellationToken);

            var practiceQuestionSourcewares = await AddPracticeQuestionSourcewareAsync(addPracticeQuestionDtos, cancellationToken);

            base.Entity.PracticeQuestions.ForEach(item =>
            {
                item.PracticeQuestionKnowladges = practiceQuestionKnowladges.Where(x => item.Id == x.PracticeQuestionId).ToList();
                item.PracticeQuestionOptionses = practiceQuestionOptions.Where(x => item.Id == x.PracticeQuestionId).ToList();
                item.PracticeQuestionSourcewares = practiceQuestionSourcewares.Where(x => item.Id == x.PracticeQuestionId).ToList();
            });

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

            return id;
        }


        public async Task<List<PracticeQuestionViewResponse>> GetPracticeQuestionViewResponses(PracticeQuestionGroupRequest practiceQuestionGroupRequest)
        {
            var expression = practiceQuestionGroupRequest.GetExpression();
            var practiceQuestionTable = _practiceQuestionRepository.Queryable().Where(expression);

            var queryable = await practiceQuestionTable.ToListAsync();

            var result = queryable.GroupBy(x => x.QuestionType).Select(m => new PracticeQuestionViewResponse
            {
                QuestionType = m.Key,
                Questions = m.Select(n => new SimpleViewResponse
                {
                    Id = n.Id
                }).ToList()
            }).ToList();

            return result;
        }

        public async Task<List<PracticeQuestionViewResponse>> GetViewPracticeQuestions(PracticeQuestionGroupRequest practiceQuestionGroupRequest)
        {
            var expression = practiceQuestionGroupRequest.GetExpression();
            var practiceQuestionTable = _practiceQuestionRepository.Queryable().Where(expression);
            var practiceResultTable = _practiceResultRepository.Queryable();

            var queryable = await practiceQuestionTable
                .LeftJoin(practiceResultTable, (q, r) => q.Id == r.PracticeQuestionId)
                .Select((q, r) => new GradingExamViewResponse
                {
                    Id = q.Id,
                    IsCorrect = r.IsCorrect,
                    QuestionType = q.QuestionType
                })
                .ToListAsync();

            var result = queryable.GroupBy(x => x.QuestionType).Select(m => new PracticeQuestionViewResponse
            {
                QuestionType = m.Key,
                Questions = m.GroupBy(g=>g.Id).Select(n => new SimpleViewResponse
                {
                    Id = n.Key
                }).ToList()
            }).ToList();

            return result;
        }


        public async Task<PracticeQuestionDto> GetPracticeQuestion(PracticeQuestionRequest practiceQuestionRequest)
        {
            PracticeQuestionDto practiceQuestionDto = await QueryPracticeQuestion(practiceQuestionRequest);

            return practiceQuestionDto;
        }



        public async Task<ViewPracticeQuestionDto> ViewPracticeQuestion(PracticeQuestionRequest practiceQuestionRequest)
        {
            var practiceQuestionDto = await QueryPracticeQuestion(practiceQuestionRequest);

            var viewPracticeQuestionDto = _mapper.Map<ViewPracticeQuestionDto>(practiceQuestionDto);

            if (viewPracticeQuestionDto != null)
            {
               

                viewPracticeQuestionDto.Answer = string.Join(",", viewPracticeQuestionDto.PracticeQuestionOptionsDtos.Where(x => x.IsAnswer).Select(m => m.Label));
            }

            return viewPracticeQuestionDto;
        }

        public async Task<PracticeQuestionDto> Practice(SubmitPracticeDto submitPracticeDto, CancellationToken cancellationToken)
        {
            var current = await _practiceQuestionRepository.GetAsync(submitPracticeDto.PracticeQuestionId);

            var practiceResult = await AddPracticeResult(submitPracticeDto, cancellationToken);

            practiceResult.PracticeResultItems = await AddPracticeResultItem(submitPracticeDto, cancellationToken);

            await _practiceResultRepository.AddNav(practiceResult).Include(x => x.PracticeResultItems).ExecuteCommandAsync();

            var nextId = await _practiceQuestionRepository.Queryable().Where(x => x.PracticeId == current.PracticeId && x.SortIndex > current.SortIndex).OrderBy(x => x.SortIndex).Select(x => x.Id).FirstAsync();

            var practiceQuestionRequest = new PracticeQuestionRequest
            {
                PracticeQuestionId = nextId
            };

            return await QueryPracticeQuestion(practiceQuestionRequest);
        }

        private async Task<ExamPracticeResult> AddPracticeResult(SubmitPracticeDto submitPracticeDto, CancellationToken cancellationToken)
        {
            var practiceQuetionOptions = await _practiceQuestionOptionsRepository.Queryable().Where(x => x.PracticeQuestionId == submitPracticeDto.PracticeQuestionId && x.IsAnswer).ToListAsync();

            var practiceRecordTable = _practiceRecordRepository.Queryable();
            var practiceQuestionTable = _practiceQuestionRepository.Queryable().Where(x => x.Id == submitPracticeDto.PracticeQuestionId);

            var practiceRecordId = await practiceRecordTable.InnerJoin(practiceQuestionTable, (p, q) => p.PracticeId == q.PracticeId)
                                .Select((p, q) => p.Id).FirstAsync();


            var practiceResult = new ExamPracticeResult
            {
                PracticeRecordId = practiceRecordId,
                PracticeQuestionId = submitPracticeDto.PracticeQuestionId,
                IsCorrect = submitPracticeDto.PracticeResultItemDtos.Select(x => x.QuestionOptionId).All(m => practiceQuetionOptions.Any(t => t.QuestionOptionId == m))
            };

            practiceResult.ToInsert(_sessionContext);

            await _practiceResultRepository.ValidateAddAsync(practiceResult, cancellationToken);

            submitPracticeDto.PracticeResultId = practiceResult.Id;

            return practiceResult;
        }

        public async Task Complete(CompletePracticeRecordDto completePracticeRecordDto, CancellationToken cancellationToken)
        {
            var practiceRecord = await _practiceRecordRepository.Queryable().Where(x => x.PracticeId == completePracticeRecordDto.PracticeId && x.UserId == _sessionContext.UserId).FirstAsync();

            practiceRecord.IsComplete = true;

            practiceRecord.ToUpdate(_sessionContext);

            await _practiceRecordRepository.ValidateUpdateAsync(practiceRecord, cancellationToken);
        }
        #endregion

        #region private method

        private async Task<List<ExamPracticeResultItem>> AddPracticeResultItem(SubmitPracticeDto submitPracticeDto, CancellationToken cancellationToken)
        {
            if (submitPracticeDto.PracticeResultItemDtos == null) return null;

            var practiceResultItems = new List<ExamPracticeResultItem>();

            submitPracticeDto.PracticeResultItemDtos.ForEach(item =>
            {
                var practiceResultItem = _mapper.Map<ExamPracticeResultItem>(item);
                practiceResultItem.PracticeResultId = submitPracticeDto.PracticeResultId;
                practiceResultItem.ToInsert(_sessionContext);
                practiceResultItems.Add(practiceResultItem);
            });

            await _practiceResultItemRepository.ValidateAddAsync(practiceResultItems, cancellationToken);

            return practiceResultItems;
        }
        private async Task<PracticeQuestionDto> QueryPracticeQuestion(PracticeQuestionRequest practiceQuestionRequest)
        {
            if (practiceQuestionRequest.PracticeQuestionId.IsNullOrEmpty()) return null;

            var practiceQuestion = await _practiceQuestionRepository.GetAsync(x => x.Id == practiceQuestionRequest.PracticeQuestionId);

            var practiceQuestionDto = _mapper.Map<PracticeQuestionDto>(practiceQuestion);

            practiceQuestionDto.PracticeQuestionOptionsDtos = await GetPracticeQuestionOptions(practiceQuestionRequest);

            var practiceResultItems = await GetPracticeResultItems(practiceQuestionRequest);

            if (practiceResultItems != null)
            {
                practiceQuestionDto.PracticeQuestionOptionsDtos.ForEach(item =>
                {        
                    item.IsSelected = practiceResultItems.Any(x => x.QuestionOptionId == item.QuestionOptionId);
                });
            }

            practiceQuestionDto.PracticeQuestionSourcewareDtos = await GetPracticeQuestionSourceware(practiceQuestionRequest);

            practiceQuestionDto.PracticeQuestionKnowladgeDtos = await GetPracticeQuestionKnowladge(practiceQuestionRequest);
            return practiceQuestionDto;
        }

        private async Task<List<ExamPracticeResultItem>> GetPracticeResultItems(PracticeQuestionRequest practiceQuestionRequest)
        {
            var practiceRecordRepository = new ExamRepository<ExamPracticeRecord>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var practiceRecordTable = practiceRecordRepository.Queryable().Where(x=>x.UserId == _sessionContext.UserId);

            var practiceResultItems = practiceRecordTable.InnerJoin<ExamPracticeResult>((p, r) => p.Id == r.PracticeRecordId)
                                      .InnerJoin<ExamPracticeResultItem>((p, r, i) => i.PracticeResultId == r.Id)
                                      .Where((p, r, i) => r.PracticeQuestionId == practiceQuestionRequest.PracticeQuestionId)
                                      .Select((p, r, i) => i);

            return await practiceResultItems.Distinct().ToListAsync();
        }

        private async Task<List<PracticeQuestionKnowladgeDto>> GetPracticeQuestionKnowladge(PracticeQuestionRequest practiceQuestionRequest)
        {
            var knowledgeRepository = new ExamRepository<KnowledgeBase.Knowledge>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var expression = practiceQuestionRequest.GetExpression();
            var practiceQuestionTable = _practiceQuestionRepository.Queryable().Where(expression);
            var practiceQuestionKnowladgeTable = _practiceQuestionKnowladgeRepository.Queryable();
            var knowlegdeTable = knowledgeRepository.Queryable();
            var queryResult = practiceQuestionTable.InnerJoin(practiceQuestionKnowladgeTable, (t, ts) => t.QuestionId == ts.QuestionId && t.Id == ts.PracticeQuestionId)
                .InnerJoin(knowlegdeTable, (t, ts, sw) => ts.KnowladgeId == sw.Id)
                .Select((t, ts, sw) => new PracticeQuestionKnowladgeDto
                {
                    KnowladgeId = ts.KnowladgeId,
                    QuestionId = ts.QuestionId,
                    Title = sw.Title,
                });


            return await queryResult.Distinct().ToListAsync();
        }

        private async Task<List<PracticeQuestionSourcewareDto>> GetPracticeQuestionSourceware(PracticeQuestionRequest practiceQuestionRequest)
        {

            var sourcewareRepository = new ExamRepository<ExamSourceware>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var expression = practiceQuestionRequest.GetExpression();
            var practiceQuestionTable = _practiceQuestionRepository.Queryable().Where(expression);
            var practiceQuestionSourcewareTable = _practiceQuestionSourcewareRepository.Queryable();
            var sourcewareTable = sourcewareRepository.Queryable();
            var queryResult = practiceQuestionTable.InnerJoin(practiceQuestionSourcewareTable, (t, ts) => t.QuestionId == ts.QuestionId && t.Id == ts.PracticeQuestionId)
                .InnerJoin(sourcewareTable, (t, ts, sw) => ts.SourcewareId == sw.Id)
                .Select((t, ts, sw) => new PracticeQuestionSourcewareDto
                {
                    AttachmentId = sw.AttachmentId,
                    SourcewareId = ts.SourcewareId,
                    QuestionId = ts.QuestionId,
                    Name = sw.Name,
                });


            return await queryResult.Distinct().ToListAsync();
        }

        private async Task<List<PracticeQuestionOptionsDto>> GetPracticeQuestionOptions(PracticeQuestionRequest practiceQuestionRequest)
        {
            var expression = practiceQuestionRequest.GetExpression();
            var practiceQuestionTable = _practiceQuestionRepository.Queryable().Where(expression);
            var practiceQuestionOptionTable = _practiceQuestionOptionsRepository.Queryable();

            var queryResult = practiceQuestionTable.InnerJoin(practiceQuestionOptionTable, (p, o) => p.QuestionId == o.QuestionId && p.Id == o.PracticeQuestionId)
                .OrderBy((p,o)=>o.Label)
                .Select((p, o) => new PracticeQuestionOptionsDto
                {
                    Content = o.Content,
                    Label = o.Label,
                    PracticeQuestionId = o.PracticeQuestionId,
                    QuestionOptionId = o.QuestionOptionId,
                    IsAnswer = o.IsAnswer
                });

            return await queryResult.Distinct().ToListAsync();
        }
        private SqlSugar.ISugarQueryable<PracticeViewResponse> QueryResult(PracticePagedRequest queryRequest)
        {
            var expression = queryRequest.GetExpression();
            var practiceTable = _repository.Queryable().Where(expression);
            var practiceRecordTable = _practiceRecordRepository.Queryable();

            var queryResult = practiceTable.InnerJoin(practiceRecordTable, (p, r) => p.Id == r.PracticeId).Select((p, r) => new PracticeViewResponse
            {
                Id = p.Id,
                Code = p.Code,
                Count = p.Count,
                PracticeTime = r.PracticeTime,
                PracticeType = p.PracticeType
            });
            return queryResult;
        }

        private async Task<List<ExamPracticeQuestionSourceware>> AddPracticeQuestionSourcewareAsync(List<AddPracticeQuestionDto> practiceQuestionDtos, CancellationToken cancellationToken)
        {
            var questionSourcewareRepository = new ExamRepository<ExamQuestionSourceware>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var quesitonIds = practiceQuestionDtos.Select(x => x.QuestionId);

            var questionSourceware = await questionSourcewareRepository.Queryable().Where(x => quesitonIds.Contains(x.QuestionId)).ToListAsync();

            var practiceQuestionSourcewares = new List<ExamPracticeQuestionSourceware>();

            practiceQuestionDtos.Where(x => x.QuestionType.CheckSelectType()).ToList().ForEach(x =>
            {
                var practiceQuestion = base.Entity?.PracticeQuestions.FirstOrDefault(n => n.QuestionId == x.QuestionId) ?? null;

                var options = questionSourceware.Where(n => x.QuestionId == n.QuestionId).ToList();

                if (options != null)
                {
                    options.ForEach(item =>
                    {
                        var practiceQuestionSourceware = _mapper.Map<ExamQuestionSourceware, ExamPracticeQuestionSourceware>(item);

                        practiceQuestionSourceware.PracticeQuestionId = practiceQuestion?.Id;

                        practiceQuestionSourceware.ToInsert(_sessionContext);

                        practiceQuestionSourcewares.Add(practiceQuestionSourceware);
                    });
                }
            });

            await _practiceQuestionSourcewareRepository.ValidateAddAsync(practiceQuestionSourcewares, cancellationToken);

            return practiceQuestionSourcewares;
        }

        private async Task<List<ExamPracticeQuestionKnowladge>> AddPracticeQuestionKnowladgeAsync(List<AddPracticeQuestionDto> practiceQuestionDtos, CancellationToken cancellationToken)
        {
            var questionKnowladgeRepository = new ExamRepository<ExamQuestionKnowladge>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var quesitonIds = practiceQuestionDtos.Select(x => x.QuestionId);

            var questionKnowladge = await questionKnowladgeRepository.Queryable().Where(x => quesitonIds.Contains(x.QuestionId)).ToListAsync();

            var practiceQuestionKnowladges = new List<ExamPracticeQuestionKnowladge>();

            practiceQuestionDtos.Where(x => x.QuestionType.CheckSelectType()).ToList().ForEach(x =>
            {
                var practiceQuestion = base.Entity?.PracticeQuestions.FirstOrDefault(n => n.QuestionId == x.QuestionId) ?? null;
                var options = questionKnowladge.Where(n => x.QuestionId == n.QuestionId).ToList();

                if (options != null)
                {
                    options.ForEach(item =>
                    {
                        var practiceQuestionKnowladge = _mapper.Map<ExamQuestionKnowladge, ExamPracticeQuestionKnowladge>(item);

                        practiceQuestionKnowladge.PracticeQuestionId = practiceQuestion?.Id;

                        practiceQuestionKnowladge.ToInsert(_sessionContext);

                        practiceQuestionKnowladges.Add(practiceQuestionKnowladge);
                    });
                }
            });

            await _practiceQuestionKnowladgeRepository.ValidateAddAsync(practiceQuestionKnowladges, cancellationToken);

            return practiceQuestionKnowladges;
        }

        private async Task<List<ExamPracticeQuestionOptions>> AddPracticeQuestionOptions(List<AddPracticeQuestionDto> practiceQuestionDtos, CancellationToken cancellationToken)
        {
            var questionOptionRepository = new ExamRepository<ExamQuestionOptions>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var quesitonIds = practiceQuestionDtos.Select(x => x.QuestionId);

            var questionOptions = await questionOptionRepository.Queryable().Where(x => quesitonIds.Contains(x.QuestionId)).ToListAsync();

            var practiceQuestionOptions = new List<ExamPracticeQuestionOptions>();

            practiceQuestionDtos.Where(x => x.QuestionType.CheckSelectType()).ToList().ForEach(x =>
            {
                var practiceQuestion = base.Entity?.PracticeQuestions.FirstOrDefault(n=> n.QuestionId == x.QuestionId) ?? null;
                var options = questionOptions.Where(n => x.QuestionId == n.QuestionId).ToList();

                if (options != null)
                {
                    options.ForEach(item =>
                    {
                        var practiceQuestionOption = _mapper.Map<ExamQuestionOptions, ExamPracticeQuestionOptions>(item);

                        practiceQuestionOption.PracticeQuestionId = practiceQuestion?.Id;

                        practiceQuestionOption.QuestionOptionId = item.Id;

                        practiceQuestionOption.ToInsert(_sessionContext);

                        practiceQuestionOptions.Add(practiceQuestionOption);
                    });
                }

            });

            await _practiceQuestionOptionsRepository.ValidateAddAsync(practiceQuestionOptions, cancellationToken);

            return practiceQuestionOptions;
        }

        private async Task<List<ExamPracticeQuestion>> AddPracticeQuestions(List<AddPracticeQuestionDto> addPracticeQuestionDtos, CancellationToken cancellationToken)
        {
            var practiceQuestions = new List<ExamPracticeQuestion>();

            var sortIndex = 0;

            addPracticeQuestionDtos.ForEach(item =>
            {
                var practiceQuestion = _mapper.Map<ExamPracticeQuestion>(item);
                practiceQuestion.SortIndex = sortIndex;
                practiceQuestions.Add(practiceQuestion);
                sortIndex++;
            });

            practiceQuestions.ToInsert(_sessionContext);

            await _practiceQuestionRepository.ValidateAddAsync(practiceQuestions, cancellationToken);

            return practiceQuestions;

        }

        private async Task<ExamPracticeRecord> AddPracticeRecord(string practiceId, CancellationToken cancellationToken)
        {
            var practiceRecordDto = new PracticeRecordDto
            {
                PracticeId = practiceId,
                UserId = _sessionContext.UserId,
                PracticeTime = DateTime.Now
            };

            var practiceRecord = _mapper.Map<ExamPracticeRecord>(practiceRecordDto);

            practiceRecord.ToInsert(_sessionContext);

            await _practiceRecordRepository.ValidateAddAsync(practiceRecord, cancellationToken);

            return practiceRecord;
        }

        private async Task<List<ExamQuestion>> ExactractQuestion(AddPracticeDto actionRequest)
        {
            if (actionRequest.PracticeTagDtos == null) return new List<ExamQuestion>();

            var tagIds = actionRequest.PracticeTagDtos.Select(x => x.TagId).ToList();

            var questionTagRepostiory = new ExamRepository<ExamQuestionTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var questionRepository = new ExamRepository<ExamQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            ISugarQueryable<ExamQuestionTag, ExamQuestion> questionTable = GetCheckQuestions(questionTagRepostiory);

            // 按照标签获取试题，至少取一道
            if (actionRequest.Count < actionRequest.PracticeTagDtos.Count)
            {
                var queryResult = questionTable.Where((qt, q) => tagIds.Contains(qt.TagId)).Take(actionRequest.Count).Select((qt, q) => q);

                return await queryResult.ToListAsync();
            }
            else
            {
                var unionQuestions = new List<ISugarQueryable<ExamQuestion>>();
                var ids = new List<string>();
                // 保证每个标签至少获取一道题
                tagIds.ForEach(v =>
                {
                    var unionQuestion = GetCheckQuestions(questionTagRepostiory).Where((qt, q) => qt.TagId == v).Take(1).Select((qt, q) => q);
                    ids.Add(GetCheckQuestions(questionTagRepostiory).Where((qt, q) => qt.TagId == v).Select(qt => qt.Id).First());
                    unionQuestions.Add(unionQuestion);
                });

                var mainQuesiton = GetCheckQuestions(questionTagRepostiory).Where((qt, q) => tagIds.Contains(qt.TagId) && !ids.Contains(qt.Id)).Take(actionRequest.Count - tagIds.Count).Select((qt, q) => q);

                unionQuestions.Add(mainQuesiton);

                var queryResult = questionRepository.UnionAll(unionQuestions.ToArray());

                return await queryResult.ToListAsync();
            }

        }

        private static ISugarQueryable<ExamQuestionTag, ExamQuestion> GetCheckQuestions(ExamRepository<ExamQuestionTag> questionTagRepostiory)
        {
            return questionTagRepostiory.Queryable().InnerJoin<ExamQuestion>((qt, q) => qt.QuestionId == q.Id).Where((qt, q) => q.QuestionType == Share.Enums.Exams.EQuestionType.Single || q.QuestionType == Share.Enums.Exams.EQuestionType.Multi || q.QuestionType == Share.Enums.Exams.EQuestionType.Judge);
        }

        private async Task<string> GenerateCode(string codePrefix, int length)
        {
            var trainPlan = await _repository.Queryable().Where(x => x.CreationTime.Date == DateTime.Now.Date).OrderByDescending(x => x.CreationTime).FirstAsync();


            var code = string.Empty;
            if (trainPlan != null)
            {
                code = trainPlan.Code;
            }
            code = CodeUtility.GenerateCode(codePrefix, length, code);
            return code;
        }
        #endregion

        #region protect method

        protected override async Task CompleteAdd(ExamPractice entity)
        {
            await base.AddNav(entity).Include(x => x.PracticeRecords)
                  .Include(x => x.PracticeQuestions)
                  .ThenInclude(x => x.PracticeQuestionKnowladges)
                  .Include(x => x.PracticeQuestions, new InsertNavOptions
                  {
                      OneToManyIfExistsNoInsert = true
                  })
                  .ThenInclude(x => x.PracticeQuestionOptionses)
                  .Include(x => x.PracticeQuestions, new InsertNavOptions
                  {
                      OneToManyIfExistsNoInsert = true
                  })
                  .ThenInclude(x => x.PracticeQuestionSourcewares).ExecuteCommandAsync();
        }

        public async Task<TagQuestionCountViewResponse> GetTagQuestionCount(TagQuestionCountForPracticeRequest tagQuestionCountForPracticeRequest)
        {
            var expression = tagQuestionCountForPracticeRequest.GetExpression();
            var questionExpression = tagQuestionCountForPracticeRequest.GetQuestionExpression();

            var tagQuestionRepository = new ExamRepository<ExamQuestionTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var quesitonRepository = new ExamRepository<ExamQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var tagQuestionTable = tagQuestionRepository.Queryable().Where(expression);
            var questionTable = quesitonRepository.Queryable().Where(questionExpression);

            var queryable = tagQuestionTable.InnerJoin(questionTable, (t, q) => t.QuestionId == q.Id)
                .Select((t, q) => new TagQuestionCountViewResponse
                {
                    TotalCount = SqlFunc.AggregateCount(t.Id)
                });

            var taqQuestions = await queryable
                .ToListAsync();

            return taqQuestions.Count() > 0 ? taqQuestions.FirstOrDefault() : new TagQuestionCountViewResponse
            {
                TotalCount = 0
            };
        }

        #endregion
    }
}
