using System.Collections.Concurrent;
using ZB.MOM.WW.CBDD.Core.Collections;
using ZB.MOM.WW.CBDD.Core.Indexing;
using ZB.MOM.WW.CBDD.Core.Transactions;

namespace ZB.MOM.WW.CBDD.Core.CDC;

/// <summary>
/// Handles CDC watch/notify behavior for a single collection.
/// Extracted from DocumentCollection to keep storage/query concerns separated from event plumbing.
/// </summary>
/// <typeparam name="TId">Document identifier type.</typeparam>
/// <typeparam name="T">Document type.</typeparam>
internal sealed class CollectionCdcPublisher<TId, T> where T : class
{
    private readonly ITransactionHolder _transactionHolder;
    private readonly string _collectionName;
    private readonly IDocumentMapper<TId, T> _mapper;
    private readonly ChangeStreamDispatcher? _dispatcher;
    private readonly ConcurrentDictionary<ushort, string> _keyReverseMap;

    /// <summary>
    /// Initializes a new instance of the <see cref="CollectionCdcPublisher"/> class.
    /// </summary>
    /// <param name="transactionHolder">The transaction holder.</param>
    /// <param name="collectionName">The collection name.</param>
    /// <param name="mapper">The document mapper.</param>
    /// <param name="dispatcher">The change stream dispatcher.</param>
    /// <param name="keyReverseMap">The key reverse map.</param>
    public CollectionCdcPublisher(
        ITransactionHolder transactionHolder,
        string collectionName,
        IDocumentMapper<TId, T> mapper,
        ChangeStreamDispatcher? dispatcher,
        ConcurrentDictionary<ushort, string> keyReverseMap)
    {
        _transactionHolder = transactionHolder ?? throw new ArgumentNullException(nameof(transactionHolder));
        _collectionName = collectionName ?? throw new ArgumentNullException(nameof(collectionName));
        _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
        _dispatcher = dispatcher;
        _keyReverseMap = keyReverseMap ?? throw new ArgumentNullException(nameof(keyReverseMap));
    }

    /// <summary>
    /// Executes Watch.
    /// </summary>
    /// <param name="capturePayload">Whether to include payload data.</param>
    public IObservable<ChangeStreamEvent<TId, T>> Watch(bool capturePayload = false)
    {
        if (_dispatcher == null)
            throw new InvalidOperationException("CDC is not initialized.");

        return new ChangeStreamObservable<TId, T>(
            _dispatcher,
            _collectionName,
            capturePayload,
            _mapper,
            _keyReverseMap);
    }

    /// <summary>
    /// Executes Notify.
    /// </summary>
    /// <param name="type">The operation type.</param>
    /// <param name="id">The document identifier.</param>
    /// <param name="docData">The serialized document payload.</param>
    public void Notify(OperationType type, TId id, ReadOnlySpan<byte> docData = default)
    {
        var transaction = _transactionHolder.GetCurrentTransactionOrStart();
        if (_dispatcher == null)
            return;

        if (!_dispatcher.HasAnyWatchers(_collectionName))
            return;

        ReadOnlyMemory<byte>? payload = null;
        if (!docData.IsEmpty && _dispatcher.HasPayloadWatchers(_collectionName))
        {
            payload = docData.ToArray();
        }

        var idBytes = _mapper.ToIndexKey(id).Data.ToArray();

        if (transaction is Transaction t)
        {
            t.AddChange(new InternalChangeEvent
            {
                Timestamp = DateTime.UtcNow.Ticks,
                TransactionId = transaction.TransactionId,
                CollectionName = _collectionName,
                Type = type,
                IdBytes = idBytes,
                PayloadBytes = payload
            });
        }
    }
}
