using MediatR;
using Microsoft.EntityFrameworkCore;
using Workflow.Domain.Entities;
using Workflow.Domain.Enums;
using Workflow.Domain.Exceptions;
using Workflow.Infrastructure.Persistence;

using TaskStatus = Workflow.Domain.Enums.TaskStatus;

namespace Workflow.Application.Features.WorkflowInstances.Commands;

public record WithdrawWorkflowInstanceCommand(
    Guid InstanceId,
    Guid UserId
) : IRequest<Unit>;

public class WithdrawWorkflowInstanceCommandHandler(WorkflowDbContext db)
    : IRequestHandler<WithdrawWorkflowInstanceCommand, Unit>
{
    public async Task<Unit> Handle(WithdrawWorkflowInstanceCommand request, CancellationToken cancellationToken)
    {
        var instance = await db.WorkflowInstances.FindAsync([request.InstanceId], cancellationToken)
            ?? throw new NotFoundException($"Workflow instance '{request.InstanceId}' not found.");

        if (instance.InitiatorId != request.UserId)
        {
            throw new BusinessException("Only the initiator can withdraw the workflow instance.");
        }

        // Check if any task has been processed (status != Pending)
        var tasks = await db.WorkflowTasks
            .Where(t => t.InstanceId == request.InstanceId)
            .ToListAsync(cancellationToken);

        var hasProcessedTasks = tasks.Any(t => t.Status != Domain.Enums.TaskStatus.Pending);
        if (hasProcessedTasks)
        {
            throw new BusinessException("Cannot withdraw instance: tasks have already been processed.");
        }

        // Terminate the instance
        instance.Status = InstanceStatus.Terminated;

        // Terminate all active tokens
        var tokens = await db.WorkflowTokens
            .Where(t => t.InstanceId == request.InstanceId)
            .ToListAsync(cancellationToken);

        foreach (var token in tokens)
        {
            token.Status = TokenStatus.Terminated;
        }

        await db.SaveChangesAsync(cancellationToken);

        return Unit.Value;
    }
}
