#nullable enable
using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Models.Common;
using Baya.Application.Models.Verification;
using Baya.Domain.Entities.Verification;
using Mediator;

namespace Baya.Application.Features.Verification.Commands.UpsertStepType;

internal sealed class AdminUpsertStepTypeCommandHandler(IUnitOfWork unitOfWork, ICacheService cache)
    : IRequestHandler<AdminUpsertStepTypeCommand, OperationResult<VerificationStepTypeDto>>
{
    public async ValueTask<OperationResult<VerificationStepTypeDto>> Handle(
        AdminUpsertStepTypeCommand request, CancellationToken cancellationToken)
    {
        var repo = unitOfWork.VerificationRepository;
        VerificationStepType type;

        if (request.Id is { } id)
        {
            var existing = await repo.GetStepTypeByIdAsync(id, cancellationToken);
            if (existing is null)
                return OperationResult<VerificationStepTypeDto>.NotFoundResult("Step type not found.");

            if (!string.Equals(existing.Code, request.Code, StringComparison.Ordinal))
            {
                // The stable machine code is what steps snapshot against — it must not change once any
                // nurse verification already seeded a step from this type.
                if (await repo.StepTypeInUseAsync(id, cancellationToken))
                    return OperationResult<VerificationStepTypeDto>.FailureResult(nameof(request.Code), "Code cannot change once the step type is in use.");

                if (await repo.StepTypeCodeExistsAsync(request.Code, cancellationToken))
                    return OperationResult<VerificationStepTypeDto>.ConflictResult("A step type with this code already exists.");

                existing.Code = request.Code;
            }

            existing.DisplayName = request.DisplayName;
            existing.Description = request.Description;
            existing.IsRequired = request.IsRequired;
            existing.IsAutomated = request.IsAutomated;
            existing.AutomationProvider = request.AutomationProvider;
            existing.SortOrder = request.SortOrder;
            existing.IsActive = request.IsActive;
            type = existing;
        }
        else
        {
            if (await repo.StepTypeCodeExistsAsync(request.Code, cancellationToken))
                return OperationResult<VerificationStepTypeDto>.ConflictResult("A step type with this code already exists.");

            type = new VerificationStepType
            {
                Code = request.Code,
                DisplayName = request.DisplayName,
                Description = request.Description,
                IsRequired = request.IsRequired,
                IsAutomated = request.IsAutomated,
                AutomationProvider = request.AutomationProvider,
                SortOrder = request.SortOrder,
                IsActive = request.IsActive
            };
            await repo.AddStepTypeAsync(type, cancellationToken);
        }

        await unitOfWork.CommitAsync();
        await VerificationCache.InvalidateStepTypesAsync(cache, cancellationToken);

        return OperationResult<VerificationStepTypeDto>.SuccessResult(new VerificationStepTypeDto(
            type.Id, type.Code, type.DisplayName, type.Description, type.IsRequired, type.IsAutomated,
            type.AutomationProvider, type.SortOrder, type.IsActive));
    }
}
