using Exam.Infrastructure.Data.Entity;
using Exam.Infrastructure.Enums;
using Exam.Infrastructure.Extensions;
using Exam.Share.ViewResponses.Train;
using Hotline.Application.Exam.Core.Constants;
using Hotline.Application.Exam.Core.Extensions;
using Hotline.Application.Exam.Core.Utilities;
using Hotline.Application.Exam.Extensions;
using Hotline.Application.Exam.Interface.Train;
using Hotline.Application.Exam.QueryExtensions.Trains;
using Hotline.Exams.Questions;
using Hotline.Exams.Trains;
using Hotline.Repository.SqlSugar;
using Hotline.Repository.SqlSugar.DataPermissions;
using Hotline.Repository.SqlSugar.Exam.Core.Constants;
using Hotline.Repository.SqlSugar.Exam.Extensions;
using Hotline.Repository.SqlSugar.Exam.Interfaces.Questions;
using Hotline.Repository.SqlSugar.Exam.Interfaces.Trains;
using Hotline.Repository.SqlSugar.Exam.Repositories;
using Hotline.Repository.SqlSugar.Exam.Service;
using Hotline.Share.Dtos.Trains;
using Hotline.Share.Requests.Train;
using JiebaNet.Segmenter.Common;
using MapsterMapper;
using SqlSugar;
using XF.Domain.Authentications;
using XF.Domain.Dependency;

namespace Hotline.Application.Exam.Service.Trains
{
    public class TrainTemplateService : ApiService<ExamTrainTemplate, AddTrainTemplateDto, UpdateTrainTemplateDto, HotlineDbContext>, ITrainTemplateService, IScopeDependency
    {
        private readonly ITrainTemplateRepository _repository;
        private readonly ITrainKnowladgeRepository _trainKnowladgeRepository;
        private readonly ITrainPracticeRepository _trainPracticeRepository;
        private readonly ITrainPracticeOptionsRepository _trainPracticeOptionsRepository;
        private readonly ITrainPracticeSourcewareRepository _trainPracticeSourcewareRepository;
        private readonly ITrainPracticeKnowladgeRepository _trainPracticeKnowladgeRepository;
        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
        private readonly IServiceProvider _serviceProvider;
        private readonly ISessionContext _sessionContext;
        private readonly IMapper _mapper;
        private AddTrainTemplateDto _addTrainTemplateDto;

        public TrainTemplateService(ITrainTemplateRepository repository,
            ITrainKnowladgeRepository trainKnowladgeRepository,
            ITrainPracticeRepository trainPracticeRepository,
            ITrainPracticeOptionsRepository trainPracticeOptionsRepository,
            ITrainPracticeSourcewareRepository trainPracticeSourcewareRepository,
            ITrainPracticeKnowladgeRepository trainPracticeKnowladgeRepository,
            IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
            ISessionContext sessionContext,
            IMapper mapper) : base(repository, mapper)
        {
            _repository = repository;
            this._trainKnowladgeRepository = trainKnowladgeRepository;
            this._trainPracticeRepository = trainPracticeRepository;
            this._trainPracticeOptionsRepository = trainPracticeOptionsRepository;
            this._trainPracticeSourcewareRepository = trainPracticeSourcewareRepository;
            this._trainPracticeKnowladgeRepository = trainPracticeKnowladgeRepository;
            this._dataPermissionFilterBuilder = dataPermissionFilterBuilder;
            this._serviceProvider = serviceProvider;
            this._sessionContext = sessionContext;
            this._mapper = mapper;
        }

        #region public method
        public async Task<TrainTemplateDto> GetAsync(EntityQueryRequest entityQueryRequest)
        {

            var trainTemplate = await _repository.GetAsync(entityQueryRequest.Id);

            var trainTemplateDto = _mapper.Map<TrainTemplateDto>(trainTemplate);

            trainTemplateDto.TrainKnowladges = await GetKnowladges(entityQueryRequest);

            trainTemplateDto.TrainPracticeDtos = await GetTrainPractices(entityQueryRequest);

            return trainTemplateDto;
        }

