using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using NATS.Client.JetStream;

namespace StellaOps.Scanner.Queue.Nats;

internal sealed class NatsScanQueueLease : IScanQueueLease
{
    private readonly NatsScanQueue _queue;
    private readonly NatsJSMsg<byte[]> _message;
    private int _completed;

    internal NatsScanQueueLease(
        NatsScanQueue queue,
        NatsJSMsg<byte[]> message,
        string messageId,
        string jobId,
        byte[] payload,
        int attempt,
        DateTimeOffset enqueuedAt,
        DateTimeOffset leaseExpiresAt,
        string consumer,
        string? idempotencyKey,
        IReadOnlyDictionary<string, string> attributes)
    {
        _queue = queue;
        _message = message;
        MessageId = messageId;
        JobId = jobId;
        Payload = payload;
        Attempt = attempt;
        EnqueuedAt = enqueuedAt;
        LeaseExpiresAt = leaseExpiresAt;
        Consumer = consumer;
        IdempotencyKey = idempotencyKey;
        Attributes = attributes;
    }

    public string MessageId { get; }

    public string JobId { get; }

    public ReadOnlyMemory<byte> Payload { get; }

    public int Attempt { get; internal set; }

    public DateTimeOffset EnqueuedAt { get; }

    public DateTimeOffset LeaseExpiresAt { get; private set; }

    public string Consumer { get; }

    public string? IdempotencyKey { get; }

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

    internal NatsJSMsg<byte[]> Message => _message;

    public Task AcknowledgeAsync(CancellationToken cancellationToken = default)
        => _queue.AcknowledgeAsync(this, cancellationToken);

    public Task RenewAsync(TimeSpan leaseDuration, CancellationToken cancellationToken = default)
        => _queue.RenewLeaseAsync(this, leaseDuration, cancellationToken);

    public Task ReleaseAsync(QueueReleaseDisposition disposition, CancellationToken cancellationToken = default)
        => _queue.ReleaseAsync(this, disposition, cancellationToken);

    public Task DeadLetterAsync(string reason, CancellationToken cancellationToken = default)
        => _queue.DeadLetterAsync(this, reason, cancellationToken);

    internal bool TryBeginCompletion()
        => Interlocked.CompareExchange(ref _completed, 1, 0) == 0;

    internal void RefreshLease(DateTimeOffset expiresAt)
        => LeaseExpiresAt = expiresAt;
}
