using System;
using System.Collections.Generic;

namespace StellaOps.Scanner.Queue;

public sealed class ScanQueueMessage
{
    private readonly byte[] _payload;

    public ScanQueueMessage(string jobId, ReadOnlyMemory<byte> payload)
    {
        if (string.IsNullOrWhiteSpace(jobId))
        {
            throw new ArgumentException("Job identifier must be provided.", nameof(jobId));
        }

        JobId = jobId;
        _payload = CopyPayload(payload);
    }

    public string JobId { get; }

    public string? IdempotencyKey { get; init; }

    public string? TraceId { get; init; }

    public IReadOnlyDictionary<string, string>? Attributes { get; init; }

    public ReadOnlyMemory<byte> Payload => _payload;

    private static byte[] CopyPayload(ReadOnlyMemory<byte> payload)
    {
        if (payload.Length == 0)
        {
            return Array.Empty<byte>();
        }

        var copy = new byte[payload.Length];
        payload.Span.CopyTo(copy);
        return copy;
    }
}

public readonly record struct QueueEnqueueResult(string MessageId, bool Deduplicated);

public sealed class QueueLeaseRequest
{
    public QueueLeaseRequest(string consumer, int batchSize, TimeSpan leaseDuration)
    {
        if (string.IsNullOrWhiteSpace(consumer))
        {
            throw new ArgumentException("Consumer name must be provided.", nameof(consumer));
        }

        if (batchSize <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(batchSize), batchSize, "Batch size must be positive.");
        }

        if (leaseDuration <= TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException(nameof(leaseDuration), leaseDuration, "Lease duration must be positive.");
        }

        Consumer = consumer;
        BatchSize = batchSize;
        LeaseDuration = leaseDuration;
    }

    public string Consumer { get; }

    public int BatchSize { get; }

    public TimeSpan LeaseDuration { get; }
}

public sealed class QueueClaimOptions
{
    public QueueClaimOptions(
        string claimantConsumer,
        int batchSize,
        TimeSpan minIdleTime)
    {
        if (string.IsNullOrWhiteSpace(claimantConsumer))
        {
            throw new ArgumentException("Consumer must be provided.", nameof(claimantConsumer));
        }

        if (batchSize <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(batchSize), batchSize, "Batch size must be positive.");
        }

        if (minIdleTime < TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException(nameof(minIdleTime), minIdleTime, "Idle time cannot be negative.");
        }

        ClaimantConsumer = claimantConsumer;
        BatchSize = batchSize;
        MinIdleTime = minIdleTime;
    }

    public string ClaimantConsumer { get; }

    public int BatchSize { get; }

    public TimeSpan MinIdleTime { get; }
}

public enum QueueReleaseDisposition
{
    Retry,
    Abandon
}
