using Hangfire;
using System.Text.Json;
using KArtSell.Modules.ModelOperations.Domain;

namespace KArtSell.Host.Features.SecurityMaster;

/// <summary>
/// VS-02 ASYNC: Security Master Outbox Events
/// Published when sync completes
/// </summary>

public class SecurityMasterSyncedEvent
{
    public Guid EventId { get; set; } = Guid.NewGuid();
    public string EventType { get; set; } = "SecurityMasterSynced";
    public int NewVersion { get; set; }
    public int RulesCount { get; set; }
    public DateTime SyncedAt { get; set; }
    public string CorrelationId { get; set; } = "";
}

public class PermissionRuleUpdatedEvent
{
    public Guid EventId { get; set; } = Guid.NewGuid();
    public string EventType { get; set; } = "PermissionRuleUpdated";
    public Guid RuleId { get; set; }
    public string ResourceName { get; set; } = "";
    public string Action { get; set; } = "";
    public int NewVersion { get; set; }
    public DateTime UpdatedAt { get; set; }
    public string CorrelationId { get; set; } = "";
}

public interface ISecurityMasterEventPublisher
{
    Task PublishSyncCompletedAsync(SecurityMasterSyncedEvent evt, CancellationToken ct);
    Task PublishRuleUpdatedAsync(PermissionRuleUpdatedEvent evt, CancellationToken ct);
}

public class SecurityMasterEventPublisher : ISecurityMasterEventPublisher
{
    private readonly Npgsql.NpgsqlDataSource _dataSource;

    public SecurityMasterEventPublisher(Npgsql.NpgsqlDataSource dataSource)
    {
        _dataSource = dataSource;
    }

    public async Task PublishSyncCompletedAsync(SecurityMasterSyncedEvent evt, CancellationToken ct)
    {
        await using var connection = await _dataSource.OpenConnectionAsync(ct);

        const string sql = """
            INSERT INTO shared.outbox (aggregate_id, event_type, payload, published_at, correlation_id)
            VALUES (@aggregateId, @eventType, @payload, CURRENT_TIMESTAMP, @correlationId);
            """;

        await using var cmd = connection.CreateCommand();
        cmd.CommandText = sql;
        cmd.Parameters.AddWithValue("@aggregateId", Guid.NewGuid());
        cmd.Parameters.AddWithValue("@eventType", evt.EventType);
        cmd.Parameters.AddWithValue("@payload", JsonSerializer.Serialize(evt));
        cmd.Parameters.AddWithValue("@correlationId", evt.CorrelationId);

        await cmd.ExecuteNonQueryAsync(ct);
    }

    public async Task PublishRuleUpdatedAsync(PermissionRuleUpdatedEvent evt, CancellationToken ct)
    {
        await using var connection = await _dataSource.OpenConnectionAsync(ct);

        const string sql = """
            INSERT INTO shared.outbox (aggregate_id, event_type, payload, published_at, correlation_id)
            VALUES (@aggregateId, @eventType, @payload, CURRENT_TIMESTAMP, @correlationId);
            """;

        await using var cmd = connection.CreateCommand();
        cmd.CommandText = sql;
        cmd.Parameters.AddWithValue("@aggregateId", evt.RuleId);
        cmd.Parameters.AddWithValue("@eventType", evt.EventType);
        cmd.Parameters.AddWithValue("@payload", JsonSerializer.Serialize(evt));
        cmd.Parameters.AddWithValue("@correlationId", evt.CorrelationId);

        await cmd.ExecuteNonQueryAsync(ct);
    }
}

/// <summary>
/// VS-02 ASYNC: Hangfire Job for periodic sync
/// Scheduled every 30 seconds
/// Idempotent: Multiple runs produce same result
/// </summary>

public interface ISecurityMasterSyncJob
{
    Task ExecuteAsync(CancellationToken ct);
}

public class SecurityMasterSyncJobHandler : ISecurityMasterSyncJob
{
    private readonly ISecurityMasterSyncHandler _syncHandler;
    private readonly ISecurityMasterEventPublisher _eventPublisher;
    private readonly Npgsql.NpgsqlDataSource _dataSource;

    public SecurityMasterSyncJobHandler(
        ISecurityMasterSyncHandler syncHandler,
        ISecurityMasterEventPublisher eventPublisher,
        Npgsql.NpgsqlDataSource dataSource)
    {
        _syncHandler = syncHandler;
        _eventPublisher = eventPublisher;
        _dataSource = dataSource;
    }

