using KArtSell.BuildingBlocks.Time;
using KArtSell.Modules.ModelOperations.Domain;

namespace KArtSell.Modules.ModelOperations.Application;

public sealed class ModelOperationRequestService(
    IApprovedModelContextReader contextReader,
    IModelOperationRequestRepository repository,
    IClock clock) : IModelOperationRequestService
{
    public async Task<ModelOperationRequest?> RequestAsync(
        Guid scheduleId,
        string operationCode,
        string scopeKey,
        string automationMode,
        string idempotencyKey,
        DateTimeOffset scheduledFor,
        string catchUpPolicy,
        int maxCatchUp,
        string correlationId,
        CancellationToken cancellationToken)
    {
        var definition = ModelOperationRegistry.GetRequired(operationCode);
        ModelOperationExecutionBoundary.EnsureAllowed(definition);
        if (!definition.AutomationMode.ToContractValue().Equals(automationMode, StringComparison.OrdinalIgnoreCase))
            throw new InvalidOperationException("Schedule automation mode does not match the approved operation registry.");
        if (maxCatchUp is < 0 or > 31) throw new ArgumentOutOfRangeException(nameof(maxCatchUp));
        if (string.IsNullOrWhiteSpace(catchUpPolicy)) throw new ArgumentException("Catch-up policy is required.", nameof(catchUpPolicy));

        var now = clock.UtcNow;
        var context = await contextReader.ReadAsync(scopeKey, now, cancellationToken);
        if (context is null)
            return null;

        context.VersionSet.EnsureValid();
        var request = new ModelOperationRequest(
            Guid.NewGuid(),
            scheduleId,
            definition.OperationCode,
            scopeKey,
            definition.AutomationMode.ToContractValue(),
            idempotencyKey,
            scheduledFor,
            catchUpPolicy,
            maxCatchUp,
            context,
            correlationId,
            now);

        return await repository.AddAsync(request, cancellationToken) ? request : null;
    }
}
