using Books.Api.Infrastructure;
using EventFlow.Aggregates;
using EventFlow.ReadStores;
using Hangfire;

namespace Books.Api.EventFlow.Infrastructure;

public class ReadStoresResilienceStrategy(
    ILogger<ReadStoresResilienceStrategy> logger,
    IScheduler scheduler) : IDispatchToReadStoresResilienceStrategy
{
    public Task BeforeUpdateAsync(
        IReadStoreManager readStoreManager,
        IReadOnlyCollection<IDomainEvent> domainEvents,
        CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    public Task<bool> HandleUpdateFailedAsync(
        IReadStoreManager readStoreManager,
        IReadOnlyCollection<IDomainEvent> domainEvents,
        Exception exception,
        CancellationToken cancellationToken)
    {
        var firstEvent = domainEvents.FirstOrDefault();
        var aggregateId = firstEvent?.GetIdentity()?.Value ?? "unknown";
        var readModelType = readStoreManager.ReadModelType.Name;

        logger.LogError(exception,
            "[RESILIENCE] Failed to update read model {ReadModelType} for aggregate {AggregateId}. " +
            "Events: {EventCount}. This may cause data inconsistency.",
            readModelType,
            aggregateId,
            domainEvents.Count);

        // Schedule a retry job to repopulate the read model
        if (firstEvent != null)
        {
            var aggregateType = firstEvent.AggregateType.Name;

            scheduler.EnqueueJob<ReadModelRepopulationJob>(
                job => job.RepopulateReadModelAsync(
                    aggregateType,
                    aggregateId,
                    readModelType),
                TimeSpan.FromSeconds(30));

            logger.LogWarning(
                "[RESILIENCE] Scheduled read model repopulation job for {ReadModelType} / {AggregateId}",
                readModelType,
                aggregateId);
        }

        // Return false to indicate we handled the failure (don't rethrow)
        return Task.FromResult(false);
    }

    public Task UpdateSucceededAsync(
        IReadStoreManager readStoreManager,
        IReadOnlyCollection<IDomainEvent> domainEvents,
        CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

public class ReadModelRepopulationJob(ILogger<ReadModelRepopulationJob> logger)
{
    [AutomaticRetry(Attempts = 3, DelaysInSeconds = [30, 60, 120])]
    public Task RepopulateReadModelAsync(
        string aggregateType,
        string aggregateId,
        string readModelType)
    {
        logger.LogInformation(
            "[REPOPULATION] Starting read model repopulation for {ReadModelType} / {AggregateId}",
            readModelType,
            aggregateId);

        // For now, we log the repopulation attempt
        // A full implementation would:
        // 1. Load all events for the aggregate from the event store
        // 2. Clear the existing read model entry
        // 3. Replay all events to rebuild the read model

        logger.LogWarning(
            "[REPOPULATION] Read model repopulation for {ReadModelType} / {AggregateId} " +
            "requires manual intervention. Check data consistency.",
            readModelType,
            aggregateId);

        return Task.CompletedTask;
    }
}
