using DocumentFormat.OpenXml.Office2010.Excel;
using Exam.Application;
using Exam.Infrastructure.Data.Entity;
using Exam.Infrastructure.Enums;
using Exam.Infrastructure.Extensions;
using Exam.Share.ViewResponses.Question;
using Hotline.Application.Exam.QueryExtensions.Questions;
using Hotline.Exams.Sourcewares;
using Hotline.Repository.SqlSugar;
using Hotline.Repository.SqlSugar.DataPermissions;
using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
using Hotline.Share.Dtos.Questions;
using Hotline.Share.Requests.Question;
using JiebaNet.Segmenter.Common;
using MapsterMapper;
using SqlSugar;
using XF.Domain.Dependency;
using XF.Domain.Entities;
using Hotline.Application.Exam.Extensions;
using Hotline.Application.Exam.Core.Extensions;
using Hotline.Application.Exam.Constants.Messages;
using Hotline.Repository.SqlSugar.Exam.Core.Constants;
using NPOI.SS.Formula.Functions;
using XF.Domain.Authentications;
using Hotline.Tools;
using Microsoft.AspNetCore.Http;
using System.Dynamic;
using NPOI.Util.ArrayExtensions;
using System.Threading.Tasks;
using Hotline.Share.Enums.Exams;
using Hotline.Share.Tools;
using DocumentFormat.OpenXml.Drawing;
using Hotline.Application.Exam.Core.Utilities;
using Hotline.Application.Exam.Interface.Questions;
using Hotline.Exams.ExamManages;
using Hotline.Exams.Questions;
using Hotline.Repository.SqlSugar.Exam.Repositories;
using Hotline.Share.Dtos.TestPapers;
using Hotline.Repository.SqlSugar.Exam.Service;
using Hotline.Repository.SqlSugar.Exam.Extensions;
using XF.Domain.Exceptions;

namespace Hotline.Application.Exam.Service.Questions
{
    public class QuestionService : ApiService<ExamQuestion, AddQuestionDto, UpdateQuestionDto, HotlineDbContext>, IQuestionService, IScopeDependency
    {
        #region ctor
        private readonly IQuestionRepository _repository;
        private readonly IQuestionTagRepository _questionTagRepository;
        private readonly IQuestionOptionsRepository _questionOptionRepository;
        private readonly IQuestionAnswerRepository _questionAnswerRepository;
        private readonly IQuestionSourcewareRepository _questionSourcewareRepository;
        private readonly IQuestionKnowladgeRepository _questionKnowladgeRepository;
        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
        private readonly IServiceProvider _serviceProvider;
        private readonly IMapper _mapper;
        private readonly ISessionContext _sessionContext;
        private AddQuestionDto _addQuestionDto;

        public QuestionService(IQuestionRepository repository,
            IQuestionTagRepository questionTagRepository,
            IQuestionOptionsRepository questionOptionsRepository,
            IQuestionAnswerRepository questionAnswerRepository,
            IQuestionSourcewareRepository questionSourcewareRepository,
            IQuestionKnowladgeRepository questionKnowladgeRepository,
            IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
            IMapper mapper,
            ISessionContext sessionContext
            ) : base(repository, mapper)
        {
            _repository = repository;
            _questionTagRepository = questionTagRepository;
            _questionAnswerRepository = questionAnswerRepository;
            _questionOptionRepository = questionOptionsRepository;
            _questionSourcewareRepository = questionSourcewareRepository;
            _questionKnowladgeRepository = questionKnowladgeRepository;
            _dataPermissionFilterBuilder = dataPermissionFilterBuilder;
            _serviceProvider = serviceProvider;
            _mapper = mapper;
            this._sessionContext = sessionContext;
        }
        #endregion

        #region public method
        public async Task<QuestionDto> GetAsync(EntityQueryRequest entityQueryRequest)
        {
            var entity = await _repository.GetAsync(entityQueryRequest.Id);

            var questionDto = _mapper.Map<QuestionDto>(entity);

            if (questionDto != null)
            {
                questionDto.QuestionTagDtos = await GetQuestionTags(entityQueryRequest);

                questionDto.QuestionAnswerDto = await GetQuestionAnswer(entityQueryRequest);

                questionDto.QuestionKnowladgeDtos = await GetKnowladges(entityQueryRequest);

                questionDto.QuestionOptionsDtos = await GetQuestionOptions(entityQueryRequest);

                questionDto.QuestionSourcewareDtos = await GetQuestionSourcewares(entityQueryRequest);
            }

            return questionDto;
        }

        public async Task<(int, List<QuestionViewResponse>)> GetListAsync(QuestionPagedRequest queryRequest)
        {
            var expression = queryRequest.GetExpression();
            var questionTable = _repository.Queryable().Where(expression);

            var questionKnowladgeExpression = queryRequest.GetQuestionKnowladgeExpression();
            var questionKnowladgeTable = new ExamRepository<ExamQuestionKnowladge>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(questionKnowladgeExpression);

            var queryable = questionTable.InnerJoin(questionKnowladgeTable, (q, k) => q.Id == k.QuestionId).Select((q, k) => new QuestionViewResponse
            {
                DifficultyLevel = q.DifficultyLevel,
                Title = q.Title,
                QuestionType = q.QuestionType,
                Id = q.Id
            });

            var result = await queryable.ToListAsync();

            var total = await queryable.CountAsync();

            return (total, result);
        }

