using DocumentFormat.OpenXml.Office2010.Excel;
using Exam.Infrastructure.Data.Entity;
using Exam.Infrastructure.Enums;
using Exam.Infrastructure.Extensions;
using Exam.Share;
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.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.Trains;
using Hotline.Repository.SqlSugar.Exam.Repositories;
using Hotline.Repository.SqlSugar.Exam.Service;
using Hotline.Share.Dtos.Trains;
using Hotline.Share.Requests.Train;
using Hotline.Users;
using JiebaNet.Segmenter.Common;
using MapsterMapper;
using XF.Domain.Authentications;
using XF.Domain.Dependency;

namespace Hotline.Application.Exam.Service.Trains
{
    public class TrainPlanService : ApiService<ExamTrainPlan, AddTrainPlanDto, UpdateTrainPlanDto, HotlineDbContext>, ITrainPlanService, IScopeDependency
    {
        private readonly ITrainPlanRepository _repository;
        private readonly ITrainPlanTemplateRepository _trainPlanTemplateRepository;
        private readonly ITrainRecordRepository _trainRecordRepository;
        private readonly IDataPermissionFilterBuilder _dataPermissionFilterBuilder;
        private readonly IServiceProvider _serviceProvider;
        private readonly ISessionContext _sessionContext;
        private readonly IMapper _mapper;
        private AddTrainPlanDto _addTrainPlanDto;

        public TrainPlanService(ITrainPlanRepository repository,
            ITrainPlanTemplateRepository trainPlanTemplateRepository,
            ITrainRecordRepository trainRecordRepository,
            IDataPermissionFilterBuilder dataPermissionFilterBuilder,
            IServiceProvider serviceProvider,
            ISessionContext sessionContext,
            IMapper mapper) : base(repository, mapper, sessionContext)
        {
            this._repository = repository;
            this._trainPlanTemplateRepository = trainPlanTemplateRepository;
            this._trainRecordRepository = trainRecordRepository;
            this._dataPermissionFilterBuilder = dataPermissionFilterBuilder;
            this._serviceProvider = serviceProvider;
            this._sessionContext = sessionContext;
            this._mapper = mapper;
        }

        #region public mehtod
        public async Task<TrainPlanDto> GetAsync(EntityQueryRequest entityQueryRequest)
        {

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

            var trainPlanDto = _mapper.Map<TrainPlanDto>(trainPlan);

            trainPlanDto.TrainPlanTemplateDtos = await GetTrainPlanTemplates(entityQueryRequest);

            trainPlanDto.TrainRecordDtos = await GetTrainRecords(entityQueryRequest);

            return trainPlanDto;
        }

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

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

