using FluentValidation;
using Hotline.Authentications;
using Hotline.Share.Dtos.BatchTask;
using Hotline.Share.Enums.BatchTask;
using Hotline.Validators.BatchTask;
using MapsterMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using XF.Domain.Authentications;
using XF.Domain.Dependency;
using XF.Domain.Exceptions;
using XF.Domain.Repository;

namespace Hotline.BatchTask;

public class ApptaskDomainService : IApptaskDomainService, IScopeDependency
{
    private readonly IRepository<Apptask> _apptaskRepository;
    private readonly IRepository<ApptaskItem> _apptaskItemRepository;
    private readonly ISessionContextManager _sessionContextManager;
    private readonly IMapper _mapper;
    private readonly ILogger<ApptaskDomainService> _logger;

    public ApptaskDomainService(
        IRepository<Apptask> apptaskRepository,
        IRepository<ApptaskItem> apptaskItemRepository,
        ISessionContextManager sessionContextManager,
        IMapper mapper,
        ILogger<ApptaskDomainService> logger)
    {
        _apptaskRepository = apptaskRepository;
        _apptaskItemRepository = apptaskItemRepository;
        _sessionContextManager = sessionContextManager;
        _mapper = mapper;
        _logger = logger;
    }

    /// <summary>
    /// 新增任务
    /// </summary>
    public async Task<string> AddAsync(AddApptaskRequest request, CancellationToken cancellation)
    {
        var validator = new ApptaskValidator();
        var result = validator.Validate(request);
        if (!result.IsValid)
            throw new ValidationException(result.Errors.FirstOrDefault()?.ErrorMessage);

        var priority = 9;
        if (request.Priority.HasValue && request.Priority.Value >= 0 && request.Priority.Value < 9)
            priority = request.Priority.Value;
        var apptask = new Apptask
        {
            Name = request.Name,
            Description = request.Description,
            TaskType = request.TaskType,
            ApptaskItems = request.ApptaskItems.Select(d => new ApptaskItem
            {
                BusinessId = d.BusinessId,
                TaskType = request.TaskType,
                TaskStatus = ETaskStatus.Waiting,
                TaskParams = d.TaskParams is null ? null : System.Text.Json.JsonSerializer.Serialize(d.TaskParams),
                TryLimit = request.TryLimit,
                Priority = priority
            }).ToList()
        };

        if (string.IsNullOrEmpty(apptask.Name))
            apptask.CreateName();

        await _apptaskRepository.AddNav(apptask)
            .Include(d => d.ApptaskItems)
            .ExecuteCommandAsync();

        return apptask.Id;
    }

    /// <summary>
    /// 查询任务进度
    /// </summary>
    /// <returns></returns>
    public async Task<ApptaskProgressDto> GetProgressAsync(string taskId, CancellationToken cancellation)
    {
        var apptask = await _apptaskRepository.Queryable()
            .Includes(d => d.ApptaskItems)
            .FirstAsync(d => d.Id == taskId, cancellation);
        if (apptask is null)
            throw new UserFriendlyException("无效任务编号");
        return _mapper.Map<ApptaskProgressDto>(apptask);
    }

    /// <summary>
    /// 查询当前任务是否全部执行完成
    /// </summary>
    /// <param name="taskId"></param>
    /// <param name="cancellation"></param>
    /// <returns></returns>
    public async Task<bool> IsCompletedAsync(string taskId, CancellationToken cancellation)
    {
        var anyUnCompleted = await _apptaskItemRepository.Queryable()
            .AnyAsync(d => d.Tries < d.TryLimit
                           && (d.TaskStatus == ETaskStatus.Waiting || d.TaskStatus == ETaskStatus.Processing)
                           && d.ApptaskId == taskId, cancellation);
        return !anyUnCompleted;
    }