        public async Task<PageViewResponse<QuestionViewResponse>> GetPagedListAsync(QuestionPagedRequest queryRequest)
        {
            var expression = queryRequest.GetExpression();
            var questionTable = _repository.Queryable().Where(expression);

            var questionTagExpression = queryRequest.GetQuestionTagExpression();
            var questionTagRepository = new BaseRepository<ExamQuestionTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var examTagRepository = new BaseRepository<ExamTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var questionTagTable = questionTagRepository.Queryable().Where(questionTagExpression);

            var allQuestionTagTable = questionTagRepository.Queryable();

            var examTagExpression = queryRequest.GetExamTagExpression();
            var examTagTable = examTagRepository.Queryable().Where(examTagExpression);
            var allExamTagTable = examTagRepository.Queryable();

            var questionKnowladgeExpression = queryRequest.GetQuestionKnowladgeExpression();
            var questionKnowladgeTable = new ExamRepository<ExamQuestionKnowladge>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(questionKnowladgeExpression);


            var queryable = questionTable.InnerJoin(questionTagTable, (s, d) => s.Id == d.QuestionId).InnerJoin(examTagTable, (s, d, t) => d.TagId == t.Id)
            //.InnerJoin(questionKnowladgeTable, (s, d, t,k) => s.Id == k.QuestionId)
            .Distinct()
            .Select((s, d, t) => new QuestionViewResponse
            {
                DifficultyLevel = s.DifficultyLevel,
                FormalEnable = s.FormalEnable,
                SimulateEnable = s.SimulateEnable,
                SortIndex = s.SortIndex,
                Status = s.Status,
                QuestionType = s.QuestionType,
                Title = s.Title,
                Id = s.Id
            });

            if (queryRequest.KnowladgeIds != null && queryRequest.KnowladgeIds.Any())
            {
                queryable = questionTable.InnerJoin(questionTagTable, (s, d) => s.Id == d.QuestionId).InnerJoin(examTagTable, (s, d, t) => d.TagId == t.Id)
                .InnerJoin(questionKnowladgeTable, (s, d, t, k) => s.Id == k.QuestionId)
            .Distinct()
            .Select((s, d, t) => new QuestionViewResponse
            {
                DifficultyLevel = s.DifficultyLevel,
                FormalEnable = s.FormalEnable,
                SimulateEnable = s.SimulateEnable,
                SortIndex = s.SortIndex,
                Status = s.Status,
                QuestionType = s.QuestionType,
                Title = s.Title,
                Id = s.Id
            });
            }

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

            var questionIds = list.Select(x => x.Id);

            var tags = await allQuestionTagTable.Where(x => questionIds.Contains(x.QuestionId))
                .InnerJoin(allExamTagTable, (q, e) => q.TagId == e.Id)
                .Select((q, e) => new TagQuestionDto
                {
                    Id = q.QuestionId,
                    TagId = q.TagId,
                    Tag = e.Name
                }).ToListAsync();

            list.ForEach(item =>
            {
                item.Tag = string.Join(",", tags.Where(x => x.Id == item.Id).Select(x => x.Tag));
            });


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

            return result;
        }

        public override async Task<string> AddAsync(AddQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            base.StartTran();

            base.Entity = await AddQuestion(actionRequest, cancellationToken);

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

            return base.Entity.Id;
        }

        public override async Task UpdateAsync(UpdateQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            base.StartTran();

            await base.UpdateAsync(actionRequest, cancellationToken);

            ResolveQuestionId(actionRequest, actionRequest.Id);

            _addQuestionDto = _mapper.Map<AddQuestionDto>(actionRequest);

            base.Entity.QuestionTags = await ModifyQuestionTags(actionRequest, cancellationToken);

            base.Entity.QuestionOptionses = await ModifyQuestionOptions(actionRequest, cancellationToken);

            base.Entity.QuestionAnswers = await ModifyQuestionAnswer(actionRequest, cancellationToken);

            base.Entity.QuestionKnowladges = await ModifyKnowladges(actionRequest, cancellationToken);

            base.Entity.QuestionSourcewares = await ModifySourcewares(actionRequest, cancellationToken);

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

        public override async Task DeleteAsync(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
        {
            await base.DeleteAsync(entityQueryRequest, cancellationToken);

            var tmpEntityQueryRequest = ExpressionableUtility.CreateExpression<ExamQuestionTag>()
                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.QuestionId == entityQueryRequest.Id)
                .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.QuestionId))
                .ToEntityQueryRequest<ExamQuestionTag>();

            await DeleteQuestionTags(tmpEntityQueryRequest, cancellationToken);

