#nullable enable
using Baya.Application.Common;
using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Models.Common;
using Baya.Domain.Entities.Messaging;
using Mediator;
using Microsoft.EntityFrameworkCore;

namespace Baya.Application.Features.Messaging.Commands.AddParticipant;

/// <summary>
/// Adds a user to a ticket (staff on any ticket, or the ticket owner). Enforces <c>UNIQUE(ticket_id, user_id)</c>:
/// a duplicate active participant is a clean conflict; a previously-removed participant is resurrected (the same
/// unique row), never re-inserted. The unique index is the authoritative backstop against a race.
/// </summary>
internal sealed class AddParticipantCommandHandler(
    ICurrentUser currentUser,
    IUnitOfWork unitOfWork)
    : IRequestHandler<AddParticipantCommand, OperationResult<bool>>
{
    public async ValueTask<OperationResult<bool>> Handle(AddParticipantCommand request, CancellationToken cancellationToken)
    {
        if (currentUser.UserId is not { } callerId)
            return OperationResult<bool>.UnauthorizedResult("Not authenticated.");

        var header = await unitOfWork.TicketRepository.GetHeaderAsync(request.TicketId, cancellationToken);
        if (header is null)
            return OperationResult<bool>.NotFoundResult("Ticket not found.");

        if (!StaffRoles.IsStaff(currentUser.Roles) && header.OpenedById != callerId)
            return OperationResult<bool>.ForbiddenResult("Only staff or the ticket owner can manage participants.");

        var existing = await unitOfWork.TicketRepository.GetParticipantAsync(request.TicketId, request.UserId, cancellationToken);
        if (existing is not null)
        {
            if (existing.IsActive)
                return OperationResult<bool>.ConflictResult("That user is already a participant on this ticket.");

            existing.Restore(callerId);
        }
        else
        {
            await unitOfWork.TicketRepository.AddParticipantAsync(new TicketParticipant
            {
                TicketId = request.TicketId,
                UserId = request.UserId,
                AddedById = callerId
            }, cancellationToken);
        }

        try
        {
            await unitOfWork.CommitAsync();
        }
        catch (DbUpdateException)
        {
            // Lost the UNIQUE(ticket_id, user_id) race — treat as the duplicate it is, not a 500.
            await unitOfWork.RollBackAsync();
            return OperationResult<bool>.ConflictResult("That user is already a participant on this ticket.");
        }

        return OperationResult<bool>.SuccessResult(true);
    }
}
