using Exam.Application.Interface.Exam;
using Exam.Infrastructure.Data.Entity;
using Exam.Infrastructure.Enums;
using Exam.Infrastructure.Extensions;
using Exam.Share;
using Exam.Share.Dtos.ExamManage;
using Exam.Share.ViewResponses.Exam;
using Hotline.Application.Exam.Core.Extensions;
using Hotline.Application.Exam.Extensions;
using Hotline.Application.Exam.Interface.Strategy;
using Hotline.Application.Exam.QueryExtensions.ExamManages;
using Hotline.Application.Exam.Strategy;
using Hotline.Exams.ExamManages;
using Hotline.Repository.SqlSugar;
using Hotline.Repository.SqlSugar.DataPermissions;
using Hotline.Repository.SqlSugar.Exam.Interfaces.ExamManages;
using Hotline.Share.Dtos.ExamManages;
using Hotline.Share.Exams.Extensions;
using Hotline.Share.Requests.Exam;
using Hotline.Share.ViewResponses;
using Hotline.Share.ViewResponses.Exam;
using Hotline.Users;
using JiebaNet.Segmenter.Common;
using MapsterMapper;
using Hotline.Application.Exam.Core.Utilities;
using Hotline.Application.Exam.Interface.ExamManages;
using Hotline.Exams.Validate;
using Hotline.Repository.SqlSugar.Exam.Repositories;
using XF.Domain.Authentications;
using XF.Domain.Dependency;
using XF.Domain.Exceptions;
using XF.Domain.Repository;
using ExamQuestion = Hotline.Exams.Questions.ExamQuestion;
using Hotline.Repository.SqlSugar.Exam.Service;
using Hotline.Repository.SqlSugar.Exam.Extensions;
using Exam.Infrastructure.Data.Interface;
using Hotline.Repository.SqlSugar.Exam.Interface;
using DocumentFormat.OpenXml.Drawing.Charts;
using SqlSugar;
using System.Threading;
using DocumentFormat.OpenXml.Office2013.Excel;
using Hotline.Share.Enums.Exams;
using DocumentFormat.OpenXml.Wordprocessing;
using Hotline.Repository.SqlSugar.Exam.Repositories.ExamManages;
using Hotline.Exams.Questions;
using Hotline.Exams.Sourcewares;
using Hotline.Share.Dtos.Questions;
using DocumentFormat.OpenXml.Office2010.Excel;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;

namespace Hotline.Application.Exam.Service.ExamManages
{
    public class UserExamService : ApiService<ExamUserExam, AddUserExamDto, UpdateUserExamDto, HotlineDbContext>, IUserExamService, IScopeDependency
    {
        private readonly IUserExamRepository _repository;
        private readonly IUserExamItemRepository _userExamItemRepository;
        private readonly IUserExamItemOptionRepository _userExamItemOptionRepository;
        private readonly IExamAnswerRepository _examAnswerRepository;
        private readonly IExamManageRepository _examManageRepository;
        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
        private readonly IServiceProvider _serviceProvider;
        private readonly IMapper _mapper;
        private readonly ISessionContext _sessionContext
            ;

        public UserExamService(IUserExamRepository repository,
            IUserExamItemRepository userExamItemRepository,
            IUserExamItemOptionRepository userExamItemOptionRepository,
            IExamAnswerRepository examAnswerRepository,
            IExamManageRepository examManageRepository,
            IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
            IMapper mapper, ISessionContext sessionContext) : base(repository, mapper, sessionContext)
        {
            this._repository = repository;
            this._userExamItemRepository = userExamItemRepository;
            this._userExamItemOptionRepository = userExamItemOptionRepository;
            this._examAnswerRepository = examAnswerRepository;
            this._examManageRepository = examManageRepository;
            this._dataPermissionFilterBuilder = dataPermissionFilterBuilder;
            this._serviceProvider = serviceProvider;
            this._mapper = mapper;
            this._sessionContext = sessionContext;
        }

        #region public method
        public Task<UserExamDto> GetAsync(EntityQueryRequest entityQueryRequest)
        {
            throw new NotImplementedException();
        }
        public Task<(int, List<UserExamResultViewResponse>)> GetListAsync(UserExamPagedRequest queryRequest)
        {
            throw new NotImplementedException();
        }

        public async Task<ExamQuestionDto> GetExamQuestionDto(ExamQuestionRequest examQuestionRequest)
        {
            var expression = examQuestionRequest.GetExpression();
            var userUserExpression = examQuestionRequest.GetUserExamExpression();
            //var question = await new ExamRepository<Exams.ExamManages.ExamQuestionBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(expression).FirstAsync();
            var querable = new ExamRepository<Exams.ExamManages.ExamQuestionBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(expression);
            var userExamTable = new ExamRepository<Exams.ExamManages.ExamUserExam>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(userUserExpression);
            querable = querable.InnerJoin(userExamTable, (q, u) => q.ExamId == u.ExamId).Select((q, u) => q);
            var question = await querable.FirstAsync();
            if (question != null)
            {
                var examQuestionDto = _mapper.Map<ExamQuestionDto>(question);

                var questionScore = await new ExamRepository<ExamQuestionScoreBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).GetAsync(x => x.QuestionType == question.QuestionType && x.ExamManageId == question.ExamId);

                if (questionScore != null)
                {
                    examQuestionDto.Score = questionScore.Score;
                }

                if (examQuestionDto.QuestionType.CheckSelectType())
                {
                    var questionOptions = await new ExamRepository<ExamQuestionOptionsBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.ExamQuestionId == question.Id).ToListAsync();

                    List<ExamUserExamItemOptions> userItemItemOptions = await GetUserExteamItemOptios(question);

                    if (questionOptions != null)
                    {
                        examQuestionDto.QuestionOptions = new List<ExamQuestionOptionsDto>();

                        questionOptions.ForEach(item =>
                        {
                            var examQuestionOptionsDto = _mapper.Map<ExamQuestionOptionsDto>(item);
                            if (userItemItemOptions != null)
                            {
                                examQuestionOptionsDto.IsSelected = userItemItemOptions.Any(m => m.QuestionOptionId == item.Id);
                            }

                            examQuestionDto.QuestionOptions.Add(examQuestionOptionsDto);
                        });
                    }

                }
                else
                {
                    List<ExamAnswer> examAnswers = await GetExamAnswers(examQuestionRequest);

                    examQuestionDto.Answer = examAnswers != null ? examAnswers.FirstOrDefault()?.Answer : null;

                }