            tmpEntityQueryRequest = ExpressionableUtility.CreateExpression<ExamQuestionOptions>()
                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.QuestionId == entityQueryRequest.Id)
                .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.QuestionId))
                .ToEntityQueryRequest<ExamQuestionOptions>();

            await DeleteQuestionOptions(tmpEntityQueryRequest, cancellationToken);

            tmpEntityQueryRequest = ExpressionableUtility.CreateExpression<ExamQuestionAnswer>()
                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.QuestionId == entityQueryRequest.Id)
                .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.QuestionId))
                .ToEntityQueryRequest<ExamQuestionAnswer>();

            await DeleteQuestionAnswer(tmpEntityQueryRequest, cancellationToken);

            tmpEntityQueryRequest = ExpressionableUtility.CreateExpression<ExamQuestionKnowladge>()
                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.QuestionId == entityQueryRequest.Id)
                .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.QuestionId))
                .ToEntityQueryRequest<ExamQuestionKnowladge>();

            await DeleteKnowladges(tmpEntityQueryRequest, cancellationToken);

            tmpEntityQueryRequest = ExpressionableUtility.CreateExpression<ExamQuestionSourceware>()
                .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.QuestionId == entityQueryRequest.Id)
                .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.QuestionId))
                .ToEntityQueryRequest<ExamQuestionSourceware>();

            await DeleteSourcewares(tmpEntityQueryRequest, cancellationToken);
        }

        public async Task ImportExcel(IFormFile files, CancellationToken cancellationToken)
        {
            var stream = files.OpenReadStream();
            var contents = ExcelHelper.Read(stream, true);

            var questions = new List<ExamQuestion>();
            var examTags = await GetExamTags(contents);

            base.StartTran();

            var isEmptyValue = false;

            contents.ForEach(async item =>
            {
                var value = (item as ExpandoObject).GetValueOrDefault(ExamSystemConstants.ColumnNames[16]);

                if (value == null)
                    return;

                var questionDto = BuildQuestion(item as ExpandoObject, examTags);

                var question = await AddQuestion(questionDto, cancellationToken);
                if (question != null)
                    questions.Add(question);
            });

            if(isEmptyValue) throw new Exception("答案不能为空");

            await _repository.AddNav(questions)
                        .Include(x => x.QuestionTags)
                        .Include(x => x.QuestionOptionses)
                        .Include(x => x.QuestionSourcewares)
                        .Include(x => x.QuestionAnswers)
                        .Include(x => x.QuestionKnowladges).ExecuteCommandAsync();
            stream.Close();            
        }

        private async Task<List<ExamTag>> GetExamTags(List<object> contents)
        {
            var examTags = new List<ExamTag>();

            var tagNames = new List<string>();

            contents.ForEach(item =>
            {
                var tagNameStr = ((ExpandoObject)item).GetValueOrDefault(ExamSystemConstants.ColumnNames[1])?.ToString();

                if (tagNameStr.IsNotNullOrEmpty())
                {
                    var names = tagNameStr.Split(",").ToList();

                    names.ForEach(name =>
                    {
                        if (!tagNames.Contains(name))
                        {
                            tagNames.Add(name);
                        }
                    });

                }
            });

            var repository = new ExamRepository<ExamTag>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            examTags = await repository.Queryable().Where(x => tagNames.Contains(x.Name)).ToListAsync();

            return examTags;
        }

        private AddQuestionDto BuildQuestion(ExpandoObject item, List<ExamTag> examTags)
        {
            if (item != null)
            {
                var options = new List<string>();

                ResolveOptions(options, item, 6, 15);

                var value = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[16]);

                if (value != null) {
                    var answer = value.ToString();

                    var questionDto = new AddQuestionDto
                    {
                        Title = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[2]) != null ? item.GetValueOrDefault(ExamSystemConstants.ColumnNames[5]).ToString() : string.Empty,
                        QuestionTagDtos = BuildQuestionTags(item.GetValueOrDefault(ExamSystemConstants.ColumnNames[1]).ToString(), examTags),
                        DifficultyLevel = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[2]).ToString().ToEnumByDesc<EDifficultyLevel>(),
                        QuestionType = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[0]).ToString().ToEnumByDesc<EQuestionType>(),
                        FormalEnable = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[4]).ToString().ToEnumByDesc<ECheck>() == ECheck.Yes,
                        SimulateEnable = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[5]).ToString().ToEnumByDesc<ECheck>() == ECheck.Yes,
                    };
                    questionDto.QuestionAnswerDto = BuildQuestionAnswer(answer, questionDto.QuestionType);

                    questionDto.QuestionOptionsDtos = BuildQuestionOptions(options, questionDto.QuestionType, answer);

                    return questionDto;
                }

               
            }

            return null;
        }

        private List<AddQuestionOptionsDto> BuildQuestionOptions(List<string> options, EQuestionType questionType, string answer)
        {
            if (questionType.CheckSelectType())
            {
                var addQuestionOptionDtos = new List<AddQuestionOptionsDto>();

                var index = 0;
                options.ForEach(item =>
                {
                    var addQuestionOption = new AddQuestionOptionsDto
                    {
                        Content = item,
                        Label = ExamSystemConstants.Labels[index],
                        IsAnswer = answer.Contains(ExamSystemConstants.Labels[index])
                    };

                    addQuestionOptionDtos.Add(addQuestionOption);

                    index++;
                });

                return addQuestionOptionDtos;
            }
            return null;
        }

        private void ResolveOptions(List<string> options, ExpandoObject item, int start, int end)
        {
            for (int i = start; i <= end; i++)
            {
                var option = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[i])?.ToString();
                if (option.IsNotNullOrEmpty())
                {
                    options.Add(option.ToString());
                }
            }
        }

        private AddQuestionAnswerDto BuildQuestionAnswer(string? content, EQuestionType questionType)
        {
            if (!questionType.CheckSelectType())
            {
                var addQuestionAnswerDto = new AddQuestionAnswerDto
                {
                    Answer = content
                };

                return addQuestionAnswerDto;
            }
            return null;
        }

        private List<AddQuestionTagDto> BuildQuestionTags(string content, List<ExamTag> examTags)
        {
            if (content.IsNotNullOrEmpty())
            {
                var addQuestionTagDtos = new List<AddQuestionTagDto>();

                var tagNames = content.Split(",");

                tagNames.ToList().ForEach(item =>
                {
                    var examTag = examTags.FirstOrDefault(x => x.Name == item);

                    if (examTag != null)
                    {
                        addQuestionTagDtos.Add(new AddQuestionTagDto
                        {
                            TagId = examTag.Id
                        });
                    }
                });

                return addQuestionTagDtos;
            }

            return null;
        }
        #endregion

        #region private method

        private void ResolveQuestionId(AddQuestionDto actionRequest, string id)
        {
            actionRequest.QuestionKnowladgeDtos?.ForEach(x => x.QuestionId = id);

            actionRequest.QuestionOptionsDtos?.ForEach(x => x.QuestionId = id);

            actionRequest.QuestionSourcewareDtos?.ForEach(x => x.QuestionId = id);

            actionRequest.QuestionTagDtos?.ForEach(x => x.QuestionId = id);

            if (actionRequest.QuestionAnswerDto != null)
            {
                actionRequest.QuestionAnswerDto.QuestionId = id;
            }
        }

        private void ResolveQuestionId(UpdateQuestionDto actionRequest, string id)
        {
            actionRequest.QuestionKnowladgeDtos?.ForEach(x => x.QuestionId = id);

            actionRequest.QuestionOptionsDtos?.ForEach(x => x.QuestionId = id);

            actionRequest.QuestionSourcewareDtos?.ForEach(x => x.QuestionId = id);

            actionRequest.QuestionTagDtos?.ForEach(x => x.QuestionId = id);

            if (actionRequest.QuestionAnswerDto != null)
            {
                actionRequest.QuestionAnswerDto.QuestionId = id;
            }
        }

        private async Task<List<ExamQuestionTag>> AddQuestionTags(AddQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.QuestionTagDtos == null) return null;

            actionRequest.QuestionTagDtos.ResolveOperationStatus();

            var questionTagDtos = actionRequest.QuestionTagDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();

            var questionTags = _mapper.Map<List<ExamQuestionTag>>(questionTagDtos);

            questionTags.ToInsert(_sessionContext);

            await _questionTagRepository.ValidateAddAsync(questionTags, cancellationToken);

            return questionTags;
        }

        private async Task<List<ExamQuestionOptions>> AddQuestionOptions(AddQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.QuestionOptionsDtos == null) return null;

            actionRequest.QuestionOptionsDtos.ResolveOperationStatus();
            // 简答和填空没有选项
            if (actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Essay || actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Blank)
                return null;

            var questionOptionseDtos = actionRequest.QuestionOptionsDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();


            var questionOptionses = _mapper.Map<List<ExamQuestionOptions>>(questionOptionseDtos);

            var sortIndex = 0;
            questionOptionses.ForEach(m =>
            {
                m.SortIndex = sortIndex;
                m.Label = ExamSystemConstants.Labels[sortIndex];
                sortIndex++;
            });

            questionOptionses.ToInsert(_sessionContext);

            await _questionOptionRepository.ValidateAddAsync(questionOptionses, cancellationToken);

            return questionOptionses;
        }
        private async Task<List<ExamQuestionAnswer>> AddQuestionAnswer(AddQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.QuestionAnswerDto == null) return null;

            if (actionRequest.QuestionAnswerDto.Answer != null)
            {
                actionRequest.QuestionAnswerDto.OperationStatus = EEOperationStatus.Add;
            }

            // 简答和填空没有选项
            if (actionRequest.QuestionType.CheckSelectType()) return null;
            var questionAnswer = _mapper.Map<ExamQuestionAnswer>(actionRequest.QuestionAnswerDto);

            var questionAnswers = new List<ExamQuestionAnswer>();

            questionAnswer.ToInsert(_sessionContext);

            questionAnswers.Add(questionAnswer);

            await _questionAnswerRepository.ValidateAddAsync(questionAnswer, cancellationToken);

            return questionAnswers;
        }
        private async Task<List<ExamQuestionSourceware>> AddSourcewares(AddQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.QuestionSourcewareDtos == null) return null;

            actionRequest.QuestionSourcewareDtos.ResolveOperationStatus();

            var questionSourcewareDtos = actionRequest.QuestionSourcewareDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();

            var questionSourcewares = _mapper.Map<List<ExamQuestionSourceware>>(questionSourcewareDtos);

            questionSourcewares.ToInsert(_sessionContext);

            await _questionSourcewareRepository.ValidateAddAsync(questionSourcewares, cancellationToken);

            return questionSourcewares;
        }
        private async Task<List<ExamQuestionKnowladge>> AddKnowladges(AddQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.QuestionKnowladgeDtos == null) return null;

            actionRequest.QuestionKnowladgeDtos.ResolveOperationStatus();

            var questionKnoladgeDtos = actionRequest.QuestionKnowladgeDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();

            var questionKnowladges = _mapper.Map<List<ExamQuestionKnowladge>>(questionKnoladgeDtos);

            questionKnowladges.ToInsert(_sessionContext);

            await _questionKnowladgeRepository.ValidateAddAsync(questionKnowladges, cancellationToken);

            return questionKnowladges;
        }
        private async Task<List<ExamQuestionSourceware>> UpdateSourcewares(UpdateQuestionDto actionRequest, List<ExamQuestionSourceware> all, CancellationToken cancellationToken)
        {
            if (actionRequest.QuestionSourcewareDtos == null) return null;

            actionRequest.QuestionSourcewareDtos.ResolveOperationStatus();

            var questionSourcewareDtos = actionRequest.QuestionSourcewareDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();

            var ids = questionSourcewareDtos.Select(x => x.Id);

            var questionSourcewares = all.Where(x => ids.Contains(x.Id)).ToList();

            var entitys = new List<ExamQuestionSourceware>();
            foreach (var questionSourcewareDto in questionSourcewareDtos)
            {
                var entity = questionSourcewares.FirstOrDefault(x => x.Id == questionSourcewareDto.Id);
                if (entity != null)
                {
                    entity.QuestionId = actionRequest.Id;
                    entitys.Add(_mapper.Map<UpdateQuestionSourcewareDto, ExamQuestionSourceware>(questionSourcewareDto, entity));
                }

            }

            //questionSourcewares = _mapper.Map<List<UpdateQuestionSourcewareDto>,List<QuestionSourceware>>(questionSourcewareDtos,questionSourcewares);

            //questionSourcewares.ForEach(x => x.QuestionId = actionRequest.Id);

            questionSourcewares.ToUpdate(_sessionContext);

            await _questionSourcewareRepository.ValidateUpdateAsync(questionSourcewares, cancellationToken);

            return questionSourcewares;
        }

        private async Task<List<ExamQuestionKnowladge>> UpdateKnowladges(UpdateQuestionDto actionRequest, List<ExamQuestionKnowladge> all, CancellationToken cancellationToken)
        {
            if (actionRequest.QuestionKnowladgeDtos == null) return null;

            actionRequest.QuestionKnowladgeDtos.ResolveOperationStatus();

            var questionKnowladgeDtos = actionRequest.QuestionKnowladgeDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();

            var ids = questionKnowladgeDtos.Select(x => x.Id);

            var questionKnowladges = all.Where(x => ids.Contains(x.Id)).ToList();

            var entitys = new List<ExamQuestionKnowladge>();
            foreach (var questionKnowladgeDto in questionKnowladgeDtos)
            {
                var entity = questionKnowladges.FirstOrDefault(x => x.Id == questionKnowladgeDto.Id);
                if (entity != null)
                {
                    entity.QuestionId = actionRequest.Id;
                    entitys.Add(_mapper.Map<UpdateQuestionKnowladgeDto, ExamQuestionKnowladge>(questionKnowladgeDto, entity));
                }

            }


            //questionKnowladges = _mapper.Map<List<UpdateQuestionKnowladgeDto>, List<QuestionKnowladge>>(questionKnowladgeDtos,questionKnowladges);

            //questionKnowladges.ForEach(x => x.QuestionId = actionRequest.Id);

            questionKnowladges.ToUpdate(_sessionContext);

            await _questionKnowladgeRepository.ValidateUpdateAsync(questionKnowladges, cancellationToken);

            return questionKnowladges;
        }
        private async Task<ExamQuestionAnswer> UpdateQuestionAnswer(UpdateQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.QuestionAnswerDto == null) return null;

            // 简单和填空没有选项
            if (actionRequest.QuestionType.CheckSelectType()) return null;

            var questionAnswer = await _questionAnswerRepository.GetAsync(actionRequest.QuestionAnswerDto.Id);

            questionAnswer = _mapper.Map<UpdateQuestionAnswerDto, ExamQuestionAnswer>(actionRequest.QuestionAnswerDto, questionAnswer);

            questionAnswer.QuestionId = actionRequest.Id;

            questionAnswer.ToUpdate(_sessionContext);

            await _questionAnswerRepository.ValidateUpdateAsync(questionAnswer, cancellationToken);

            return questionAnswer;
        }
        private async Task<List<ExamQuestionOptions>> UpdateQuestionOptions(UpdateQuestionDto actionRequest, List<ExamQuestionOptions> all, CancellationToken cancellationToken)
        {
            if (actionRequest.QuestionOptionsDtos == null) return null;

            // 简单和填空没有选项
            if (actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Essay || actionRequest.QuestionType == Share.Enums.Exams.EQuestionType.Blank)
                return null;

            var questionOptionsDtos = actionRequest.QuestionOptionsDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();

            var ids = questionOptionsDtos.Select(x => x.Id);

            var questionOptionses = all.Where(x => ids.Contains(x.Id)).ToList();

            var entitys = new List<ExamQuestionOptions>();
            foreach (var questionOptionsDto in questionOptionsDtos)
            {
                var entity = questionOptionses.FirstOrDefault(x => x.Id == questionOptionsDto.Id);
                if (entity != null)
                {
                    entity.QuestionId = actionRequest.Id;
                    entitys.Add(_mapper.Map<UpdateQuestionOptionsDto, ExamQuestionOptions>(questionOptionsDto, entity));
                }
            }

            //questionOptionses =  _mapper.Map<List<QuestionOptionsDto>, List<QuestionOptions>>(questionOptionsDtos,questionOptionses);

            //entitys.ForEach(x => x.QuestionId = actionRequest.Id);

            entitys.ToUpdate(_sessionContext);

            await _questionOptionRepository.ValidateUpdateAsync(entitys, cancellationToken);

            return entitys;
        }

        private async Task<List<ExamQuestionTag>> UpdateQuestionTags(UpdateQuestionDto actionRequest, List<ExamQuestionTag> all, CancellationToken cancellationToken)
        {
            if (actionRequest.QuestionTagDtos == null) return null;

            var questionTagDtos = actionRequest.QuestionTagDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();

            var ids = questionTagDtos.Select(x => x.Id);

            var questionTags = all.Where(x => ids.Contains(x.Id)).ToList();

            var entitys = new List<ExamQuestionTag>();
            foreach (var questionOptionsDto in questionTagDtos)
            {
                var entity = questionTags.FirstOrDefault(x => x.Id == questionOptionsDto.Id);
                if (entity != null)
                {
                    entity.QuestionId = actionRequest.Id;
                    entitys.Add(_mapper.Map<UpdateQuestionTagDto, ExamQuestionTag>(questionOptionsDto, entity));
                }

            }
            //questionTags = _mapper.Map<List<UpdateQuestionTagDto>, List<QuestionTag>>(questionTagDtos,questionTags);

            questionTags.ToUpdate(_sessionContext);

            await _questionTagRepository.ValidateUpdateAsync(questionTags, cancellationToken);

            return questionTags;
        }
        private async Task DeleteSourcewares(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
        {
            await _questionSourcewareRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
        }
        private async Task DeleteKnowladges(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
        {
            await _questionKnowladgeRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
        }
        private async Task DeleteQuestionAnswer(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
        {
            await _questionAnswerRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
        }
        private async Task DeleteQuestionOptions(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
        {
            await _questionOptionRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
        }
        private async Task DeleteQuestionTags(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
        {
            await _questionTagRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
        }
        private async Task<List<ExamQuestionSourceware>> ModifySourcewares(UpdateQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            var all = await _questionSourcewareRepository.Queryable().Where(x => x.QuestionId == actionRequest.Id).ToListAsync();

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

            var questionSourcewares = new List<ExamQuestionSourceware>();

            actionRequest.QuestionSourcewareDtos.ResolveOperationStatus(all);

            _addQuestionDto.QuestionSourcewareDtos = _mapper.Map<List<UpdateQuestionSourcewareDto>, List<AddQuestionSourcewareDto>>(actionRequest.QuestionSourcewareDtos);

            _addQuestionDto.QuestionSourcewareDtos.ResolveOperationStatus();

            questionSourcewares.AddRangeExt(await AddSourcewares(_addQuestionDto, cancellationToken));

            questionSourcewares.AddRangeExt(await UpdateSourcewares(actionRequest, all, cancellationToken));

            var questionSourcewareDtos = actionRequest.QuestionSourcewareDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete);
            var ids = questionSourcewareDtos.Select(m => m.Id);
            EntityQueryRequest entityQueryRequest = ResovleDelete<ExamQuestionSourceware>(ids);

            await DeleteSourcewares(entityQueryRequest, cancellationToken);

            return questionSourcewares;
        }

        private async Task<List<ExamQuestionKnowladge>> ModifyKnowladges(UpdateQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            var all = await _questionKnowladgeRepository.Queryable().Where(x => x.QuestionId == actionRequest.Id).ToListAsync();

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

            var questionKnowladges = new List<ExamQuestionKnowladge>();

            actionRequest.QuestionKnowladgeDtos.ResolveOperationStatus(all);

            _addQuestionDto.QuestionKnowladgeDtos = _mapper.Map<List<UpdateQuestionKnowladgeDto>, List<AddQuestionKnowladgeDto>>(actionRequest.QuestionKnowladgeDtos);

            _addQuestionDto.QuestionKnowladgeDtos.ResolveOperationStatus();

            questionKnowladges.AddRangeExt(await AddKnowladges(_addQuestionDto, cancellationToken));

            questionKnowladges.AddRangeExt(await UpdateKnowladges(actionRequest, all, cancellationToken));

            var questionKnowladgeDtos = actionRequest.QuestionKnowladgeDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete);
            var ids = questionKnowladgeDtos.Select(m => m.Id);
            EntityQueryRequest entityQueryRequest = ResovleDelete<ExamQuestionKnowladge>(ids);

            await DeleteKnowladges(entityQueryRequest, cancellationToken);

            return questionKnowladges;
        }

        private async Task<List<ExamQuestionAnswer>> ModifyQuestionAnswer(UpdateQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            var all = await _questionAnswerRepository.Queryable().Where(x => x.QuestionId == actionRequest.Id).ToListAsync();

            var questionAnswers = new List<ExamQuestionAnswer>();

            if (actionRequest.QuestionAnswerDto == null)
            {
                if (all == null)
                    return null;
                else
                {
                    var ids = all.Select(m => m.Id).ToList();
                    var entityQueryRequest = new EntityQueryRequest
                    {
                        Expression = ExpressionableUtility.CreateExpression<ExamQuestionAnswer>()
                        .AndIF(ids.IsNotEmpty(), x => ids.Contains(x.Id)).ToExpression()
                    };

                    await DeleteQuestionAnswer(entityQueryRequest, cancellationToken);

                    return null;
                }

            }

            if (actionRequest.QuestionAnswerDto.Id != null)
            {
                actionRequest.QuestionAnswerDto.OperationStatus = EEOperationStatus.Update;

                var update = await UpdateQuestionAnswer(actionRequest, cancellationToken);

                if (update != null)
                {
                    questionAnswers.Add(update);
                }
            }
            else
            {
                var add = await AddQuestionAnswer(_addQuestionDto, cancellationToken);

                if (add != null)
                {
                    questionAnswers.AddRange(add);
                }
            }

            if (actionRequest.QuestionAnswerDto != null && actionRequest.QuestionAnswerDto.OperationStatus == EEOperationStatus.Delete)
            {
                var entityQueryRequest = new EntityQueryRequest
                {
                    Id = actionRequest.QuestionAnswerDto?.Id
                };

                await DeleteQuestionAnswer(entityQueryRequest, cancellationToken);
            }

            return questionAnswers;

        }

        private async Task<List<ExamQuestionOptions>> ModifyQuestionOptions(UpdateQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.QuestionOptionsDtos == null) return null;

            var questionOptions = new List<ExamQuestionOptions>();

            var all = await _questionOptionRepository.Queryable().Where(x => x.QuestionId == actionRequest.Id).ToListAsync();

            actionRequest.QuestionOptionsDtos.ResolveOperationStatus(all);

            _addQuestionDto.QuestionOptionsDtos = _mapper.Map<List<UpdateQuestionOptionsDto>, List<AddQuestionOptionsDto>>(actionRequest.QuestionOptionsDtos);

            _addQuestionDto.QuestionOptionsDtos.ResolveOperationStatus();

            questionOptions.AddRangeExt(await AddQuestionOptions(_addQuestionDto, cancellationToken));

            questionOptions.AddRangeExt(await UpdateQuestionOptions(actionRequest, all, cancellationToken));

            var questionOptionsDtos = actionRequest.QuestionOptionsDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete);
            var ids = questionOptionsDtos.Select(m => m.Id);
            EntityQueryRequest entityQueryRequest = ResovleDelete<ExamQuestionOptions>(ids);

            await DeleteQuestionOptions(entityQueryRequest, cancellationToken);

            return questionOptions;
        }

        private async Task<List<ExamQuestionTag>> ModifyQuestionTags(UpdateQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.QuestionTagDtos == null) return null;

            var questionTags = new List<ExamQuestionTag>();

            var all = await _questionTagRepository.Queryable().Where(x => x.QuestionId == actionRequest.Id).ToListAsync();

            actionRequest.QuestionTagDtos.ResolveOperationStatus(all);

            _addQuestionDto.QuestionTagDtos = _mapper.Map<List<UpdateQuestionTagDto>, List<AddQuestionTagDto>>(actionRequest.QuestionTagDtos);

            questionTags.AddRangeExt(await AddQuestionTags(_addQuestionDto, cancellationToken));

            questionTags.AddRangeExt(await UpdateQuestionTags(actionRequest, all, cancellationToken));

            var questionTagDtos = actionRequest.QuestionTagDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete);
            var ids = questionTagDtos.Select(m => m.Id);
            EntityQueryRequest entityQueryRequest = ResovleDelete<ExamQuestionTag>(ids);

            await DeleteQuestionTags(entityQueryRequest, cancellationToken);

            return questionTags;
        }

        private EntityQueryRequest ResovleDelete<T>(IEnumerable<string> ids) where T : class, IEntity<string>, new()
        {
            Expressionable<T> expressionable = ExpressionableUtility.CreateExpression<T>();
            expressionable.AndIF(ids.Any(), x => ids.Contains(x.Id));

            var entityQueryRequest = new EntityQueryRequest
            {
                Expression = ids.Any() ? expressionable.ToExpression() : null
            };
            return entityQueryRequest;
        }

        private async Task<List<QuestionSourcewareDto>> GetQuestionSourcewares(EntityQueryRequest entityQueryRequest)
        {
            var questionSourcewareTable = _questionSourcewareRepository.Queryable().Where(x => x.QuestionId == entityQueryRequest.Id);

            var sourcewareTable = new ExamRepository<ExamSourceware>(_questionSourcewareRepository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable();

            var questionSourcewares = questionSourcewareTable.InnerJoin(sourcewareTable, (q, s) =>
              q.SourcewareId == s.Id).Select((q, s) => new QuestionSourcewareDto
              {
                  Id = q.Id,
                  SourcewareId = q.SourcewareId,
                  Name = s.Name
              });

            return await questionSourcewares.ToListAsync();
        }

        private async Task<List<QuestionOptionsDto>> GetQuestionOptions(EntityQueryRequest entityQueryRequest)
        {
            var questionOptionTable = await _questionOptionRepository.Queryable().Where(x => x.QuestionId == entityQueryRequest.Id).ToListAsync();

            var questionOptions = _mapper.Map<List<QuestionOptionsDto>>(questionOptionTable);

            return questionOptions;
        }

        private async Task<List<QuestionKnowladgeDto>> GetKnowladges(EntityQueryRequest entityQueryRequest)
        {
            var questionKnowladgeTable = await _questionKnowladgeRepository.Queryable().Where(x => x.QuestionId == entityQueryRequest.Id).ToListAsync();

            var questionKnowladges = _mapper.Map<List<QuestionKnowladgeDto>>(questionKnowladgeTable);

            return questionKnowladges;
        }

        private async Task<QuestionAnswerDto> GetQuestionAnswer(EntityQueryRequest entityQueryRequest)
        {
            var questionAnswer = await _questionAnswerRepository.GetAsync(x => x.QuestionId == entityQueryRequest.Id);

            var questionAnswerDto = _mapper.Map<QuestionAnswerDto>(questionAnswer);

            return questionAnswerDto ?? new QuestionAnswerDto();
        }

        private async Task<List<QuestionTagDto>> GetQuestionTags(EntityQueryRequest entityQueryRequest)
        {
            var questionTags = _questionTagRepository.Queryable().Where(x => x.QuestionId == entityQueryRequest.Id);

            var examTags = new ExamRepository<ExamTag>(_questionTagRepository.UOW, _dataPermissionFilterBuilder, _serviceProvider).Queryable();

            var questionTagDtos = questionTags.InnerJoin(examTags, (q, t) => q.TagId == t.Id).Select((q, t) => new QuestionTagDto
            {
                Id = q.Id,
                TagId = q.TagId,
                Tag = t.Name,
                QuestionId = q.QuestionId
            });

            return await questionTagDtos.ToListAsync();
        }

        private async Task<ExamQuestion> AddQuestion(AddQuestionDto actionRequest, CancellationToken cancellationToken)
        {
            var id = await base.AddAsync(actionRequest, cancellationToken);

            ResolveQuestionId(actionRequest, id);

            base.Entity.QuestionTags = await AddQuestionTags(actionRequest, cancellationToken);

            base.Entity.QuestionOptionses = await AddQuestionOptions(actionRequest, cancellationToken);

            base.Entity.QuestionAnswers = await AddQuestionAnswer(actionRequest, cancellationToken);

            base.Entity.QuestionKnowladges = await AddKnowladges(actionRequest, cancellationToken);

            base.Entity.QuestionSourcewares = await AddSourcewares(actionRequest, cancellationToken);

            return base.Entity;
        }
        #endregion

        #region protected method

        //protected override async Task CompleteAdd(Question entity)
        //{
        //    if (entity.QuestionType.CheckSelectType())
        //    {
        //        await base.AddNav(entity)
        //        .Include(x => x.QuestionTags)
        //        .Include(x => x.QuestionOptionses)
        //        .Include(x => x.QuestionSourcewares)
        //        .Include(x => x.QuestionKnowladges).ExecuteCommandAsync();
        //    }
        //    else
        //    {
        //        await base.AddNav(entity)
        //       .Include(x => x.QuestionTags)
        //       .Include(x => x.QuestionAnswerE)
        //       .Include(x => x.QuestionSourcewares)
        //       .Include(x => x.QuestionKnowladges).ExecuteCommandAsync();
        //    }
        //}

        //protected override async Task CompleteUpdate(Question entity)
        //{
        //    if (entity.QuestionType.CheckSelectType())
        //    {
        //        await base.UpdateNav(entity)
        //        .Include(x => x.QuestionTags,new UpdateNavOptions
        //        {
        //            OneToManyInsertOrUpdate = true
        //        })
        //        .Include(x => x.QuestionOptionses, new UpdateNavOptions
        //        {
        //            OneToManyInsertOrUpdate = true
        //        })
        //        .Include(x => x.QuestionSourcewares, new UpdateNavOptions
        //        {
        //            OneToManyInsertOrUpdate = true
        //        })
        //        .Include(x => x.QuestionKnowladges, new UpdateNavOptions
        //        {
        //            OneToManyInsertOrUpdate = true
        //        }).ExecuteCommandAsync();
        //    }
        //    else
        //    {
        //        await base.UpdateNav(entity)
        //       .Include(x => x.QuestionTags, new UpdateNavOptions
        //       {
        //           OneToManyInsertOrUpdate = true
        //       })
        //       .Include(x => x.QuestionAnswerE)
        //       .Include(x => x.QuestionSourcewares, new UpdateNavOptions
        //       {
        //           OneToManyInsertOrUpdate = true
        //       })
        //       .Include(x => x.QuestionKnowladges, new UpdateNavOptions
        //       {
        //           OneToManyInsertOrUpdate = true
        //       }).ExecuteCommandAsync();
        //    }
        //}

        #endregion
    }
}