        public async Task<(int, List<TrainTemplateViewResponse>)> GetListAsync(TrainTemplatePagedRequest queryRequest)
        {
            SqlSugar.ISugarQueryable<TrainTemplateViewResponse> queryResult = QueryRequest(queryRequest);

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

            return (total, items);
        }

        public async Task<PageViewResponse<TrainTemplateViewResponse>> GetPagedListAsync(TrainTemplatePagedRequest queryRequest)
        {
            SqlSugar.ISugarQueryable<TrainTemplateViewResponse> queryResult = QueryRequest(queryRequest);

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

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

        }

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

            actionRequest.Code = await GenerateCode(ExamBusiConstants.TrainTemplateCode,3);

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

            ResolveTemplateId(actionRequest, id);

            base.Entity.TrainKnowladges = await AddTrainKnowladgesAsync(actionRequest, cancellationToken);

            base.Entity.TrainPractices =  await AddTrainPracticesAsync(actionRequest, cancellationToken);

            var trainPracticeOptions = await AddTrainPracticeOptionsAsync(actionRequest, cancellationToken);


            var trainPracticeKnowladges =  await AddTrainPracticeKnowladgeAsync(actionRequest, cancellationToken);

            base.Entity.TrainPractices.ForEach(item =>
            {
                item.TrainPracticeKnowladges = trainPracticeKnowladges.Where(x => item.Id == x.TrainPracticeId).ToList();
            });

            var trainPracticeSourcewares = await AddTrainPracticeSourcewareAsync(actionRequest, cancellationToken);

            base.Entity.TrainPractices.ForEach(item =>
            {
                item.TrainPracticeKnowladges = trainPracticeKnowladges.Where(x => item.Id == x.TrainPracticeId).ToList();
                item.TrainPracticeOptionses = trainPracticeOptions.Where(x => item.Id == x.TrainPracticeId).ToList();
                item.TrainPracticeSourcewares = trainPracticeSourcewares.Where(x => item.Id == x.TrainPracticeId).ToList();
            });


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

            return id;

        }

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

            await base.UpdateAsync(actionRequest, cancellationToken);

            _addTrainTemplateDto = _mapper.Map<AddTrainTemplateDto>(actionRequest);

            base.Entity.TrainKnowladges = await ModifyTrainKnowladgeAsync(actionRequest, cancellationToken);

            base.Entity.TrainPractices = await ModifyTrainPracticesAsync(actionRequest, cancellationToken);

            var trainPracticeOptions = await ModifyTrainPracticeOptionsAsync(actionRequest, cancellationToken);

            var trainPracticeKnowladges = await ModifyTrainPracticeKnowladgeAsync(actionRequest, cancellationToken);

            var trainPracticeSourcewares = await ModifyTrainPracticeSourcewareAsync(actionRequest, cancellationToken);

            base.Entity.TrainPractices.ForEach(item =>
            {
                item.TrainPracticeOptionses = trainPracticeOptions.Where(x => item.Id == x.TrainPracticeId).ToList();          
                item.TrainPracticeKnowladges = trainPracticeKnowladges.Where(x => item.Id == x.TrainPracticeId).ToList();
                item.TrainPracticeSourcewares = trainPracticeSourcewares.Where(x => item.Id == x.TrainPracticeId).ToList();
            });

            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<ExamTrainKnowladge>()
               .AndIF(entityQueryRequest.Id.IsNotNullOrEmpty(), x => x.TrainTemplateId == entityQueryRequest.Id)
               .AndIF(entityQueryRequest.Ids.IsNotNullOrEmpty(), x => entityQueryRequest.Ids.Contains(x.TrainTemplateId))
               .ToEntityQueryRequest<ExamTrainKnowladge>();

            await DeleteTrainKnowladgesAsync(tmpEntityQueryRequest, cancellationToken);

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