                return examQuestionDto;
            }
            else
            {
                throw new UserFriendlyException(ExamErrorMessage.ServiceError, string.Format(ExamErrorMessage.IsNotExists, string.Concat(typeof(ExamQuestion).GetDescription(), ":", examQuestionRequest.QuestionId)));
            }

        }

        private async Task<List<ExamAnswer>> GetExamAnswers(ExamQuestionRequest examQuestionRequest)
        {
            var examAnswerRepository = new ExamRepository<ExamAnswer>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var userExamItemRepository = new ExamRepository<ExamUserExamItem>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var userExamItemTable = userExamItemRepository.Queryable().Where(i => i.QuestionId == examQuestionRequest.QuestionId);
            var userExamTable = _repository.Queryable().Where(u => u.UserId == _sessionContext.UserId && u.Id == examQuestionRequest.UserExamId);
            var examAnswerTable = examAnswerRepository.Queryable();

            var examAnswers = await examAnswerTable.InnerJoin(userExamItemTable, (e, i) => e.UserExamItemId == i.Id)
                .InnerJoin(userExamTable, (e, i, u) => i.UserExamId == u.Id)
                .Select((e, i, u) => e).ToListAsync();
            return examAnswers;
        }

        private async Task<List<ExamUserExamItemOptions>> GetUserExteamItemOptios(Exams.ExamManages.ExamQuestionBak quesetion)
        {
            var userExamItemOptionsRepository = new ExamRepository<ExamUserExamItemOptions>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var userExamItemOptionsTable = userExamItemOptionsRepository.Queryable();
            var userExamItemTable = _userExamItemRepository.Queryable();
            var userExamTable = _repository.Queryable();

            var userItemItemOptions = await userExamItemOptionsTable
                .InnerJoin(userExamItemTable, (o, u) => o.UserExamItemId == u.Id)
                .InnerJoin(userExamTable, (o, u, e) => u.UserExamId == e.Id)
                .Where((o, u, e) => u.QuestionId == quesetion.QuestionId && e.UserId == _sessionContext.UserId && e.ExamStatus == EExamStatus.Executing).
                Select((o, u, e) => o).ToListAsync();
            return userItemItemOptions;
        }

        public async Task<List<ExamQuestionViewResponse>> GetExamQuestionViewResponses(ExamQuestionGroupRequest examQuestionGroupRequest)
        {
            if (examQuestionGroupRequest.ExamId.IsNull() && examQuestionGroupRequest.UserId.IsNull())
                throw UserFriendlyException.SameMessage("查询参数不能为空");

            var expression = examQuestionGroupRequest.GetExpression();
            var examManageTable = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(expression);
            var testPaperItemTable = new ExamRepository<Exams.ExamManages.ExamQuestionBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();

            var queryable = await examManageTable.InnerJoin(testPaperItemTable, (e, i) => e.Id == i.ExamId)
                .Select((e, i) => i).ToListAsync();

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

            return result;

        }


        public async Task<List<GradingQuestionViewResponce>> GetGradingQuestionViewResponces(ExamQuestionGroupRequest examQuestionGroupRequest)
        {
            var expression = examQuestionGroupRequest.GetExpression();
            var examManageTable = _examManageRepository.Queryable().Where(expression);
            var testPaperItemTable = new ExamRepository<Exams.ExamManages.ExamQuestionBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();

            var queryable = await examManageTable.InnerJoin(testPaperItemTable, (e, i) => e.Id == i.ExamId)
                .Select((e, i) => i).ToListAsync();

            var examQuestionScores = await new ExamRepository<ExamQuestionScoreBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.ExamManageId == examQuestionGroupRequest.ExamId).ToListAsync();

            var userExamItemTable = _userExamItemRepository.Queryable();
            var userExamTable = _repository.Queryable().Where(x => x.ExamId == examQuestionGroupRequest.ExamId && x.UserId == examQuestionGroupRequest.UserId);

            var userExamItems = await userExamItemTable.InnerJoin(userExamTable, (i, u) => i.UserExamId == u.Id)
                .Select((i, u) => i).ToListAsync();

            var result = queryable.GroupBy(x => x.QuestionType).Select(m => new GradingQuestionViewResponce
            {
                QuestionType = m.Key,
                Questions = m.Select(n => new GradingExamViewResponse
                {
                    IsCorrect = CheckCorrect(n, examQuestionScores, userExamItems),
                    Id = userExamItems.FirstOrDefault(x => x.QuestionId == n.QuestionId)?.Id ?? n.Id
                }).ToList()
            }).ToList();

            return result;
        }

        private bool CheckCorrect(Exams.ExamManages.ExamQuestionBak n, List<ExamQuestionScoreBak> examQuestionScores, List<ExamUserExamItem> userExamItems)
        {
            var examQuestionScore = examQuestionScores.FirstOrDefault(x => x.QuestionType == n.QuestionType);
            var userItem = userExamItems.FirstOrDefault(x => x.QuestionId == n.QuestionId);

            if (userItem != null && examQuestionScore != null)
            {
                return userItem.Score == examQuestionScore.Score;
            }

            return false;
        }

        public async Task<PageViewResponse<UserExamResultViewResponse>> GetPagedListAsync(UserExamPagedRequest queryRequest)
        {
            SqlSugar.ISugarQueryable<UserExamResultViewResponse> queryable = GetQueryable(queryRequest);

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

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

            return result;
        }
        public async Task<GradingExamQuestionDto> GradingAsync(GradingExamItemDto gradingExtamItemDto, CancellationToken cancellationToken)
        {
            var userExamItem = await _userExamItemRepository.GetAsync(m => m.Id == gradingExtamItemDto.UserExamItemId);

            if (userExamItem != null)
            {
                userExamItem = _mapper.Map<GradingExamItemDto, ExamUserExamItem>(gradingExtamItemDto, userExamItem);

                await _userExamItemRepository.UpdateWithValidateAsync(userExamItem, cancellationToken);
            }

            return await GetNextExamQuestion(gradingExtamItemDto);
        }

        public async Task SubmitAsync(SubmitExamDto submitExamDto, CancellationToken cancellationToken)
        {
            var userExam = await _repository.GetAsync(x => x.Id == submitExamDto.Id);

            if (userExam != null)
            {
                userExam = _mapper.Map<SubmitExamDto, ExamUserExam>(submitExamDto, userExam);

                userExam.ExamStatus = EExamStatus.Complete;

                await _repository.UpdateWithValidateAsync(userExam, cancellationToken);

                await CompleteExamManage(userExam.ExamId, cancellationToken);
            }
        }

        private async Task CompleteExamManage(string examId, CancellationToken cancellationToken)
        {
            var userExams = await _repository.Queryable().Where(x => x.ExamId == examId).ToListAsync();

            if (userExams.All(x => x.IsSubmit))
            {
                var examManage = await _examManageRepository.GetAsync(x => x.Id == examId);

                examManage.ExamStatus = EExamStatus.Complete;

                await _examManageRepository.UpdateWithValidateAsync(examManage, cancellationToken);
            }
        }

        public async Task<StartExamViewResponse> ExamAsync(UpdateUserExamItemDto updateUserExamItemDto, CancellationToken cancellationToken)
        {
            var userExam = await new ExamRepository<ExamUserExam>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.Id == updateUserExamItemDto.UserExamId).FirstAsync();

            var startExamViewResponse = await CheckExamValid(userExam, cancellationToken);

            if (!startExamViewResponse.CheckValidate())
            {
                await SetExamStatus(userExam, startExamViewResponse, cancellationToken);

                return startExamViewResponse;
            }

            var hasUserExamItem = _userExamItemRepository.Queryable().Where(x => x.UserExamId == updateUserExamItemDto.UserExamId && x.QuestionId == updateUserExamItemDto.QuestionId).Any();

            if (!hasUserExamItem)
            {
                var addUserExamItemDto = _mapper.Map<UpdateUserExamItemDto, AddUserExamItemDto>(updateUserExamItemDto);
                await AddExamAsync(_userExamItemRepository, addUserExamItemDto, cancellationToken);
            }
            else
            {
                await UpdateExamAsync(_userExamItemRepository, updateUserExamItemDto, cancellationToken);
            }

            await CalcuteExamItemScore(_userExamItemRepository, updateUserExamItemDto, cancellationToken);

            return startExamViewResponse;
        }

        private async Task SetExamStatus(ExamUserExam userExam, StartExamViewResponse startExamViewResponse, CancellationToken cancellationToken)
        {
            userExam.ExamStatus = startExamViewResponse.IsCompleted ? EExamStatus.Complete : EExamStatus.NoStart;

            userExam.ToUpdate(_sessionContext);

            await _repository.UpdateWithValidateAsync(userExam, cancellationToken);
        }

        public async Task<StartExamViewResponse> StartUserExamAsync(StartUserExamDto startUserExamDto, CancellationToken cancellationToken)
        {
            var userExam = await _repository.GetAsync(x => x.Id == startUserExamDto.Id);

            if (userExam == null) return new StartExamViewResponse
            {
                IsJoin = false
            };

            if (userExam.ExamStatus == EExamStatus.NoStart || userExam.ExamStatus == EExamStatus.Complete)
                userExam.StartTime = DateTime.Now;

            var startExamViewResponse = await CheckExamValid(userExam, cancellationToken);

            if (!startExamViewResponse.CheckValidate())
            {
                await SetExamStatus(userExam, startExamViewResponse, cancellationToken);

                return startExamViewResponse;
            }

            if (userExam.ExamStatus == EExamStatus.NoStart)
            {
                userExam.ExamStatus = Share.Enums.Exams.EExamStatus.Executing;

                userExam.ToUpdate(_sessionContext);

                await _repository.UpdateWithValidateAsync(userExam, cancellationToken);

            }
            // TODO: 删除之前选项和答案
            else if (userExam.ExamStatus == EExamStatus.Complete)
            {
                await ReExam(userExam, cancellationToken);
            }
            var examManage = await _examManageRepository.GetAsync(x => x.Id == userExam.ExamId);

            return new StartExamViewResponse
            {
                StartTime = userExam.StartTime,
                TimeSpan = examManage?.TimeSpan,
                IsCompleted = false
            };
        }

        /// <summary>
        /// 重考
        /// </summary>
        /// <param name="userExam"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task ReExam(ExamUserExam userExam, CancellationToken cancellationToken)
        {
            //新增重考记录
            var reExamUserExam = _mapper.Map<ExamUserExam, ExamUserExam>(userExam);

            reExamUserExam.IsCheck = false;
            reExamUserExam.IsSubmit = false;
            reExamUserExam.IsSuccess = false;
            reExamUserExam.IsReExam = true;
            reExamUserExam.Score = 0;
            reExamUserExam.ExamStatus = EExamStatus.NoStart;

            reExamUserExam.ToInsert(_sessionContext);

            await _repository.AddWithValidateAsync(reExamUserExam, cancellationToken);
        }

        private async Task<StartExamViewResponse> CheckExamValid(ExamUserExam examUserExam, CancellationToken cancellationToken)
        {

            var examManage = await _examManageRepository.Queryable().Where(x => x.Id == examUserExam.ExamId).FirstAsync();

            var startExamViewResponse = new StartExamViewResponse
            {
                IsJoin = true,
                IsCompleted = false
            };

            if (examManage != null)
            {
                var examStrategyProxy = new ExamStrategyProxy();

                var examStrategys = new List<IExamStrategy>();

                startExamViewResponse = AddCheckStartTimeStrategy(examUserExam, examManage, startExamViewResponse, examStrategys);
                startExamViewResponse = AddCheckEndTimeStrategy(examUserExam, examManage, startExamViewResponse, examStrategys);
                startExamViewResponse = AddCheckValidateTimeStrategy(examUserExam, examManage, startExamViewResponse, examStrategys);
                startExamViewResponse = AddCheckValidateCountStrategy(examUserExam, examManage, startExamViewResponse, examStrategys);

                examStrategyProxy.Initial(examStrategys);

                if (!examStrategyProxy.Validate())
                {
                    startExamViewResponse = (StartExamViewResponse)examStrategyProxy.GetResult();
                    return startExamViewResponse;
                }
            }

            startExamViewResponse.IsStart = true;

            if (examManage.ExamStatus == EExamStatus.NoStart)
            {
                await UpdateExamStatus(examManage, cancellationToken);
            }

            startExamViewResponse.TimeSpan = examManage?.TimeSpan ?? 0;
            startExamViewResponse.StartTime = examUserExam?.StartTime;
            return startExamViewResponse;
        }

        private async Task UpdateExamStatus(ExamManage? examManage, CancellationToken cancellationToken)
        {
            examManage.ExamStatus = EExamStatus.Executing;

            examManage.ToUpdate(_sessionContext);

            await _examManageRepository.UpdateWithValidateAsync(examManage, cancellationToken);
        }

        private StartExamViewResponse AddCheckValidateCountStrategy(ExamUserExam examUserExam, ExamManage examManage, StartExamViewResponse startExamViewResponse, List<IExamStrategy> examStrategys)
        {
            if (examManage.ExamType == Share.Enums.Exams.EExamType.Simulate)
            {
                var checkStartTime = new CheckValidateCountStrategy(1, 1)
                {
                    CallBack = () =>
                    {
                        var response = new StartExamViewResponse
                        {
                            IsJoin = false,
                            IsStart = false,
                            IsCompleted = true,
                            StartTime = examUserExam.StartTime,
                            TimeSpan = examManage.TimeSpan
                        };

                        return response;
                    }
                };
                examStrategys.Add(checkStartTime);
                return startExamViewResponse;
            }
            else
            {
                var count = new ExamRepository<ExamUserExam>(_uow, _dataPermissionFilterBuilder, _serviceProvider).CountAsync(x => x.ExamId == examManage.Id && x.UserId == examUserExam.UserId).Result;

                var checkStartTime = new CheckValidateCountStrategy(examManage.Count, count)
                {
                    CallBack = () =>
                    {
                        var response = new StartExamViewResponse
                        {
                            IsJoin = false,
                            IsStart = false,
                            IsCompleted = true,
                            StartTime = examUserExam.StartTime,
                            TimeSpan = examManage.TimeSpan
                        };

                        return response;
                    }
                };
                examStrategys.Add(checkStartTime);
                return startExamViewResponse;
            }

        }

        private StartExamViewResponse AddCheckValidateTimeStrategy(ExamUserExam examUserExam, ExamManage examManage, StartExamViewResponse startExamViewResponse, List<IExamStrategy> examStrategys)
        {
            if (examManage.ExamType == Share.Enums.Exams.EExamType.Simulate) return startExamViewResponse;

            var checkStartTime = new CheckValidateTimeStrategy(examManage.TimeSpan, examUserExam.StartTime)
            {
                CallBack = () =>
                {
                    var response = new StartExamViewResponse
                    {
                        IsJoin = false,
                        IsStart = false,
                        IsCompleted = true,
                        StartTime = examUserExam.StartTime,
                        TimeSpan = examManage.TimeSpan
                    };

                    return response;
                }
            };
            examStrategys.Add(checkStartTime);
            return startExamViewResponse;
        }

        private StartExamViewResponse AddCheckEndTimeStrategy(ExamUserExam examUserExam, ExamManage examManage, StartExamViewResponse startExamViewResponse, List<IExamStrategy> examStrategys)
        {
            if (examManage.ExamType == Share.Enums.Exams.EExamType.Simulate) return startExamViewResponse;

            var checkStartTime = new CheckEndTimeStrategy(examManage.EndTime, DateTime.Now)
            {
                CallBack = () =>
                {
                    var response = new StartExamViewResponse
                    {
                        IsJoin = false,
                        IsStart = false,
                        IsCompleted = true,
                        StartTime = examUserExam.StartTime,
                        TimeSpan = examManage.TimeSpan
                    };

                    return response;
                }
            };
            examStrategys.Add(checkStartTime);
            return startExamViewResponse;
        }

        private static StartExamViewResponse AddCheckStartTimeStrategy(ExamUserExam examUserExam, ExamManage examManage, StartExamViewResponse startExamViewResponse, List<IExamStrategy> examStrategys)
        {
            if (examManage.ExamType == Share.Enums.Exams.EExamType.Simulate) return startExamViewResponse;

            var checkStartTime = new CheckStartTimeStrategy(examManage.StartTime, examUserExam.StartTime)
            {
                CallBack = () =>
                {
                    var response = new StartExamViewResponse
                    {
                        IsJoin = false,
                        IsStart = false,
                        IsCompleted = false,
                        StartTime = examUserExam.StartTime,
                        TimeSpan = examManage.TimeSpan
                    };

                    return response;
                }
            };
            examStrategys.Add(checkStartTime);
            return startExamViewResponse;
        }

        public async Task CompleteGradingAsync(GradingExamDto gradingExtamDto, CancellationToken cancellationToken)
        {
            var userExam = await _repository.GetAsync(x => x.Id == gradingExtamDto.Id);

            if (userExam == null) return;

            var userExamItems = await _userExamItemRepository.Queryable().Where(x => x.UserExamId == gradingExtamDto.Id).ToListAsync();

            if (userExamItems != null)
            {
                var totalScore = userExamItems.Sum(x => x.Score);
                userExam.Score = totalScore;

                var examManage = await new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider).GetAsync(x => x.Id == userExam.ExamId);
                userExam.IsSuccess = totalScore >= examManage.CutoffScore;

                userExam.ExamStatus = Share.Enums.Exams.EExamStatus.Complete;

                userExam.ToUpdate(_sessionContext);

                await _repository.UpdateWithValidateAsync(userExam, cancellationToken);
            }
        }

        public async Task<List<GradingExamQuestionDto>> GetGradingExamQuestion(GradingExamRequest gradingExamRequest)
        {
            var expression = gradingExamRequest.GetExpression();

            ISugarQueryable<GradingExamQuestionTempDto> queryable = GetViewExamQuestionTempDtos(expression);

            queryable =
            queryable.MergeTable().Where((q) => !(q.QuestionType == EQuestionType.Single || q.QuestionType == EQuestionType.Multi || q.QuestionType == EQuestionType.Judge));

            var queryResult = await queryable.ToListAsync();

            var gradingExamQuestionDtos = queryResult.GroupBy(x => new
            {
                Id = x.Id,
                QuestionType = x.QuestionType
            }).Select(g => new GradingExamQuestionDto
            {
                Answer = g.FirstOrDefault().Answer,
                QuestionType = g.Key.QuestionType,
                Id = g.Key.Id,
                QuestionScore = g.FirstOrDefault().QuestionScore,
                Score = g.FirstOrDefault().Score,
                Title = g.FirstOrDefault().Title,
                CorrectAnswer = g.Key.QuestionType.CheckSelectType() ? string.Join(",", g.Where(i => i.IsAnswer).Select(n => n.Label).Distinct()) : g.FirstOrDefault()?.CorrectAnswer
            }).ToList();

            return gradingExamQuestionDtos;
        }

        private ISugarQueryable<GradingExamQuestionTempDto> GetViewExamQuestionTempDtos(Expression<Func<ExamUserExam, bool>> expression)
        {

            var userExamTable = _repository.Queryable().Where(expression);

            var questionScoreRepository = new ExamRepository<ExamQuestionScoreBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var userExamItemTable = _userExamItemRepository.Queryable();
            var userExamItemOptionTable = _userExamItemOptionRepository.Queryable();
            var examAnswerTable = new ExamRepository<ExamAnswer>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
            var questionTable = new ExamRepository<Exams.ExamManages.ExamQuestionBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
            var quesitonOptionTable = new ExamRepository<ExamQuestionOptionsBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
            var testPaperItemAnswerTable = new ExamRepository<ExamQuestionAnswerBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
            var questionScoreTable = questionScoreRepository.Queryable();

            return userExamTable.InnerJoin(userExamItemTable, (e, i) => e.Id == i.UserExamId)
                .InnerJoin(questionTable, (e, i, q) => i.QuestionId == q.QuestionId && q.ExamId == e.ExamId)
                .LeftJoin(userExamItemOptionTable, (e, i, q, o) => i.Id == o.UserExamItemId)
                .LeftJoin(quesitonOptionTable, (e, i, q, o, qo) => o.QuestionOptionId == qo.Id)
                .LeftJoin(examAnswerTable, (e, i, q, o, qo, a) => i.Id == a.UserExamItemId)
                .LeftJoin(testPaperItemAnswerTable, (e, i, q, o, qo, a, ta) => ta.QuestionId == q.QuestionId)
                .InnerJoin(questionScoreTable, (e, i, q, o, qo, a, ta, s) => q.QuestionType == s.QuestionType && e.ExamId == s.ExamManageId)
            .Select(
            (e, i, q, o, qo, a, ta, s) => new GradingExamQuestionTempDto
            {
                Id = i.Id,
                QuestionType = q.QuestionType,
                CorrectAnswer = ta.Id != null ? ta.Answer : string.Empty,
                Answer = a.Id != null ? a.Answer : string.Empty,
                Title = q.Title,
                QuestionOptionId = o.Id,
                QuestionId = q.QuestionId,
                ExamQuestionId = q.Id,
                UserExamItemId = i.Id,
                Content = qo.Content,
                Label = qo.Label,
                IsAnswer = qo.IsAnswer,
                IsSelected = o.Id != null,
                QuestionScore = s.Score,
                Score = i.Score
            }
                );
        }

        public async Task<GradingExamQuestionDto> ViewGradingExamQuestion(ViewGradingExamRequest viewGradingExamRequest)
        {
            var gradingExtamItemDto = _mapper.Map<ViewGradingExamRequest, GradingExamItemDto>(viewGradingExamRequest);

            return await GetNextExamQuestion(gradingExtamItemDto);
        }


        public async Task<UnExamUserPageViewResponse> GetUnExamUsersPaged(UnExamUserReportPagedRequest unExamUserReportPagedRequest)
        {
            ISugarQueryable<UnExamUserViewResponse> queryResult = GEtUnExamUserQueryable(unExamUserReportPagedRequest);

            var total = await queryResult.CountAsync();

            var items = unExamUserReportPagedRequest.IsPaged ?
                await queryResult.ToPageListAsync(unExamUserReportPagedRequest.PageIndex, unExamUserReportPagedRequest.PageSize)
                : await queryResult.ToListAsync();

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

        }

        public async Task<List<UnExamUserViewResponse>> GetUnExamUsers(UnExamUserReportPagedRequest unExamUserReportPagedRequest)
        {
            ISugarQueryable<UnExamUserViewResponse> queryResult = GEtUnExamUserQueryable(unExamUserReportPagedRequest);

            var items = await queryResult.ToListAsync();

            return items;
        }

        private ISugarQueryable<UnExamUserViewResponse> GEtUnExamUserQueryable(UnExamUserReportPagedRequest unExamUserReportPagedRequest)
        {
            if (unExamUserReportPagedRequest.EndTime == null)
                unExamUserReportPagedRequest.ResolveEndTime();

            var examManageRepository = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var userRepository = new ExamRepository<User>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var expression = unExamUserReportPagedRequest.GetExpression();
            var userExamExpression = unExamUserReportPagedRequest.GetUserExamExpression();
            var examManageTable = examManageRepository.Queryable().Where(expression);
            var userTable = userRepository.Queryable();
            var userExamTable = _repository.Queryable().Where(userExamExpression);

            var queryResult = userExamTable.InnerJoin(examManageTable, (ue, e) => ue.ExamId == e.Id)
                .InnerJoin(userTable, (ue, e, u) => ue.UserId == u.Id)
                .Select((ue, e, u) => new UnExamUserViewResponse
                {
                    ExamName = e.Name,
                    OrgName = u.Organization.Name,
                    UserName = u.Name
                })
                .MergeTable()
                .OrderByPropertyNameIF(!string.IsNullOrEmpty(unExamUserReportPagedRequest.SortField), unExamUserReportPagedRequest.SortField, (OrderByType)(unExamUserReportPagedRequest.SortRule ?? 0));
            return queryResult;
        }
        public async Task<UserExamResultPageViewResponse> GetUserExamResults(UserExamResultReportPagedRequest userExamResultReportPagedRequest)
        {
            if (userExamResultReportPagedRequest.EndTime == null)
                userExamResultReportPagedRequest.ResolveEndTime();

            var examManageRepository = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var userRepository = new ExamRepository<User>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var expression = userExamResultReportPagedRequest.GetExpression();
            var userExamExpression = userExamResultReportPagedRequest.GetUserExamExpression();
            var examManageTable = examManageRepository.Queryable().Where(expression);
            var userTable = userRepository.Queryable();
            var userExamTable = _repository.Queryable().Where(userExamExpression);

            var queryResult = userExamTable.InnerJoin(examManageTable, (ue, e) => ue.ExamId == e.Id)
                .InnerJoin(userTable, (ue, e, u) => ue.UserId == u.Id)
               .Select((ue, e, u) => new UserExamResultViewResponse
               {
                   ExamName = e.Name,
                   OrgName = u.Organization.Name,
                   UserName = u.Name,
                   TotalScore = e.TotalScore,
                   CutoffScore = e.CutoffScore,
                   Score = ue.Score ?? 0,

               })
                .MergeTable()
               .OrderByPropertyNameIF(!string.IsNullOrEmpty(userExamResultReportPagedRequest.SortField), userExamResultReportPagedRequest.SortField, (OrderByType)(userExamResultReportPagedRequest.SortRule ?? 0))
                .OrderByDescending(x => x.Score)
      ;
            
            var total = await queryResult.CountAsync();
            var items = userExamResultReportPagedRequest.IsPaged ?
                await queryResult.ToPageListAsync(userExamResultReportPagedRequest.PageIndex, userExamResultReportPagedRequest.PageSize)
                : await queryResult.ToListAsync();
                ;

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



        public async Task<GradingResultPageViewResponse> GetGradingResultPagedList(GradingPagedRequest gradingPagedRequest)
        {
            // 只要有阅卷记录就在已阅卷列表中，已阅卷和未阅卷会有重复数据，只有所有记录都已阅卷才会从未阅卷列表中排除
            var userExamTable = _repository.Queryable().Where(x => x.IsSubmit);

            var examManageTable = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();

            var queryable = userExamTable.InnerJoin(examManageTable, (u, e) => u.ExamId == e.Id).GroupBy((u, e) => new
            {
                ExamName = e.Name,
                ExamCode = e.Code,
                CutoffScore = e.CutoffScore,
                TotalScore = e.TotalScore,
                Id = e.Id,
                Status = e.Status,
                Remark = e.Remark
            }).Select((u, e) => new GradingResultViewResponse
            {
                CutoffScore = e.CutoffScore,
                TotalScore = e.TotalScore,
                ExamName = e.Name,
                ExamCode = e.Code,
                IsCheck = SqlFunc.Subqueryable<ExamUserExam>().Where(x => x.ExamId == e.Id && !x.IsCheck).Count() <= 0,
                Id = e.Id,
                Remark = e.Remark
            }).MergeTable();

            queryable = queryable.WhereIF(gradingPagedRequest.IsCheck != null, x => x.IsCheck == gradingPagedRequest.IsCheck);

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

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

            return result;
        }

        public async Task BatchGradingAsync(BatchGradingExamItemDto batchGradingExamItemDto, CancellationToken cancellationToken)
        {

            var userExamItemIds = batchGradingExamItemDto.Items.Select(x => x.UserExamItemId);
            var userExamItems = await _userExamItemRepository.Queryable().Where(m => userExamItemIds.Contains(m.Id)).ToListAsync();

            if (userExamItems != null && userExamItemIds.Any())
            {
                var updateUserExamItems = new List<ExamUserExamItem>();
                userExamItems.ForEach(x =>
                {
                    var gradingExamItemDto = batchGradingExamItemDto.Items.Find(m => m.UserExamItemId == x.Id);

                    var updateUserExamItem = _mapper.Map<GradingExamItemDto, ExamUserExamItem>(gradingExamItemDto, x);

                    updateUserExamItem.IsCheck = true;

                    updateUserExamItems.Add(updateUserExamItem);
                });

                updateUserExamItems.ToUpdate(_sessionContext);

                await _userExamItemRepository.UpdateWithValidateAsync(updateUserExamItems, cancellationToken);

                var userExamId = userExamItems.FirstOrDefault()?.UserExamId;
                // 计算本次考试得分
                var userExamItemsInCheck = await _userExamItemRepository.Queryable()
                    .InnerJoin<ExamQuestionBak>((u, q) => u.QuestionId == q.QuestionId)
                    .Where((u, q) => u.UserExamId == userExamId && (q.QuestionType == EQuestionType.Single || q.QuestionType == EQuestionType.Multi || q.QuestionType == EQuestionType.Judge)).Select((u, q) => new UpdateUserExamItemDto
                    {
                        QuestionId = u.QuestionId,
                        QuestionType = q.QuestionType,
                        UserExamId = u.UserExamId,
                        Id = u.Id
                    }).Distinct().ToListAsync();
                await CalcuteExamItemScore(_userExamItemRepository, userExamItemsInCheck, cancellationToken);

                await CalcuteTotalScore(_userExamItemRepository, userExamId, cancellationToken);

            }


        }

        public async Task<List<ExamUserViewResponse>> GetUserListAsync(ExamUserQueryRequest examUserQueryRequest)
        {
            var userExamTable = _repository.Queryable().Where(x => x.ExamId == examUserQueryRequest.ExamId);
            var queryable = await userExamTable.InnerJoin<User>((ux, u) => ux.UserId == u.Id).Select((ux, u) => new ExamUserViewResponse
            {
                ExamId = ux.ExamId,
                UserId = ux.UserId,
                UserName = u.Name,
                Id = ux.Id
            }).ToListAsync();

            return queryable;
        }
        #endregion

        #region private method



        private async Task CalcuteExamItemScore(IUserExamItemRepository userExamRepository, UpdateUserExamItemDto addUserExamItemDto, CancellationToken cancellationToken)
        {
            if (!addUserExamItemDto.QuestionType.CheckSelectType()) return;

            var testPaperItemOptionsRepository = new ExamRepository<ExamQuestionOptionsBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var examManageRepository = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var testPaperItemRepository = new ExamRepository<Exams.ExamManages.ExamQuestionBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var examQuestionScoreRepository = new ExamRepository<ExamQuestionScoreBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var testPaperOptionsTable = testPaperItemOptionsRepository.Queryable().Where(x => x.QuestionId == addUserExamItemDto.QuestionId && x.IsAnswer);
            var testPaperItemTable = testPaperItemRepository.Queryable();
            var userExamTable = _repository.Queryable().Where(x => x.Id == addUserExamItemDto.UserExamId);
            var examManageTable = examManageRepository.Queryable();
            var testPaperOptionIds = await testPaperOptionsTable.InnerJoin(testPaperItemTable, (t, i) => t.ExamQuestionId == i.Id)
                .InnerJoin(examManageTable, (t, i, e) => i.ExamId == e.Id)
                .InnerJoin(userExamTable, (t, i, e, u) => e.Id == u.ExamId)
                .Select((t, i, e, u) => t.Id).ToListAsync();

            var isCorrect = addUserExamItemDto.UserExamItemOptionDtos.Select(x => x.QuestionOptionId).OrderBy(x => x).SequenceEqual(testPaperOptionIds.OrderBy(x => x));

            var userExamItem = await userExamRepository.GetAsync(x => x.UserExamId == addUserExamItemDto.UserExamId && x.QuestionId == addUserExamItemDto.QuestionId);
            var examQuesiontScores = await examQuestionScoreRepository.Queryable().Where(x => x.QuestionType == addUserExamItemDto.QuestionType)
                .InnerJoin(userExamTable, (e, u) => e.Id == u.ExamId)
                .Select((e, u) => e).ToListAsync();
            userExamItem.IsCheck = true;
            userExamItem.Score = isCorrect ? examQuesiontScores.FirstOrDefault()?.Score : 0;
            userExamItem.ToUpdate(_sessionContext);
            await userExamRepository.UpdateWithValidateAsync(userExamItem, cancellationToken);
        }

        private async Task CalcuteExamItemScore(IUserExamItemRepository userExamItemRepository, List<UpdateUserExamItemDto> addUserExamItemDtos, CancellationToken cancellationToken)
        {
            var questionIds = addUserExamItemDtos.Select(x => x.QuestionId).ToList();
            var userExamIds = addUserExamItemDtos.Select(x => x.UserExamId).ToList();
            var questionTypes = addUserExamItemDtos.Select(x => x.QuestionType).ToList();

            List<ExamQuestionOptionsBak> examQuestionOptions = await GetQuestionOptionBaks(questionIds, userExamIds, true).ToListAsync();

            var userExamItemOptionRepository = new ExamRepository<ExamUserExamItemOptions>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var examQuestionScoreRepository = new ExamRepository<ExamQuestionScoreBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var userExamTable = _repository.Queryable().Where(x => userExamIds.Contains(x.Id));

            var userExamItems = await userExamItemRepository.Queryable().Where(x => userExamIds.Contains(x.UserExamId)).ToListAsync();
            var userExamItemIds = userExamItems.Select(x => x.Id).ToList();
            var userExamItemOptions = await userExamItemOptionRepository.Queryable().Where(x => userExamItemIds.Contains(x.UserExamItemId)).ToListAsync();
            var examQuesiontScores = await examQuestionScoreRepository.Queryable().Where(x => questionTypes.Contains(x.QuestionType))
                .InnerJoin(userExamTable, (e, u) => e.ExamManageId == u.ExamId)
                .Select((e, u) => e).ToListAsync();


            foreach (var addUserExamItemDto in addUserExamItemDtos)
            {
                var examQuestionOptionIds = examQuestionOptions.Where(x => x.QuestionId == addUserExamItemDto.QuestionId).Select(x => x.Id).ToList();
                var isCorrect = userExamItemOptions.Where(x => x.UserExamItemId == addUserExamItemDto.Id).Select(x => x.QuestionOptionId).OrderBy(x => x).SequenceEqual(examQuestionOptionIds.OrderBy(x => x));
                var userExamItem = userExamItems.FirstOrDefault(x => x.QuestionId == addUserExamItemDto.QuestionId);
                if (userExamItem != null)
                {
                    userExamItem.IsCheck = true;
                    userExamItem.Score = isCorrect ? examQuesiontScores.FirstOrDefault(x => x.QuestionType == addUserExamItemDto.QuestionType)?.Score : 0;
                    userExamItem.ToUpdate(_sessionContext);
                }
            }


            await userExamItemRepository.UpdateWithValidateAsync(userExamItems, cancellationToken);
        }

        private ISugarQueryable<ExamQuestionOptionsBak> GetQuestionOptionBaks(List<string> questionIds, List<string> userExamIds, bool isFilterAnswer)
        {
            var questionRepository = new ExamRepository<Exams.ExamManages.ExamQuestionBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var questionTable = questionRepository.Queryable().Where(x => x.QuestionType == EQuestionType.Single || x.QuestionType == EQuestionType.Multi || x.QuestionType == EQuestionType.Judge);
            var userExamTable = _repository.Queryable().Where(x => userExamIds.Contains(x.Id));
            var examManageRepository = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var examManageTable = examManageRepository.Queryable();
            var examQuestionOptionsRepository = new ExamRepository<ExamQuestionOptionsBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var examQuestionOptionsTable = examQuestionOptionsRepository.Queryable()
                .Where(x => questionIds.Contains(x.QuestionId))
                .WhereIF(isFilterAnswer, x => x.IsAnswer);
            var examQuestionOptions = examQuestionOptionsTable.InnerJoin(questionTable, (t, i) => t.ExamQuestionId == i.Id)
                .InnerJoin(examManageTable, (t, i, e) => i.ExamId == e.Id)
                .InnerJoin(userExamTable, (t, i, e, u) => e.Id == u.ExamId)
                .Select((t, i, e, u) => new ExamQuestionOptionsBak
                {
                    Id = t.Id,
                    Content = t.Content,
                    IsAnswer = t.IsAnswer,
                    Label = t.Label,
                    ExamQuestionId = t.ExamQuestionId,
                    QuestionId = t.QuestionId
                });
            return examQuestionOptions;
        }

        private async Task CalcuteTotalScore(IUserExamItemRepository userExamItemRepository, string userExamId, CancellationToken cancellationToken)
        {
            var userExam = await _repository.GetAsync(x => x.Id == userExamId);

            if (userExam != null)
            {
                var userExamItems = await userExamItemRepository.Queryable().Where(x => x.UserExamId == userExamId).ToListAsync();
                var examManageRepository = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
                var examManage = await examManageRepository.GetAsync(x => x.Id == userExam.ExamId);

                var totalScore = userExamItems.Sum(x => x.Score);

                userExam.Score = totalScore;

                userExam.IsCheck = true;

                userExam.ExamStatus = EExamStatus.Complete;


                if (examManage != null)
                {
                    userExam.IsSuccess = userExam.Score > examManage.CutoffScore;
                }

                userExam.ToUpdate(_sessionContext);

                await _repository.UpdateWithValidateAsync(userExam, cancellationToken);
            }
        }

        private async Task AddExamAsync(IRepository<ExamUserExamItem> userExamItemRepository, AddUserExamItemDto addUserExamItemDto, CancellationToken cancellationToken)
        {
            var userExamItem = await AddUserExamItem(addUserExamItemDto, cancellationToken);

            userExamItem.UserExamItemOptionses = await AddUserExamItemOptions(addUserExamItemDto, cancellationToken);

            userExamItem.ExamAnswers = await AddExamAnswer(addUserExamItemDto, userExamItem.Id, cancellationToken);


            await userExamItemRepository.AddNav(userExamItem)
                .Include(x => x.UserExamItemOptionses)
                .Include(x => x.ExamAnswers)
                .ExecuteCommandAsync();
        }

        private async Task UpdateExamAsync(IRepository<ExamUserExamItem> userExamItemRepository, UpdateUserExamItemDto updateUserExamItemDto, CancellationToken cancellationToken)
        {
            var userExamItem = await UpdateUserExamItem(updateUserExamItemDto, cancellationToken);

            userExamItem.UserExamItemOptionses = await ModifyUserItemOptions(updateUserExamItemDto, cancellationToken);

            userExamItem.ExamAnswers = await UpdateExamAnswer(updateUserExamItemDto, cancellationToken);

            await userExamItemRepository.UpdateNav(userExamItem)
              .Include(x => x.UserExamItemOptionses)
              .Include(x => x.ExamAnswers)
              .ExecuteCommandAsync();
        }
        private async Task<GradingExamQuestionDto> GetNextExamQuestion(GradingExamItemDto gradingExtamItemDto)
        {
            // TODO: 获取未阅卷的第一道题
            var current = _userExamItemRepository.Queryable().Where(x => x.Id == gradingExtamItemDto.UserExamItemId);
            var userExamItemTable = _userExamItemRepository.Queryable().Where(x => !x.IsCheck);

            var userExamItem = current.InnerJoin(userExamItemTable, (c, u) => c.UserExamId == u.UserExamId).OrderBy((c, u) => c.SortIndex).First();

            if (userExamItem != null)
            {
                var testPaperItem = new ExamRepository<Exams.ExamManages.ExamQuestionBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.Id == userExamItem.Id && !x.QuestionType.CheckSelectType()).First();

                if (testPaperItem == null) return null;

                var gradingExamQuestionDto = new GradingExamQuestionDto();

                gradingExamQuestionDto = _mapper.Map<Exams.ExamManages.ExamQuestionBak, GradingExamQuestionDto>(testPaperItem, gradingExamQuestionDto);

                var examAnswer = new ExamRepository<ExamAnswer>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.UserExamItemId == userExamItem.Id).First();

                var testPaperItemAnswer = await new ExamRepository<ExamQuestionAnswerBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().FirstAsync(x => x.QuestionId == testPaperItem.QuestionId && x.ExamQuestionId == testPaperItem.Id);

                gradingExamQuestionDto.Answer = examAnswer.Answer ?? string.Empty;

                gradingExamQuestionDto.CorrectAnswer = testPaperItemAnswer != null ? testPaperItemAnswer.Answer ?? string.Empty : string.Empty;


                return gradingExamQuestionDto;

            }

            return null;
        }

        private async Task<List<ExamAnswer>> AddExamAnswer(AddUserExamItemDto addUserExamItemDto, string id, CancellationToken cancellationToken)
        {
            if (addUserExamItemDto.QuestionType.CheckSelectType()) return null;

            var examAnswers = new List<ExamAnswer>();

            var examAnswer = new ExamAnswer
            {
                UserId = _sessionContext.UserId,
                UserExamItemId = id,
                Answer = addUserExamItemDto.Answer
            };

            examAnswer.ToInsert(_sessionContext);

            await _examAnswerRepository.ValidateAddAsync(examAnswer, cancellationToken);

            examAnswers.Add(examAnswer);

            return examAnswers;
        }


        private async Task<UserExamQuestionDto> GetNextExamQuestion(AddUserExamItemDto addUserExamItemDto)
        {
            // TODO: 获取未阅卷的第一道题
            var testPaperItemRepository = new ExamRepository<Exams.ExamManages.ExamQuestionBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var examRepository = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
            var userExamTable = _repository.Queryable().Where(x => x.Id == addUserExamItemDto.UserExamId);
            var userExamItemTable = _userExamItemRepository.Queryable().WhereIF(addUserExamItemDto.QuestionId.IsNotNullOrEmpty(), x => x.QuestionId == addUserExamItemDto.QuestionId);
            var examTable = examRepository.Queryable();
            var testPaperItemTable = testPaperItemRepository.Queryable();
            var current = examTable.InnerJoin(testPaperItemTable, (e, t) => e.Id == t.ExamId)
                .InnerJoin(userExamTable, (e, t, u) => e.Id == u.ExamId)
                .InnerJoin(userExamItemTable, (e, t, u, i) => t.QuestionId == i.QuestionId).Select((e, t, u, i) => t);
            var nextTable = testPaperItemTable.InnerJoin(current, (t, c) => t.SortIndex > c.SortIndex).OrderBy((t, c) => t.SortIndex).Select((t, c) => t);

            var userExamItem = userExamItemTable.InnerJoin(nextTable, (u, n) => u.QuestionId == n.QuestionId).OrderBy((u, n) => u.SortIndex).Select((u, n) => u).First();

            if (userExamItem != null)
            {
                var question = testPaperItemRepository.Queryable().Where(x => x.Id == userExamItem.Id).First();

                if (question == null) return null;

                var userExamQuestionDto = new UserExamQuestionDto();

                userExamQuestionDto = _mapper.Map<Exams.ExamManages.ExamQuestionBak, UserExamQuestionDto>(question, userExamQuestionDto);

                if (question.QuestionType.CheckSelectType())
                {
                    var userExamItemOptionTable = _userExamItemOptionRepository.Queryable().Where(x => x.UserExamItemId == userExamItem.Id);
                    var quesitonOptionTable = new ExamRepository<ExamQuestionOptionsBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();

                    var queryResult = userExamItemOptionTable.InnerJoin(quesitonOptionTable, (u, q) => u.QuestionOptionId == q.Id)
                        .Select((u, q) => new UserExamItemOptionDto
                        {
                            Content = q.Content,
                            QuestionOptionId = u.QuestionOptionId,
                            UserExamItemId = u.UserExamItemId
                        });

                    userExamQuestionDto.UserExamItemOptionDtos = queryResult.ToList();
                }
                else
                {
                    var examAnswer = new ExamRepository<ExamAnswer>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.UserExamItemId == userExamItem.Id).First();

                    userExamQuestionDto.Answer = examAnswer.Answer ?? string.Empty;
                }

                return userExamQuestionDto;
            }
            else
            {
                return null;
            }


        }

        private async Task<List<ExamAnswer>> UpdateExamAnswer(UpdateUserExamItemDto updateUserExamItemDto, CancellationToken cancellationToken)
        {
            var examAnswerTable = _examAnswerRepository.Queryable();

            var userExamItemTable = _userExamItemRepository.Queryable().Where(x => x.Id == updateUserExamItemDto.Id);

            var examAnswer = await examAnswerTable.InnerJoin(userExamItemTable, (e, u) => e.UserExamItemId == u.Id).Select((e, u) => e).FirstAsync();

            if (!updateUserExamItemDto.QuestionType.CheckSelectType())
            {
                var examAnswers = new List<ExamAnswer>();

                if (examAnswer != null)
                {
                    examAnswer.Answer = updateUserExamItemDto.Answer;
                    examAnswer.UserId = _sessionContext.UserId;
                    examAnswer.UserExamItemId = updateUserExamItemDto.Id;

                    examAnswer.ToUpdate(_sessionContext);

                    await _examAnswerRepository.UpdateWithValidateAsync(examAnswer, cancellationToken);

                    examAnswers.Add(examAnswer);
                }
                else
                {
                    examAnswer = new ExamAnswer
                    {
                        Answer = updateUserExamItemDto.Answer,
                        UserId = _sessionContext.UserId,
                        UserExamItemId = updateUserExamItemDto.Id
                    };
                    examAnswer.ToInsert(_sessionContext);

                    await _examAnswerRepository.ValidateAddAsync(examAnswer, cancellationToken);

                    examAnswers.Add(examAnswer);
                }

                return examAnswers;
            }

            return null;
        }


        private async Task<List<ExamUserExamItemOptions>> ModifyUserItemOptions(UpdateUserExamItemDto updateUserExamItemDto, CancellationToken cancellationToken)
        {
            if (updateUserExamItemDto.QuestionType.CheckSelectType())
            {

                var entityQuestionRequest = new EntityQueryRequest
                {
                    Expression = ExpressionableUtility.CreateExpression<ExamUserExamItemOptions>()
              .AndIF(updateUserExamItemDto.Id.IsNotEmpty(), x => x.UserExamItemId == updateUserExamItemDto.Id).ToExpression()
                };

                await DeleteUserExamItemOptions(entityQuestionRequest, cancellationToken);

                //await UpdateUserItemOptions(updateUserExamItemDto, cancellationToken);

                var addUserExamItemDto = _mapper.Map<AddUserExamItemDto>(updateUserExamItemDto);

                addUserExamItemDto.UserExamItemOptionDtos = new List<AddUserExamItemOptionDto>();

                updateUserExamItemDto.UserExamItemOptionDtos.ForEach(item =>
                {
                    addUserExamItemDto.UserExamItemOptionDtos.Add(_mapper.Map<AddUserExamItemOptionDto>(item));
                });

                return await AddUserExamItemOptions(addUserExamItemDto, cancellationToken);
            }

            return null;
        }

        private async Task UpdateUserItemOptions(UpdateUserExamItemDto updateUserExamItemDto, CancellationToken cancellationToken)
        {

            if (updateUserExamItemDto.QuestionType.CheckSelectType())
            {

                var userExamItemOptions = await _userExamItemOptionRepository.Queryable().Where(x => x.UserExamItemId == updateUserExamItemDto.Id).ToListAsync();

                var entities = new List<ExamUserExamItemOptions>();

                if (updateUserExamItemDto.UserExamItemOptionDtos != null)
                {
                    updateUserExamItemDto.UserExamItemOptionDtos.Where(m => m.OperationStatus == EEOperationStatus.Update).ToList().ForEach(x =>
                    {
                        var entity = userExamItemOptions.FirstOrDefault(m => m.Id == x.Id);
                        if (entity != null)
                        {
                            entities.Add(_mapper.Map<UpdateUserExamItemOptionDto, ExamUserExamItemOptions>(x, entity));
                        }

                    });
                }

                entities.ToUpdate(_sessionContext);

                await _userExamItemOptionRepository.UpdateWithValidateAsync(entities, cancellationToken);
            }


        }

        private async Task<ExamUserExamItem> UpdateUserExamItem(UpdateUserExamItemDto updateUserExamItemDto, CancellationToken cancellationToken)
        {
            var userExamItem = await _userExamItemRepository.GetAsync(x => x.UserExamId == updateUserExamItemDto.UserExamId && x.QuestionId == updateUserExamItemDto.QuestionId);

            userExamItem = _mapper.Map<UpdateUserExamItemDto, ExamUserExamItem>(updateUserExamItemDto, userExamItem);

            updateUserExamItemDto.Id = userExamItem.Id;

            userExamItem.ToUpdate(_sessionContext);

            await _userExamItemRepository.UpdateWithValidateAsync(userExamItem, cancellationToken);

            if (updateUserExamItemDto.QuestionType.CheckSelectType())
            {
                if (updateUserExamItemDto.UserExamItemOptionDtos != null)
                {
                    updateUserExamItemDto.UserExamItemOptionDtos.ForEach(x => x.UserExamItemId = updateUserExamItemDto.Id);
                }
            }

            return userExamItem;
        }


        private async Task<List<ExamUserExamItemOptions>> AddUserExamItemOptions(AddUserExamItemDto addUserExamItemDto, CancellationToken cancellationToken)
        {
            var userExamItemOptions = new List<ExamUserExamItemOptions>();

            if (addUserExamItemDto.QuestionType.CheckSelectType())
            {
                if (addUserExamItemDto.UserExamItemOptionDtos != null)
                {
                    addUserExamItemDto.UserExamItemOptionDtos.ToList().ForEach(x =>
                    {
                        userExamItemOptions.Add(_mapper.Map<ExamUserExamItemOptions>(x));
                    });
                }
            }

            userExamItemOptions.ToInsert(_sessionContext);

            await _userExamItemOptionRepository.ValidateAddAsync(userExamItemOptions, cancellationToken);

            return userExamItemOptions;
        }

        private async Task<ExamUserExamItem> AddUserExamItem(AddUserExamItemDto addUserExamItemDto, CancellationToken cancellationToken)
        {
            var userExamItem = _mapper.Map<ExamUserExamItem>(addUserExamItemDto);

            userExamItem.ToInsert(_sessionContext);

            await _userExamItemRepository.ValidateAddAsync(userExamItem, cancellationToken);

            if (addUserExamItemDto.QuestionType.CheckSelectType())
            {
                if (addUserExamItemDto.UserExamItemOptionDtos != null)
                {
                    addUserExamItemDto.UserExamItemOptionDtos.ForEach(x => x.UserExamItemId = userExamItem.Id);
                }
            }

            return userExamItem;
        }

        private async Task DeleteUserExamItemOptions(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
        {
            await _userExamItemOptionRepository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
        }

        private SqlSugar.ISugarQueryable<UserExamResultViewResponse> GetQueryable(UserExamPagedRequest queryRequest)
        {
            if (_sessionContext.UserId != null)
            {
                queryRequest.UserId = _sessionContext.UserId;
            }
            var expression = queryRequest.GetExpression();
            var userExamTable = _repository.Queryable().Where(expression);

            var examManageExpression = queryRequest.GetExamManageExpression();
            var examManageTable = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(examManageExpression);

            var queryable = userExamTable.InnerJoin(examManageTable, (u, e) => u.ExamId == e.Id).Select((u, e) => new UserExamResultViewResponse
            {
                Id = u.Id,
                CutoffScore = e.CutoffScore,
                TotalScore = e.TotalScore,
                ExamName = e.Name,
                Score = u.Score ?? 0,
                Status = u.Status,
                SortIndex = u.SortIndex,
                ExamStatus = u.ExamStatus == EExamStatus.Absent? EExamStatus.Complete: u.ExamStatus,
                IsSuccess = u.IsCheck ? u.IsSuccess : null,
                EndTime = e.EndTime,
                StartTime = e.StartTime,
                TimeSpan = e.TimeSpan,
                ExamType = e.ExamType,
                ExamId = e.Id,
                IsCheck = u.IsCheck,
                IsReExam = u.IsReExam,
                CanReExam = SqlFunc.Subqueryable<ExamUserExamItem>().Where(x => x.UserExamId == u.Id).Count() < e.Count
            });
            return queryable;
        }

        public async Task<List<ViewExamQuestionDto>> View(string id)
        {

            List<ViewExamQuestionDto> viewExamQuestionDtos = await GetViewExamQuestion(id, null);

            var examQuestionIds = viewExamQuestionDtos.Select(x => x.ExamQuestionId).ToList();
            var questionIds = viewExamQuestionDtos.Select(x => x.QuestionId).ToList();
            var userExamIds = new List<string>
            {
                id
            };
            //List<ViewQuestionAnswerDto> questionAnswers = await GetQuestionAnswers(id, questionIds);
            List<ViewQuestionOptionDto> questionOptions = await GetQuestionOptions(questionIds, userExamIds);
            List<QuestionKnowladgeDto> questionKnowladges = await GetQuestionKnowladges(examQuestionIds);
            List<QuestionSourcewareDto> sourcewares = await GetSourcewares(examQuestionIds);

            ResolveExamQuestions(viewExamQuestionDtos, questionOptions, questionKnowladges, sourcewares);

            return viewExamQuestionDtos;

        }



        public async Task<ViewExamQuestionDto> View(string id, string questionId)
        {
            List<ViewExamQuestionDto> viewExamQuestionDtos = await GetViewExamQuestion(id, questionId);

            var examQuestionIds = viewExamQuestionDtos.Select(x => x.ExamQuestionId).ToList();
            var questionIds = viewExamQuestionDtos.Select(x => x.QuestionId).ToList();
            var userExamIds = new List<string>
            {
                id
            };
            //List<ViewQuestionAnswerDto> questionAnswers = await GetQuestionAnswers(id, questionIds);
            List<ViewQuestionOptionDto> questionOptions = await GetQuestionOptions(questionIds, userExamIds);
            List<QuestionKnowladgeDto> questionKnowladges = await GetQuestionKnowladges(examQuestionIds);
            List<QuestionSourcewareDto> sourcewares = await GetSourcewares(examQuestionIds);

            ResolveExamQuestions(viewExamQuestionDtos, questionOptions, questionKnowladges, sourcewares);

            return viewExamQuestionDtos.FirstOrDefault();
        }

        private static void ResolveExamQuestions(List<ViewExamQuestionDto> viewExamQuestionDtos, List<ViewQuestionOptionDto> questionOptions, List<QuestionKnowladgeDto> questionKnowladges, List<QuestionSourcewareDto> sourcewares)
        {
            viewExamQuestionDtos.ForEach(item =>
            {
                item.QuestionKnowladgeDtos = questionKnowladges.Where(x => x.QuestionId == item.ExamQuestionId).ToList();
                item.QuestionSourcewareDtos = sourcewares.Where(x => x.QuestionId == item.ExamQuestionId).ToList();
                if (item.QuestionType.CheckSelectType())
                {
                    item.QuestionOptions = questionOptions.Where(x => x.QuestionId == item.ExamQuestionId).ToList();

                    item.CorrectAnswer = string.Join(",", item.QuestionOptions.Where(m => m.IsAnswer).Select(m => m.Label).ToArray());
                }

            });
        }

        private async Task<List<QuestionSourcewareDto>> GetSourcewares(List<string> questionIds)
        {
            var questionSourcewareTable = new ExamRepository<ExamQuestionSourcewareBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
            var sourcewareTable = new ExamRepository<ExamSourceware>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();

            var querable = questionSourcewareTable.InnerJoin<ExamQuestionSourceware>((qsb, qs) => qsb.SourcewareId == qs.Id).InnerJoin(sourcewareTable, (qsb, qs, s) => qs.SourcewareId == s.Id).Select((qsb, qs, s) => new QuestionSourcewareDto
            {
                Id = qs.Id,
                Name = s.Name,
                QuestionId = qsb.ExamQuestionId,
                SourcewareId = s.Id,
                AttachmentId = s.AttachmentId
            }).MergeTable().Where(x => questionIds.Contains(x.QuestionId));

            return await querable.ToListAsync();
        }

        private async Task<List<QuestionKnowladgeDto>> GetQuestionKnowladges(List<string> questionIds)
        {
            var questionKnowladgeTable = new ExamRepository<ExamQuestionKnowladgeBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();


            return await questionKnowladgeTable.Where(x => questionIds.Contains(x.ExamQuestionId))
                .InnerJoin<ExamQuestionKnowladge>((kb, k) => kb.KnowladgeId == k.Id).Select((kb, k) => new QuestionKnowladgeDto
                {
                    Id = kb.Id,
                    QuestionId = kb.ExamQuestionId,
                    KnowladgeId = k.KnowladgeId,
                    Title = kb.Title
                }).ToListAsync();
        }

        private async Task<List<ViewQuestionOptionDto>> GetQuestionOptions(List<string> questionIds, List<string> userExamIds)
        {
            var selectQuestionOptions = GetQuestionOptionBaks(questionIds, userExamIds, false).MergeTable();

            var userExamItemOptionTable = _userExamItemOptionRepository.Queryable();

            var querable = selectQuestionOptions
                .LeftJoin(userExamItemOptionTable, (op, uio) => op.Id == uio.QuestionOptionId)
                .Select((op, uio) => new ViewQuestionOptionDto
                {
                    Content = op.Content,
                    Id = op.Id,
                    IsAnswer = op.IsAnswer,
                    Label = op.Label,
                    QuestionId = op.ExamQuestionId,
                    IsSelected = uio.Id != null
                }).MergeTable().OrderBy(x => x.Label);

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

        private async Task<List<ViewQuestionAnswerDto>> GetQuestionAnswers(string id, List<string> questionIds)
        {
            var userExamTable = _repository.Queryable().Where(x => x.Id == id);
            var userExamItemTable = _userExamItemRepository.Queryable();
            var examAnswerTable = _examAnswerRepository.Queryable();
            var questionTable = new ExamRepository<ExamQuestionBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
            var questionAnswerTable = new ExamRepository<ExamQuestionAnswerBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();

            var querable = examAnswerTable
                .InnerJoin(userExamItemTable, (a, ui) => ui.Id == a.UserExamItemId && ui.UserExamId == id)
                .LeftJoin(questionAnswerTable, (a, ui, qab) => qab.QuestionId == ui.QuestionId)
                .LeftJoin<ExamQuestionAnswer>((a, ui, qab, qa) => qab.QuestionAnswerId == qa.Id)
                .LeftJoin(questionTable, (a, ui, qab, qa, q) => qab.QuestionId == q.QuestionId && questionIds.Contains(q.Id))
                .Select((a, ui, qab, qa, q) => new ViewQuestionAnswerDto
                {
                    Answer = a.Answer,
                    CorrectAnswer = qa.Answer != null ? qa.Answer : string.Empty,
                    QuestionId = q.Id
                }).MergeTable();

            return await querable.ToListAsync();
        }

        private async Task<List<ViewExamQuestionDto>> GetViewExamQuestion(string id, string questionId)
        {
            var expression = ExpressionableUtility.CreateExpression<ExamUserExam>()
                .And(x => x.Id == id).ToExpression();

            ISugarQueryable<GradingExamQuestionTempDto> queryable = GetViewExamQuestionTempDtos(expression);

            queryable = queryable.MergeTable().WhereIF(!string.IsNullOrEmpty(questionId), x => x.QuestionId == questionId);

            var queryResult = await queryable.ToListAsync();

            var viewExamQuestion = queryResult.GroupBy(x => new
            {
                Id = x.Id,
                QuestionType = x.QuestionType
            }).Select(g => new ViewExamQuestionDto
            {
                Answer = g.FirstOrDefault().Answer,
                QuestionType = g.Key.QuestionType,
                QuestionId = g.FirstOrDefault().QuestionId,
                ExamQuestionId = g.FirstOrDefault().ExamQuestionId,
                Id = g.Key.Id,
                Score = g.FirstOrDefault().QuestionScore,
                RealScore = g.FirstOrDefault().Score,
                Title = g.FirstOrDefault().Title,
                CorrectAnswer = g.Key.QuestionType.CheckSelectType() ? string.Empty : g.FirstOrDefault()?.CorrectAnswer
            }).ToList();

            return viewExamQuestion;
        }
        #endregion

    }
}
