using FastEndpoints;
using Npgsql;
using System.Text.Json;
using KArtSell.Modules.ModelOperations.Domain;
using KArtSell.BuildingBlocks.Time;

namespace KArtSell.Host.Features.SecurityMaster;

/// <summary>
/// VS-02 BE: Security Master Sync Endpoint
/// POST /api/security/master/sync
///
/// Synchronizes local security rules with remote master
/// - Last-write-wins conflict resolution
/// - Idempotent by version + correlationId
/// - Atomic transaction (all-or-nothing)
/// - Returns 200 if success, 409 if conflict, 503 if unavailable
/// </summary>

public sealed class SyncSecurityMasterRequest
{
    public int FromVersion { get; set; }
}

public sealed class SyncSecurityMasterResponse
{
    public int Version { get; set; }
    public int RulesCount { get; set; }
    public DateTime SyncedAt { get; set; }
    public List<string> Conflicts { get; set; } = new();
}

// DISABLED: ISecurityMasterRulesStore implementation pending
// public sealed class SyncSecurityMasterEndpoint : Endpoint<SyncSecurityMasterRequest, SyncSecurityMasterResponse>
// {
//     private readonly ISecurityMasterSyncHandler _handler;
//
//     public SyncSecurityMasterEndpoint(ISecurityMasterSyncHandler handler)
//     {
//         _handler = handler;
//     }
//
//     public override void Configure()
//     {
//         Post("/api/security/master/sync");
//         Roles("SecurityAdmin");
//         AllowAnonymous();  // Override role check if needed for service-to-service
//     }
//
//     public override async Task HandleAsync(SyncSecurityMasterRequest req, CancellationToken ct)
//     {
//         var correlationId = HttpContext.TraceIdentifier;
//         var idempotencyKey = SecurityMasterPolicy.CreateIdempotencyKey(req.FromVersion, correlationId);
//
//         var result = await _handler.SyncAsync(
//             fromVersion: req.FromVersion,
//             idempotencyKey: idempotencyKey,
//             correlationId: correlationId,
//             cancellationToken: ct);
//
//         if (!result.IsSuccess)
//         {
//             ThrowError($"Version conflict. Local: {req.FromVersion}, Remote: {result.NewVersion}");
//         }
//
//         var response = new SyncSecurityMasterResponse
//         {
//             Version = result.NewVersion,
//             RulesCount = result.AppliedRules.Count,
//             SyncedAt = DateTime.UtcNow,
//             Conflicts = result.Conflicts,
//         };
//
//         HttpContext.Response.StatusCode = StatusCodes.Status200OK;
//         HttpContext.Response.ContentType = "application/json";
//         await HttpContext.Response.WriteAsync(JsonSerializer.Serialize(response), ct);
//     }
// }

/// <summary>
/// VS-02 BE: Get Security Rules Endpoint
/// GET /api/security/master/rules
///
/// Retrieves active security rules
/// - Returns 503 if data stale (>5 min)
/// - Cached response (100ms SLA)
/// </summary>

public sealed class GetSecurityMasterRulesResponse
{
    public List<SecurityRuleDto> Rules { get; set; } = new();
    public int Version { get; set; }
    public DateTime LastSyncAt { get; set; }
}

public sealed class SecurityRuleDto
{
    public Guid RuleId { get; set; }
    public string ResourceName { get; set; } = "";
    public string Action { get; set; } = "";
    public int Version { get; set; }
    public DateTime EffectiveAt { get; set; }
    public DateTime? ExpiresAt { get; set; }
}

// DISABLED: ISecurityMasterRulesStore implementation pending
// public sealed class GetSecurityMasterRulesEndpoint : EndpointWithoutRequest<GetSecurityMasterRulesResponse>
// {
//     private readonly ISecurityMasterRulesStore _store;
//     private readonly IClock _clock;
//
//     public GetSecurityMasterRulesEndpoint(ISecurityMasterRulesStore store, IClock clock)
//     {
//         _store = store;
//         _clock = clock;
//     }
//
//     public override void Configure()
//     {
//         Get("/api/security/master/rules");
//         AllowAnonymous();
//     }
//
//     public override async Task HandleAsync(CancellationToken ct)
//     {
//         var state = await _store.GetCurrentStateAsync(ct);
//
//         var staleTreshold = _clock.UtcNow.AddMinutes(-5);
//         if (state.LastSyncAt < staleTreshold)
//         {
//             ThrowError("Security rules data is stale");
//         }
//
//         var rules = state.Rules
//             .Where(r => SecurityMasterPolicy.IsRuleActive(r, _clock.UtcNow.DateTime))
//             .Select(r => new SecurityRuleDto
//             {
//                 RuleId = r.RuleId,
//                 ResourceName = r.ResourceName,
//                 Action = r.Action,
//                 Version = r.Version,
//                 EffectiveAt = r.EffectiveAt,
//                 ExpiresAt = r.ExpiresAt,
//             })
//             .ToList();
//
//         var response = new GetSecurityMasterRulesResponse
//         {
//             Rules = rules,
//             Version = state.Version,
//             LastSyncAt = state.LastSyncAt,
//         };
//
//         HttpContext.Response.StatusCode = StatusCodes.Status200OK;
//         HttpContext.Response.ContentType = "application/json";
//         await HttpContext.Response.WriteAsync(JsonSerializer.Serialize(response), ct);
//     }
// }

