using Exam.Infrastructure.Data.Entity;
using Exam.Infrastructure.Data.Interface;
using FluentValidation;
using Hotline.Repository.SqlSugar.Exam.Core.Constants;
using Hotline.Repository.SqlSugar.Exam.Extensions;
using Hotline.Repository.SqlSugar.Exam.Interface;
using Mapster;
using MapsterMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SqlSugar;
using System.Reflection;
using XF.Domain.Authentications;
using XF.Domain.Entities;
using XF.Domain.Repository;

namespace Hotline.Repository.SqlSugar.Exam.Service
{
    public class ApiService<T, TAdd, TUpdate, TDBContext> : IApiService<TAdd, TUpdate, T>
        where TAdd : IAddRequest
        where TUpdate : IActionRequest
        where T : class, IEntity<string>, IHasCreationTime, IDataPermission, new()
        where TDBContext : SugarUnitOfWork, new()
    {
        #region ctor
        private readonly IExamRepository<T, TDBContext> _repository;

        private AbstractValidator<T> _validator;

        private bool _isStartTrain = false;

        private IMapper _mapper;
        private readonly ISessionContext _sessionContext;

        protected readonly ISugarUnitOfWork<TDBContext> _uow;

        public T Entity { get; set; }

        public ApiService(IExamRepository<T, TDBContext> repository, IMapper mapper, ISessionContext sessionContext = null)
        {
            _repository = repository;
            _validator = _repository.Validator;
            _mapper = mapper;
            _sessionContext = sessionContext;
            _uow = _repository.UOW;
        }
        #endregion

        #region public method

        public void StartTran()
        {
            _isStartTrain = true;
        }

        public InsertNavTaskInit<T, T> AddNav(T entity)
        {
            return _repository.AddNav(entity);
        }

        public UpdateNavTaskInit<T, T> UpdateNav(T entity)
        {
            return _repository.UpdateNav(entity);
        }

        public DeleteNavTaskInit<T, T> RemoveNav(T entity)
        {
            return _repository.RemoveNav(entity);
        }

        /// <summary>
        /// TODO:暂时只能支持一级映射
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="operationConstant"></param>
        /// <returns></returns>
        public async Task Complete(T entity, string operationConstant)
        {
            if (_isStartTrain)
            {
                switch (operationConstant)
                {
                    case OperationConstant.Create:
                        await CompleteAdd(entity);
                        break;
                    case OperationConstant.Update:
                        await CompleteUpdate(entity);
                        break;
                    case OperationConstant.Delete:
                        await CompleteDelete(entity);
                        break;
                    default:
                        await CompleteAdd(entity);
                        break;
                }

            }
        }

        protected virtual async Task CompleteDelete(T entity)
        {
            var result = _repository.RemoveNav(entity);
            var memberInfos = typeof(T).GetMembers().Where(x => x.GetCustomAttribute<Navigate>() != null);

            DeleteNavMethodInfo deleteNavMethodInfo = null;

            foreach (var item in memberInfos)
            {
                var attribute = item.GetCustomAttribute<Navigate>();

                if (attribute == null) continue;

                if (deleteNavMethodInfo == null)
                {
                    deleteNavMethodInfo = result.IncludeByNameString(item.Name);
                }
                else
                {
                    deleteNavMethodInfo.IncludeByNameString(item.Name);
                }

            }

            if (deleteNavMethodInfo == null) return;

            await deleteNavMethodInfo.ExecuteCommandAsync();
        }

        protected virtual async Task CompleteUpdate(T entity)
        {
            var result = _repository.UpdateNav(entity);
            var memberInfos = typeof(T).GetMembers().Where(x => x.GetCustomAttribute<Navigate>() != null);

            UpdateNavMethodInfo updateNavMethodInfo = null;

            foreach (var item in memberInfos)
            {
                var attribute = item.GetCustomAttribute<Navigate>();

                if (attribute == null) continue;

                if (updateNavMethodInfo == null)
                {
                    updateNavMethodInfo = result.IncludeByNameString(item.Name, new UpdateNavOptions
                    {
                        OneToManyInsertOrUpdate = true
                    });
                }
                else
                {
                    updateNavMethodInfo.IncludeByNameString(item.Name, new UpdateNavOptions
                    {
                        OneToManyInsertOrUpdate = true
                    });
                }

            }

            if (updateNavMethodInfo == null) return;

            await updateNavMethodInfo.ExecuteCommandAsync();

        }

        protected virtual async Task CompleteAdd(T entity)
        {
            var result = _repository.AddNav(entity);
            var memberInfos = typeof(T).GetMembers().Where(x => x.GetCustomAttribute<Navigate>() != null);

            InsertNavMethodInfo insertNavMethodInfo = null;

            foreach (var item in memberInfos)
            {
                var attribute = item.GetCustomAttribute<Navigate>();

                if (attribute == null) continue;

                if (insertNavMethodInfo == null)
                {
                    insertNavMethodInfo = result.IncludeByNameString(item.Name);
                }
                else
                {
                    insertNavMethodInfo.IncludeByNameString(item.Name);
                }

            }

            if (insertNavMethodInfo == null) return;

            await insertNavMethodInfo.ExecuteCommandAsync();
        }

        /// <summary>
        /// 单表新增
        /// </summary>
        /// <param name="actionRequest"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<string> AddAsync(TAdd actionRequest, CancellationToken cancellationToken)
        {
            var entity = _mapper.Map<T>(actionRequest);

            entity.ToInsert();

            if (_isStartTrain)
            {
                await _repository.ValidateAddAsync(entity, cancellationToken);

                Entity = entity;

                return entity.Id;
            }

            return await _repository.AddWithValidateAsync(entity, cancellationToken);
        }

        /// <summary>
        /// 单表批量新增
        /// </summary>
        /// <param name="actionRequests"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task AddAsync(List<TAdd> actionRequests, CancellationToken cancellationToken)
        {
            var entities = _mapper.Map<List<T>>(actionRequests);

            entities.ToInsert(_sessionContext);

            if (_isStartTrain)
            {
                await _repository.ValidateAddAsync(entities, cancellationToken);
            }
            else
            {
                await _repository.AddWithValidateAsync(entities, cancellationToken);
            }
        }

        /// <summary>
        /// 单表删除
        /// </summary>
        /// <param name="entityQueryRequest"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task DeleteAsync(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
        {
            if (_isStartTrain)
            {
                await _repository.ValidateDeleteAsync(entityQueryRequest, cancellationToken);
            }
            else
            {
                await _repository.DeleteWithValidateAsync(entityQueryRequest, cancellationToken);
            }
        }

        /// <summary>
        /// 单表修改
        /// </summary>
        /// <param name="actionRequest"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task UpdateAsync(TUpdate actionRequest, CancellationToken cancellationToken)
        {
            var entity = await _repository.GetAsync(actionRequest.Id);

            entity = _mapper.Map(actionRequest, entity);

            entity.ToUpdate(_sessionContext);

            if (_isStartTrain)
            {
                await _repository.ValidateUpdateAsync(entity, cancellationToken);

                Entity = entity;
            }
            else
            {
                await _repository.UpdateWithValidateAsync(entity, cancellationToken);
            }
        }

        /// <summary>
        /// 单表修改
        /// </summary>
        /// <param name="actionRequests"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task UpdateAsync(List<TUpdate> actionRequests, CancellationToken cancellationToken)
        {
            var ids = actionRequests.Select(x => x.Id).ToList();
            var entities = await _repository.QueryAsync(x => ids.Contains(x.Id));

            entities = _mapper.Map(actionRequests, entities);

            entities.ToUpdate(_sessionContext);
            if (_isStartTrain)
            {
                await _repository.ValidateUpdateAsync(entities, cancellationToken);
            }
            else
            {
                await _repository.UpdateWithValidateAsync(entities, cancellationToken);
            }
        }

        /// <summary>
        /// 修改状态
        /// </summary>
        /// <param name="statusActionRequests"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task UpdateStatus(List<StatusActionRequest> statusActionRequests, CancellationToken cancellationToken)
        {
            var ids = statusActionRequests.Select(x => x.Id).ToList();
            var entities = await _repository.QueryAsync(x => ids.Contains(x.Id));

            entities.ForEach(item =>
            {
                var statusActionRequest = statusActionRequests.FirstOrDefault(x => x.Id == item.Id);

                item = _mapper.Map<StatusActionRequest, T>(statusActionRequest, item);
            });

            entities.ToUpdate(_sessionContext);

            if (_isStartTrain)
            {
                await _repository.ValidateUpdateAsync(entities, cancellationToken);
            }
            else
            {
                await _repository.UpdateWithValidateAsync(entities, cancellationToken);
            }
        }
        #endregion
    }
}