    public async Task ExecuteAsync(CancellationToken ct)
    {
        // Get current version
        const string versionSql = "SELECT COALESCE(MAX(version), 0) FROM security_master.rules;";
        await using var connection = await _dataSource.OpenConnectionAsync(ct);
        await using var cmd = connection.CreateCommand();
        cmd.CommandText = versionSql;

        var versionObj = await cmd.ExecuteScalarAsync(ct);
        var currentVersion = versionObj != null ? Convert.ToInt32(versionObj) : 0;

        var correlationId = Guid.NewGuid().ToString();
        var idempotencyKey = SecurityMasterPolicy.CreateIdempotencyKey(currentVersion, correlationId);

        // Perform sync
        var result = await _syncHandler.SyncAsync(
            fromVersion: currentVersion,
            idempotencyKey: idempotencyKey,
            correlationId: correlationId,
            cancellationToken: ct);

        // Publish events
        if (result.IsSuccess && result.AppliedRules.Count > 0)
        {
            var syncEvent = new SecurityMasterSyncedEvent
            {
                NewVersion = result.NewVersion,
                RulesCount = result.AppliedRules.Count,
                SyncedAt = DateTime.UtcNow,
                CorrelationId = correlationId,
            };

            await _eventPublisher.PublishSyncCompletedAsync(syncEvent, ct);

            foreach (var rule in result.AppliedRules)
            {
                var ruleEvent = new PermissionRuleUpdatedEvent
                {
                    RuleId = rule.RuleId,
                    ResourceName = rule.ResourceName,
                    Action = rule.Action,
                    NewVersion = rule.Version,
                    UpdatedAt = DateTime.UtcNow,
                    CorrelationId = correlationId,
                };

                await _eventPublisher.PublishRuleUpdatedAsync(ruleEvent, ct);
            }
        }
    }
}

/// <summary>
/// VS-02 ASYNC: Inbox Consumer (receives events)
/// Handles: SecurityMasterSynced, PermissionRuleUpdated
/// Idempotent: Re-processing same event = no-op
/// </summary>

public interface ISecurityMasterInboxConsumer
{
    string EventType { get; }
    Task ConsumeAsync(string payload, CancellationToken ct);
}

public class SecurityMasterCacheInvalidationConsumer : ISecurityMasterInboxConsumer
{
    private readonly IPermissionCacheInvalidator _cacheInvalidator;
    private readonly IInboxStore _inboxStore;

    public string EventType => "PermissionRuleUpdated";

    public SecurityMasterCacheInvalidationConsumer(
        IPermissionCacheInvalidator cacheInvalidator,
        IInboxStore inboxStore)
    {
        _cacheInvalidator = cacheInvalidator;
        _inboxStore = inboxStore;
    }

    public async Task ConsumeAsync(string payload, CancellationToken ct)
    {
        var evt = JsonSerializer.Deserialize<PermissionRuleUpdatedEvent>(payload)
            ?? throw new ArgumentException("Invalid payload");

        var messageId = evt.EventId.ToString();

        // Check idempotency
        if (await _inboxStore.IsProcessedAsync(messageId, ct))
            return;

        try
        {
            // Invalidate cache for affected resource
            await _cacheInvalidator.InvalidateByResourceAsync(evt.ResourceName, ct);

            // Mark as processed
            await _inboxStore.MarkProcessedAsync(messageId, ct);
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException($"Failed to consume event {messageId}: {ex.Message}", ex);
        }
    }
}

/// <summary>
/// Supporting abstractions
/// </summary>

public interface IPermissionCacheInvalidator
{
    Task InvalidateByResourceAsync(string resourceName, CancellationToken ct);
}

public interface IInboxStore
{
    Task<bool> IsProcessedAsync(string messageId, CancellationToken ct);
    Task MarkProcessedAsync(string messageId, CancellationToken ct);
}

/// <summary>
/// Extension methods for Hangfire registration
/// </summary>

public static class SecurityMasterJobsExtensions
{
    public static void AddSecurityMasterJobs(this IServiceCollection services)
    {
        services.AddScoped<ISecurityMasterEventPublisher, SecurityMasterEventPublisher>();
        services.AddScoped<ISecurityMasterSyncJob, SecurityMasterSyncJobHandler>();
        services.AddScoped<ISecurityMasterInboxConsumer, SecurityMasterCacheInvalidationConsumer>();
        services.AddScoped<IPermissionCacheInvalidator, PermissionCacheInvalidator>();
        services.AddScoped<IInboxStore, InboxStore>();
    }
}

/// <summary>
/// Stub implementations (to be replaced with real services)
/// </summary>

public class PermissionCacheInvalidator : IPermissionCacheInvalidator
{
    public async Task InvalidateByResourceAsync(string resourceName, CancellationToken ct)
    {
        await Task.Delay(10, ct);
    }
}

public class InboxStore : IInboxStore
{
    public async Task<bool> IsProcessedAsync(string messageId, CancellationToken ct)
    {
        await Task.Delay(5, ct);
        return false;
    }

    public async Task MarkProcessedAsync(string messageId, CancellationToken ct)
    {
        await Task.Delay(5, ct);
    }
}