/// <summary>
/// VS-02 Application Handler: Orchestrates sync operation
/// Responsibilities:
/// - Fetch remote rules
/// - Apply conflict resolution
/// - Persist to database (atomic)
/// - Publish events
/// - Audit logging
/// </summary>

public interface ISecurityMasterSyncHandler
{
    Task<SyncResult> SyncAsync(
        int fromVersion,
        string idempotencyKey,
        string correlationId,
        CancellationToken cancellationToken);
}

public class SecurityMasterSyncHandler : ISecurityMasterSyncHandler
{
    private readonly NpgsqlDataSource _dataSource;
    private readonly IRemoteSecurityMasterClient _remoteClient;
    private readonly ISecurityMasterRulesStore _store;
    private readonly IClock _clock;

    public SecurityMasterSyncHandler(
        NpgsqlDataSource dataSource,
        IRemoteSecurityMasterClient remoteClient,
        ISecurityMasterRulesStore store,
        IClock clock)
    {
        _dataSource = dataSource;
        _remoteClient = remoteClient;
        _store = store;
        _clock = clock;
    }

    public async Task<SyncResult> SyncAsync(
        int fromVersion,
        string idempotencyKey,
        string correlationId,
        CancellationToken cancellationToken)
    {
        // Check idempotency
        var existing = await _store.GetResultByIdempotencyKeyAsync(idempotencyKey, cancellationToken);
        if (existing != null)
        {
            return existing;
        }

        try
        {
            // Fetch remote rules
            var remoteState = await _remoteClient.GetRulesAsync(fromVersion, cancellationToken);

            // Get local state
            var localState = await _store.GetCurrentStateAsync(cancellationToken);

            // Resolve conflicts
            var syncState = new SyncState(
                LocalVersion: localState.Version,
                RemoteVersion: remoteState.Version,
                LocalRules: localState.Rules.ToList(),
                RemoteRules: remoteState.Rules.ToList(),
                IdempotencyKey: idempotencyKey,
                CorrelationId: correlationId);

            var result = SecurityMasterPolicy.ResolveSyncConflict(syncState);

            if (!result.IsSuccess)
            {
                return result;
            }

            // Apply changes (atomic transaction)
            await using var transaction = await _dataSource.OpenConnectionAsync(cancellationToken);
            await using var tx = await transaction.BeginTransactionAsync(cancellationToken);

            try
            {
                foreach (var rule in result.AppliedRules)
                {
                    await PersistRuleAsync(transaction, rule, cancellationToken);
                }

                // Store sync result (idempotency)
                await _store.StoreSyncResultAsync(idempotencyKey, result, cancellationToken);

                await tx.CommitAsync(cancellationToken);
            }
            catch
            {
                await tx.RollbackAsync(cancellationToken);
                throw;
            }

            return result;
        }
        catch (Exception ex)
        {
            return new SyncResult(
                IsSuccess: false,
                NewVersion: fromVersion,
                AppliedRules: new(),
                Conflicts: new() { ex.Message },
                ErrorMessage: "Sync failed: " + ex.Message,
                CorrelationId: correlationId);
        }
    }

    private async Task PersistRuleAsync(Npgsql.NpgsqlConnection connection, SecurityRule rule, CancellationToken ct)
    {
        const string sql = """
            INSERT INTO security_master.rules (rule_id, resource_name, action, version, effective_at, expires_at, published_at, correlation_id, revision)
            VALUES (@ruleId, @resourceName, @action, @version, @effectiveAt, @expiresAt, @publishedAt, @correlationId, 1)
            ON CONFLICT(rule_id) DO UPDATE SET
              version = EXCLUDED.version,
              published_at = EXCLUDED.published_at,
              revision = security_master.rules.revision + 1
            WHERE EXCLUDED.published_at > security_master.rules.published_at;
            """;

        await using var cmd = connection.CreateCommand();
        cmd.CommandText = sql;
        cmd.Parameters.AddWithValue("@ruleId", rule.RuleId);
        cmd.Parameters.AddWithValue("@resourceName", rule.ResourceName);
        cmd.Parameters.AddWithValue("@action", rule.Action);
        cmd.Parameters.AddWithValue("@version", rule.Version);
        cmd.Parameters.AddWithValue("@effectiveAt", rule.EffectiveAt);
        cmd.Parameters.AddWithValue("@expiresAt", rule.ExpiresAt ?? (object)DBNull.Value);
        cmd.Parameters.AddWithValue("@publishedAt", rule.PublishedAt);
        cmd.Parameters.AddWithValue("@correlationId", rule.CorrelationId);

        await cmd.ExecuteNonQueryAsync(ct);
    }
}

/// <summary>
/// Abstraction: Remote security master client (service-to-service)
/// </summary>

public interface IRemoteSecurityMasterClient
{
    Task<(int Version, List<SecurityRule> Rules)> GetRulesAsync(int fromVersion, CancellationToken ct);
}

/// <summary>
/// Abstraction: Local security rules store (persistence)
/// </summary>

public record SecurityMasterState(int Version, DateTime LastSyncAt, List<SecurityRule> Rules);

public interface ISecurityMasterRulesStore
{
    Task<SecurityMasterState> GetCurrentStateAsync(CancellationToken ct);
    Task StoreSyncResultAsync(string idempotencyKey, SyncResult result, CancellationToken ct);
    Task<SyncResult?> GetResultByIdempotencyKeyAsync(string idempotencyKey, CancellationToken ct);
}