    /// <summary>
    /// 终止任务
    /// </summary>
    /// <param name="taskId"></param>
    /// <param name="cancellation"></param>
    /// <returns></returns>
    public async Task TerminalTaskAsync(string taskId, CancellationToken cancellation)
    {
        var apptask = await _apptaskRepository.Queryable()
            .Includes(d => d.ApptaskItems.Where(x => x.Tries < x.TryLimit
                                                   && (x.TaskStatus == ETaskStatus.Waiting || x.TaskStatus == ETaskStatus.Failed)).ToList())
            .FirstAsync(d => d.Id == taskId, cancellation);
        if (apptask is null)
            throw new UserFriendlyException("无效任务编号");
        if (apptask.ApptaskItems.Count == 0) return;
        var Succeed = 0;
        foreach (var item in apptask.ApptaskItems)
        {
            item.TaskStatus = ETaskStatus.Terminated;
            var result = await _apptaskItemRepository.Updateable(item).ExecuteCommandWithOptLockAsync();
            if (result != 0) Succeed++;
        }

        if (Succeed != apptask.ApptaskItems.Count)
            await TerminalTaskAsync(taskId, cancellation);
    }

    /// <summary>
    /// 获取一个待执行的任务
    /// </summary>
    /// <param name="cancellation"></param>
    /// <returns></returns>
    public async Task<ApptaskItem?> GetWaitingTaskAsync(CancellationToken cancellation)
    {
        var taskItems = await _apptaskItemRepository.Queryable()
            .Where(d => d.Tries < d.TryLimit
                        && (d.TaskStatus == ETaskStatus.Waiting || d.TaskStatus == ETaskStatus.Failed))
            .OrderBy(d => d.Priority)
            .OrderBy(d => d.CreationTime)
            .Take(10)
            .ToListAsync(cancellation);

        if (taskItems.Count == 0) return null;

        foreach (var item in taskItems)
        {
            item.SetProcessing();
            item.Tries++;
            var result = await _apptaskItemRepository.Updateable(item).ExecuteCommandWithOptLockAsync();
            if (result != 0)
                return item;
        }

        return await GetWaitingTaskAsync(cancellation);
    }

    /// <summary>
    /// 执行任务
    /// </summary>
    /// <param name="executor"></param>
    /// <param name="apptaskItem"></param>
    /// <param name="cancellation"></param>
    /// <returns></returns>
    public async Task<ApptaskExecuteResult> ExecuteAsync<TRequest>(IApptaskExecutor<TRequest> executor, ApptaskItem apptaskItem, CancellationToken cancellation)
    where TRequest : class, IApptaskRequest
    {
        try
        {
            if (string.IsNullOrEmpty(apptaskItem.TaskParams))
                throw new UserFriendlyException($"无效任务参数, taskItemId: {apptaskItem.Id}");

            var request = System.Text.Json.JsonSerializer.Deserialize<TRequest>(apptaskItem.TaskParams);
            if (request is null)
                throw new UserFriendlyException($"任务参数反序列化失败, taskItemId: {apptaskItem.Id}");

            _sessionContextManager.ChangeSessionContext(request.SessionContext);
            
            //if (request is IApptaskRequest reqWithSession)
            //    _sessionContextProvider.ChangeSession(reqWithSession.SessionContext);
            var result = await executor.ExecuteAsync(request, cancellation);
            apptaskItem.TaskStatus = result.IsSuccess ? ETaskStatus.Succeeded : ETaskStatus.Failed;
            apptaskItem.Message = result.Message;
            apptaskItem.TaskEndTime = DateTime.Now;
            return ApptaskExecuteResult.Success();
        }
        catch (Exception e)
        {
            _logger.LogError("批量任务执行异常：taskId: {taskItemId}, {err}", apptaskItem.Id, e.Message);
            apptaskItem.TaskStatus = ETaskStatus.Failed;
            apptaskItem.Message = "批量任务执行异常";
            apptaskItem.TaskEndTime = DateTime.Now;
            return ApptaskExecuteResult.Fail("批量任务执行异常");
        }
        finally
        {
            await _apptaskItemRepository.Updateable(apptaskItem)
                .UpdateColumns(d => new
                {
                    d.TaskStatus,
                    d.Message,
                    d.TaskEndTime
                })
                .ExecuteCommandAsync(cancellation);
        }
    }
}