            return (total, items);
        }

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

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

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

        }

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

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

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

            ResolveTemplateId(actionRequest, id);

            _addTrainPlanDto = _mapper.Map<AddTrainPlanDto>(actionRequest);

            base.Entity.TrainPlanTemplates=  await AddTrainPlanTemplatesAsync(actionRequest, cancellationToken);

            base.Entity.TrainRecords = await AddTrainRecordsAsync(actionRequest, cancellationToken);

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

            return id;

        }

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

            await base.UpdateAsync(actionRequest, cancellationToken);

            _addTrainPlanDto = _mapper.Map<AddTrainPlanDto>(actionRequest);

            ResolveTemplateId(actionRequest);

            base.Entity.TrainPlanTemplates = await ModifyTrainPlanTemplatesAsync(actionRequest, cancellationToken);

            base.Entity.TrainRecords = await ModifyTrainRecordsAsync(actionRequest, cancellationToken);

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



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

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

            await DeleteTrainPlanTemplateAsync(tmpEntityQueryRequest, cancellationToken);

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

            await DeleteTrainRecordsAsync(tmpEntityQueryRequest, cancellationToken);

        }
        #endregion

        #region
        private async Task<List<TrainPlanTemplateDto>> GetTrainPlanTemplates(EntityQueryRequest entityQueryRequest)
        {
            var trainPlanTemplateTable = _trainPlanTemplateRepository.Queryable().Where(x => x.TrainPlanId == entityQueryRequest.Id);

            var queryResult = trainPlanTemplateTable.Select(t => new TrainPlanTemplateDto
            {
                TrainPlanId = t.TrainPlanId,
                TrainTemplateId = t.TrainTemplateId,
            }); 

            return await queryResult.ToListAsync();

        }

        private void ResolveTemplateId(AddTrainPlanDto actionRequest, string id)
        {
            if (actionRequest.TrainPlanTemplateDtos != null)
            {
                actionRequest.TrainPlanTemplateDtos.ForEach(x => x.TrainPlanId = id);
            }

            if (actionRequest.TrainRecordDtos != null)
            {
                actionRequest.TrainRecordDtos.ForEach(x => x.TrainPlanId = id);
            }
        }

        private void ResolveTemplateId(UpdateTrainPlanDto actionRequest)
        {
            if (actionRequest.TrainPlanTemplateDtos != null)
            {
                actionRequest.TrainPlanTemplateDtos.ForEach(x => x.TrainPlanId = actionRequest.Id);
            }

            if (actionRequest.TrainRecordDtos != null)
            {
                actionRequest.TrainRecordDtos.ForEach(x => x.TrainPlanId = actionRequest.Id);
            }

            if (_addTrainPlanDto.TrainPlanTemplateDtos != null)
            {
                _addTrainPlanDto.TrainPlanTemplateDtos.ForEach(x => x.TrainPlanId = actionRequest.Id);
            }

            if (_addTrainPlanDto.TrainRecordDtos != null)
            {
                _addTrainPlanDto.TrainRecordDtos.ForEach(x => x.TrainPlanId = actionRequest.Id);
            }
        }

        private async Task<List<TrainRecordDto>> GetTrainRecords(EntityQueryRequest entityQueryRequest)
        {
           
            var trainPracticeTable = _trainRecordRepository.Queryable().Where(x => x.TrainPlanId == entityQueryRequest.Id);
            var userTable = new ExamRepository<User>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();

            var trainPractices = trainPracticeTable.InnerJoin(userTable,(t,u)=>t.UserId == u.Id).Select((t, u) => new TrainRecordDto
            {
                Id = t.Id,
                TrainPlanId = t.TrainPlanId,
                UserId = t.UserId,
                Name = u.Name,
                fullOrgName = u.FullOrgName
            });

            return await trainPractices.ToListAsync();
        }

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

            var trainPlanTemplateExpression = queryRequest.GetTemplateExpression();
            var trainPlanTemplateTable = _trainPlanTemplateRepository.Queryable().Where(trainPlanTemplateExpression);
            

            var queryResult = queryable.InnerJoin(trainPlanTemplateTable,(q,t)=>q.Id == t.TrainPlanId).Select((q,t) => new TrainPlanViewResponse
            {
                Id = q.Id,
                Code = q.Code,
                Name = q.Name,
                TrainStartTime = q.TrainStartTime,
                TrainEndTime = q.TrainEndTime,
                SortIndex = q.SortIndex,
                Status = q.Status,
                CreationTime = q.CreationTime,
                CreatorName = q.CreatorName,
                CreatorOrgName = q.CreatorOrgName
            }).Distinct();
            return queryResult;
        }

        private async Task<List<ExamTrainPlanTemplate>> AddTrainPlanTemplatesAsync(AddTrainPlanDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.TrainPlanTemplateDtos != null)
            {
                actionRequest.TrainPlanTemplateDtos.ResolveOperationStatus();

                var trainPlanTemplateDtos = actionRequest.TrainPlanTemplateDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();

                var trainPlanTemplates = new List<ExamTrainPlanTemplate>();

                trainPlanTemplateDtos.ForEach(item =>
                {
                    trainPlanTemplates.Add(_mapper.Map<ExamTrainPlanTemplate>(item));
                });

                trainPlanTemplates.ToInsert(_sessionContext);

                await _trainPlanTemplateRepository.ValidateAddAsync(trainPlanTemplates, cancellationToken);

                return trainPlanTemplates;
            }

            return null;
        }

        private async Task<List<ExamTrainRecord>> AddTrainRecordsAsync(AddTrainPlanDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.TrainRecordDtos != null)
            {
                actionRequest.TrainRecordDtos.ResolveOperationStatus();

                var trainPracticeDtos = actionRequest.TrainRecordDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();
                var trainPractices = new List<ExamTrainRecord>();

                trainPracticeDtos.ForEach(item =>
                {
                    trainPractices.Add(_mapper.Map<ExamTrainRecord>(item));
                });

                trainPractices.ToInsert(_sessionContext);

                await _trainRecordRepository.ValidateAddAsync(trainPractices, cancellationToken);

                return trainPractices;
            }

            return null;
        }

        private async Task<List<ExamTrainPlanTemplate>> ModifyTrainPlanTemplatesAsync(UpdateTrainPlanDto actionRequest, CancellationToken cancellationToken)
        {
            var trainPlanTemplates = new List<ExamTrainPlanTemplate>();

            var all = await _trainPlanTemplateRepository.Queryable().Where(x => x.TrainPlanId == actionRequest.Id).ToListAsync();

            trainPlanTemplates.AddRangeExt(await AddTrainPlanTemplatesAsync(_addTrainPlanDto, cancellationToken));

            trainPlanTemplates.AddRangeExt(await UpdateTrainPlanTemplatesAsync(actionRequest, cancellationToken));

            if (actionRequest.TrainPlanTemplateDtos != null)
            {
                actionRequest.TrainPlanTemplateDtos.ResolveOperationStatus(all);

                var ids = actionRequest.TrainPlanTemplateDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete).Select(x => x.Id).ToList();

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

                    await DeleteTrainPlanTemplateAsync(entityQueryRequest, cancellationToken);
                }
            }

            return trainPlanTemplates;

        }

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

        private async Task<List<ExamTrainPlanTemplate>> UpdateTrainPlanTemplatesAsync(UpdateTrainPlanDto actionRequest, CancellationToken cancellationToken)
        {
            if (actionRequest.TrainPlanTemplateDtos == null) return null;

            var trainPlanTemplateDtos = actionRequest.TrainPlanTemplateDtos.Where(x => x.OperationStatus == EEOperationStatus.Update).ToList();

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

            var trainPlanTemplates = await _trainPlanTemplateRepository.Queryable().Where(x => ids.Contains(x.Id)).ToListAsync();

            trainPlanTemplates = _mapper.Map<List<UpdateTrainPlanTemplateDto>, List<ExamTrainPlanTemplate>>(trainPlanTemplateDtos, trainPlanTemplates);

            trainPlanTemplates.ToUpdate(_sessionContext);

            await _trainPlanTemplateRepository.ValidateUpdateAsync(trainPlanTemplates, cancellationToken);

            return trainPlanTemplates;
        }

        private async Task<List<ExamTrainRecord>> ModifyTrainRecordsAsync(UpdateTrainPlanDto actionRequest, CancellationToken cancellationToken)
        {
            var trainRecords = new List<ExamTrainRecord>();

            var all =  await _trainRecordRepository.Queryable().Where(x => x.TrainPlanId == actionRequest.Id).ToListAsync();

            trainRecords.AddRangeExt(await AddTrainRecordsAsync(_addTrainPlanDto, cancellationToken));

            trainRecords.AddRangeExt(await UpdateTrainRecordsAsync(actionRequest, cancellationToken));

            if (actionRequest.TrainRecordDtos != null)
            {

                actionRequest.TrainRecordDtos.ResolveOperationStatus(all);

                var ids = actionRequest.TrainRecordDtos.Where(x => x.OperationStatus == EEOperationStatus.Delete).Select(x => x.Id).ToList();

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

                    await DeleteTrainRecordsAsync(entityQueryRequest, cancellationToken);
                }
              
            }

            return trainRecords;
        }

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

        private async Task<List<ExamTrainRecord>> UpdateTrainRecordsAsync(UpdateTrainPlanDto actionRequest, CancellationToken cancellationToken)
        {

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

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

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

            var trainRecords = await _trainRecordRepository.Queryable().Where(x => ids.Contains(x.Id)).ToListAsync();

            trainRecords = _mapper.Map<List<UpdateTrainRecordDto>, List<ExamTrainRecord>>(trainPracticeDtos, trainRecords);

            trainRecords.ToUpdate(_sessionContext);

            await _trainRecordRepository.ValidateUpdateAsync(trainRecords, cancellationToken);

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


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