namespace StellaOps.Feedser.Merge.Services;

using System.Security.Cryptography;
using System.Linq;
using Microsoft.Extensions.Logging;
using StellaOps.Feedser.Models;
using StellaOps.Feedser.Storage.Mongo.MergeEvents;

/// <summary>
/// Persists merge events with canonical before/after hashes for auditability.
/// </summary>
public sealed class MergeEventWriter
{
    private readonly IMergeEventStore _mergeEventStore;
    private readonly CanonicalHashCalculator _hashCalculator;
    private readonly TimeProvider _timeProvider;
    private readonly ILogger<MergeEventWriter> _logger;

    public MergeEventWriter(
        IMergeEventStore mergeEventStore,
        CanonicalHashCalculator hashCalculator,
        TimeProvider timeProvider,
        ILogger<MergeEventWriter> logger)
    {
        _mergeEventStore = mergeEventStore ?? throw new ArgumentNullException(nameof(mergeEventStore));
        _hashCalculator = hashCalculator ?? throw new ArgumentNullException(nameof(hashCalculator));
        _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task<MergeEventRecord> AppendAsync(
        string advisoryKey,
        Advisory? before,
        Advisory after,
        IReadOnlyList<Guid> inputDocumentIds,
        IReadOnlyList<MergeFieldDecision>? fieldDecisions,
        CancellationToken cancellationToken)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(advisoryKey);
        ArgumentNullException.ThrowIfNull(after);

        var beforeHash = _hashCalculator.ComputeHash(before);
        var afterHash = _hashCalculator.ComputeHash(after);
        var timestamp = _timeProvider.GetUtcNow();
        var documentIds = inputDocumentIds?.ToArray() ?? Array.Empty<Guid>();

        var record = new MergeEventRecord(
            Guid.NewGuid(),
            advisoryKey,
            beforeHash,
            afterHash,
            timestamp,
            documentIds,
            fieldDecisions ?? Array.Empty<MergeFieldDecision>());

        if (!CryptographicOperations.FixedTimeEquals(beforeHash, afterHash))
        {
            _logger.LogInformation(
                "Merge event for {AdvisoryKey} changed hash {BeforeHash} -> {AfterHash}",
                advisoryKey,
                Convert.ToHexString(beforeHash),
                Convert.ToHexString(afterHash));
        }
        else
        {
            _logger.LogInformation("Merge event for {AdvisoryKey} recorded without hash change", advisoryKey);
        }

        await _mergeEventStore.AppendAsync(record, cancellationToken).ConfigureAwait(false);
        return record;
    }
}