            await DeleteTrainPracticeAsync(tmpEntityQueryRequest, cancellationToken);
        }
        #endregion

        #region private method
        private async Task<List<TrainKnowladgeDto>> GetKnowladges(EntityQueryRequest entityQueryRequest)
        {
            var trainKnowladgeTable = _trainKnowladgeRepository.Queryable().Where(x => x.TrainTemplateId == entityQueryRequest.Id);
            var knowledgeTable = new ExamRepository<KnowledgeBase.Knowledge>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();

            var queseryResult = trainKnowladgeTable.InnerJoin(knowledgeTable, (t, k) => t.KnowladgeId == k.Id).Select((t, k) => new TrainKnowladgeDto
            {
                Id = t.Id,
                KnowladgeId = t.KnowladgeId,
                Title = k.Title
            });

            return await queseryResult.ToListAsync();

        }

        private async Task<List<ExamTrainPracticeOptions>> ModifyTrainPracticeOptionsAsync(UpdateTrainTemplateDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.TrainPracticeDtos == null) return null;

            var questionIds = actionRequest.TrainPracticeDtos.Where(x => x.QuestionType.CheckSelectType()).Select(x => x.QuestionId).ToList();

            if (questionIds.IsNotNullOrEmpty())
            {
				var entityQueryRequest = new EntityQueryRequest
				{
					Expression = ExpressionableUtility.CreateExpression<ExamTrainPracticeOptions>()
							 .AndIF(questionIds.IsNotNullOrEmpty(), x => questionIds.Contains(x.QuestionId)).ToExpression()
				};

				await DeleteTrainPracticeOptionsAsync(entityQueryRequest, cancellationToken);
			}            

            return await AddTrainPracticeOptionsAsync(_addTrainTemplateDto, cancellationToken);
        }

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



        private async Task<List<ExamTrainPracticeSourceware>> AddTrainPracticeSourcewareAsync(AddTrainTemplateDto actionRequest, CancellationToken cancellationToken)
        {
            var questionSourcewareRepository = new ExamRepository<ExamQuestionSourceware>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var quesitonIds = actionRequest.TrainPracticeDtos.Select(x => x.QuestionId);

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

            var trainPracticeSourcewares = new List<ExamTrainPracticeSourceware>();

            actionRequest.TrainPracticeDtos.ToList().ForEach(x =>
            {
                var trainPractice = base.Entity?.TrainPractices.FirstOrDefault(n => n.QuestionId == x.QuestionId) ?? null;

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

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

                        trainPracticeSourceware.TrainPracticeId = trainPractice?.Id;

                        trainPracticeSourceware.ToInsert(_sessionContext);

                        trainPracticeSourcewares.Add(trainPracticeSourceware);
                    });
                }

            });

            await _trainPracticeSourcewareRepository.ValidateAddAsync(trainPracticeSourcewares, cancellationToken);

            return trainPracticeSourcewares;
        }

        private async Task<List<ExamTrainPracticeKnowladge>> AddTrainPracticeKnowladgeAsync(AddTrainTemplateDto actionRequest, CancellationToken cancellationToken)
        {
            var questionKnowladgeRepository = new ExamRepository<ExamQuestionKnowladge>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var quesitonIds = actionRequest.TrainPracticeDtos.Select(x => x.QuestionId);

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

            var trainPracticeKnowladges = new List<ExamTrainPracticeKnowladge>();

            actionRequest.TrainPracticeDtos.ToList().ForEach(x =>
            {
                var trainPractice = base.Entity?.TrainPractices.FirstOrDefault(n => n.QuestionId == x.QuestionId) ?? null;

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

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

                        trainPracticeKnowladge.TrainPracticeId = trainPractice?.Id;

                        trainPracticeKnowladge.ToInsert(_sessionContext);

                        trainPracticeKnowladges.Add(trainPracticeKnowladge);
                    });
                }

            });

            await _trainPracticeKnowladgeRepository.ValidateAddAsync(trainPracticeKnowladges, cancellationToken);

            return trainPracticeKnowladges;
        }

        private async Task<List<ExamTrainPracticeSourceware>> ModifyTrainPracticeSourcewareAsync(UpdateTrainTemplateDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.TrainPracticeDtos == null) return null;

            var questionIds = actionRequest.TrainPracticeDtos.Where(x => x.QuestionType.CheckSelectType()).Select(x => x.QuestionId).ToList();

            if (questionIds.IsNotNullOrEmpty())
            {
				var entityQueryRequest = new EntityQueryRequest
				{
					Expression = ExpressionableUtility.CreateExpression<ExamTrainPracticeSourceware>()
							.AndIF(questionIds.IsNotNullOrEmpty(), x => questionIds.Contains(x.QuestionId)).ToExpression()
				};

				await DeleteTrainPracticeSourcewareAsync(entityQueryRequest, cancellationToken);
			}           

            return await AddTrainPracticeSourcewareAsync(_addTrainTemplateDto, cancellationToken);
        }

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

        private async Task<List<ExamTrainPracticeKnowladge>> ModifyTrainPracticeKnowladgeAsync(UpdateTrainTemplateDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.TrainPracticeDtos == null) return null;

            var questionIds = actionRequest.TrainPracticeDtos.Where(x => x.QuestionType.CheckSelectType()).Select(x => x.QuestionId).ToList();

            if (questionIds.IsNotNullOrEmpty())
            {
				var entityQueryRequest = new EntityQueryRequest
				{
					Expression = ExpressionableUtility.CreateExpression<ExamTrainPracticeKnowladge>()
						  .AndIF(questionIds.IsNotNullOrEmpty(), x => questionIds.Contains(x.QuestionId)).ToExpression()
				};

				await DeleteTrainPracticeKnowladgeAsync(entityQueryRequest, cancellationToken);
			}        

            return await AddTrainPracticeKnowladgeAsync(_addTrainTemplateDto, cancellationToken);
        }

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

        private async Task<List<ExamTrainPracticeOptions>> AddTrainPracticeOptionsAsync(AddTrainTemplateDto actionRequest, CancellationToken cancellationToken)
        {
            var questionOptionRepository = new ExamRepository<ExamQuestionOptions>(_uow, _dataPermissionFilterBuilder, _serviceProvider);

            var quesitonIds = actionRequest.TrainPracticeDtos.Select(x => x.QuestionId);

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

            var trainPracticeOptions = new List<ExamTrainPracticeOptions>();

            actionRequest.TrainPracticeDtos.Where(x => x.QuestionType.CheckSelectType()).ToList().ForEach(x =>
            {
                var trainPractice = base.Entity?.TrainPractices.FirstOrDefault(n => n.QuestionId == x.QuestionId) ?? null;

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

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

                        trainPracticeOption.TrainPracticeId = trainPractice?.Id;

                        trainPracticeOption.QuestionOptionId = item.Id;

                        trainPracticeOption.ToInsert(_sessionContext);

                        trainPracticeOptions.Add(trainPracticeOption);
                    });
                }

            });

            await _trainPracticeOptionsRepository.ValidateAddAsync(trainPracticeOptions, cancellationToken);

            return trainPracticeOptions;
        }

        private void ResolveTemplateId(AddTrainTemplateDto actionRequest, string id)
        {
            if (actionRequest.TrainKnowladges != null)
            {
                actionRequest.TrainKnowladges.ForEach(x => x.TrainTemplateId = id);
            }

            if (actionRequest.TrainPracticeDtos != null)
            {
                actionRequest.TrainPracticeDtos.ForEach(x => x.TrainTemplateId = id);
            }
        }

        private async Task<List<TrainPracticeDto>> GetTrainPractices(EntityQueryRequest entityQueryRequest)
        {
            var questionTable = new ExamRepository<ExamQuestion>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
            var trainPracticeTable = _trainPracticeRepository.Queryable().Where(x => x.TrainTemplateId == entityQueryRequest.Id);

            var trainPractices = trainPracticeTable.InnerJoin(questionTable, (t, q) => t.QuestionId == q.Id).Select((t, q) => new TrainPracticeDto
            {
                Id = t.Id,
                QuestionId = t.QuestionId,
                QuestionType = q.QuestionType,
                DifficultyLevel = q.DifficultyLevel,                
                Title = q.Title
            });

            return await trainPractices.ToListAsync();
        }

        private SqlSugar.ISugarQueryable<TrainTemplateViewResponse> QueryRequest(TrainTemplatePagedRequest queryRequest)
        {
            var expression = queryRequest.GetExpression();
            var queryable = _repository.Queryable().Where(expression);

            var queryResult = queryable.Select(m => new TrainTemplateViewResponse
            {
                Id = m.Id,
                Code = m.Code,
                Name = m.Name,
                IsContainsPractice = m.IsContainsPractice,
                SortIndex = m.SortIndex,
                Status = m.Status,
                CreationTime = m.CreationTime,
                CreatorName = m.CreatorName,
                CreatorOrgName = m.CreatorOrgName
            });
            return queryResult;
        }

        private async Task<List<ExamTrainPractice>> AddTrainPracticesAsync(AddTrainTemplateDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.TrainPracticeDtos != null)
            {
                actionRequest.TrainPracticeDtos.ResolveOperationStatus();

                var trainPracticeDtos = actionRequest.TrainPracticeDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();

                var trainPractices = _mapper.Map<List<ExamTrainPractice>>(trainPracticeDtos);

                //TODO:排序
                var sortIndex = 0;
                trainPractices.ForEach(x =>
                {
                    x.SortIndex = sortIndex + 1;
                });

                trainPractices.ToInsert(_sessionContext);

                await _trainPracticeRepository.ValidateAddAsync(trainPractices, cancellationToken);

                return trainPractices;
            }

            return null;
        }

        private async Task<List<ExamTrainKnowladge>> AddTrainKnowladgesAsync(AddTrainTemplateDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.TrainKnowladges != null)
            {
                actionRequest.TrainKnowladges.ResolveOperationStatus();

                var trainKnowladgeDtos = actionRequest.TrainKnowladges.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();

                var trainKnowladges = _mapper.Map<List<ExamTrainKnowladge>>(trainKnowladgeDtos);

                trainKnowladges.ToInsert(_sessionContext);

                await _trainKnowladgeRepository.ValidateAddAsync(trainKnowladges, cancellationToken);

                return trainKnowladges;
            }
            return null;
        }

        private async Task<List<ExamTrainPractice>> ModifyTrainPracticesAsync(UpdateTrainTemplateDto actionRequest, CancellationToken cancellationToken)
        {
            var trainPractices = new List<ExamTrainPractice>();

            trainPractices.AddRangeExt(await AddTrainPracticesAsync(_addTrainTemplateDto, cancellationToken));

            trainPractices.AddRangeExt(await UpdateTrainPracticesAsync(actionRequest, cancellationToken));

            if (actionRequest.TrainPracticeDtos != null)
            {
                var ids = actionRequest.TrainPracticeDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete).Select(x => x.Id).ToList();

                if (ids.IsNotNullOrEmpty())
                {
					var entityQueryRequest = new EntityQueryRequest
					{
						Expression = ExpressionableUtility.CreateExpression<ExamTrainPractice>()
					.AndIF(ids.IsNotEmpty(), x => ids.Contains(x.Id))
					.ToExpression()
					};

					await DeleteTrainPracticeAsync(entityQueryRequest, cancellationToken);
				}                
            }

            return trainPractices;
        }

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

        private async Task<List<ExamTrainPractice>> UpdateTrainPracticesAsync(UpdateTrainTemplateDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.TrainPracticeDtos == null) return null;

            actionRequest.TrainPracticeDtos.ResolveOperationStatus();

            var trainPracticeDtos = actionRequest.TrainPracticeDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();

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

            var trainPractices = await _trainPracticeRepository.Queryable().Where(x => ids.Contains(x.Id)).ToListAsync();

            trainPractices = _mapper.Map<List<UpdateTrainPracticeDto>, List<ExamTrainPractice>>(trainPracticeDtos, trainPractices);

            trainPractices.ToUpdate(_sessionContext);

            await _trainPracticeRepository.ValidateUpdateAsync(trainPractices, cancellationToken);

            return trainPractices;
        }

        private async Task<List<ExamTrainKnowladge>> ModifyTrainKnowladgeAsync(UpdateTrainTemplateDto actionRequest, CancellationToken cancellationToken)
        {
            var trainKnowladges = new List<ExamTrainKnowladge>();

            trainKnowladges.AddRangeExt(await AddTrainKnowladgesAsync(_addTrainTemplateDto, cancellationToken));

            trainKnowladges.AddRangeExt(await UpdateTrainKnowladgesAsync(actionRequest, cancellationToken));

            if (actionRequest.TrainKnowladges != null)
            {
                var ids = actionRequest.TrainKnowladges.Where(x => x.OperationStatus == EEOperationStatus.Delete).Select(x => x.Id).ToList();

                if (ids.IsNotNullOrEmpty())
                {
					var entityQueryRequest = new EntityQueryRequest
					{
						Expression = ExpressionableUtility.CreateExpression<ExamTrainKnowladge>()
				   .AndIF(ids.IsNotEmpty(), x => ids.Contains(x.Id))
				   .ToExpression()
					};

					await DeleteTrainKnowladgesAsync(entityQueryRequest, cancellationToken);
				}
               
            }

            return trainKnowladges;
        }

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

        private async Task<List<ExamTrainKnowladge>> UpdateTrainKnowladgesAsync(UpdateTrainTemplateDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.TrainKnowladges == null) return null;

            actionRequest.TrainKnowladges.ResolveOperationStatus();

            var trainKnowladgeDtos = actionRequest.TrainKnowladges.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();

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

            var trainKnowladges = await _trainKnowladgeRepository.Queryable().Where(x => ids.Contains(x.Id)).ToListAsync();

            trainKnowladges = _mapper.Map<List<UpdateTrainKnowladgeDto>, List<ExamTrainKnowladge>>(trainKnowladgeDtos, trainKnowladges);

            trainKnowladges.ToUpdate(_sessionContext);

            await _trainKnowladgeRepository.ValidateUpdateAsync(trainKnowladges, cancellationToken);

            return trainKnowladges;
        }

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

            var code = string.Empty;
            if (trainTemplate != null)
            {
                code = trainTemplate.Code;
            }

            code = CodeUtility.GenerateCode(codePrefix,length,code);

            return code;
        }
        #endregion

        #region protected method
        protected override async Task CompleteAdd(ExamTrainTemplate entity)
        {
            await base.AddNav(entity)
                  .Include(x => x.TrainKnowladges)
                  .Include(x => x.TrainPractices)
                  .ThenInclude(x => x.TrainPracticeKnowladges)
                  .Include(x => x.TrainPractices, new InsertNavOptions
                  {
                      OneToManyIfExistsNoInsert = true
                  })
                  .ThenInclude(x => x.TrainPracticeOptionses)
                  .Include(x => x.TrainPractices, new InsertNavOptions
                  {
                      OneToManyIfExistsNoInsert = true
                  })
                  .ThenInclude(x => x.TrainPracticeSourcewares).ExecuteCommandAsync();
        }

        protected override async Task CompleteUpdate(ExamTrainTemplate entity)
        {
            await base.UpdateNav(entity)
                 .Include(x => x.TrainKnowladges)
                 .Include(x => x.TrainPractices)
                 .ThenInclude(x => x.TrainPracticeKnowladges)
                 .Include(x => x.TrainPractices, new UpdateNavOptions
                 {
                     OneToManyInsertOrUpdate = true
                 })
                 .ThenInclude(x => x.TrainPracticeOptionses)
                 .Include(x => x.TrainPractices, new UpdateNavOptions
                 {
                     OneToManyInsertOrUpdate = true
                 })
                 .ThenInclude(x => x.TrainPracticeSourcewares).ExecuteCommandAsync();
        }

        #endregion
    }
}
