using Microsoft.EntityFrameworkCore;
using Tiku.Application.Security;
using Tiku.Infrastructure.Persistence;

namespace Tiku.Infrastructure.Security;

internal sealed class AuthorizationCacheInvalidationProcessor(
    TikuDbContext dbContext,
    IAccessSecurityCache cache) : IAuthorizationCacheInvalidationProcessor
{
    public async Task<int> ProcessPendingAsync(int batchSize = 100, CancellationToken cancellationToken = default)
    {
        if (!cache.IsConfigured) return 0;
        var items = await dbContext.AuthorizationCacheInvalidations
            .Where(item => item.ProcessedAt == null)
            .OrderBy(item => item.CreatedAt)
            .Take(Math.Clamp(batchSize, 1, 500))
            .ToArrayAsync(cancellationToken);
        var processed = 0;
        foreach (var item in items)
            try
            {
                switch (item.TargetType)
                {
                    case "session" when item.SessionId.HasValue:
                        await cache.InvalidateSessionAsync(item.SessionId.Value, cancellationToken);
                        break;
                    case "user" when item.UserId.HasValue:
                        await cache.InvalidateUserAsync(item.UserId.Value, cancellationToken);
                        break;
                    case "tenant" when item.TenantId.HasValue:
                        await cache.InvalidateTenantAsync(item.TenantId.Value, cancellationToken);
                        break;
                    case "membership" when item.TenantId.HasValue && item.UserId.HasValue:
                        await cache.InvalidateMembershipAsync(item.TenantId.Value, item.UserId.Value,
                            cancellationToken);
                        break;
                    case "scope" when item.Realm.HasValue && item.Version.HasValue:
                        await cache.SetAuthorizationVersionAsync(item.Realm.Value, item.TenantId, item.Version.Value,
                            cancellationToken);
                        break;
                    default:
                        throw new InvalidOperationException($"Invalid authorization cache invalidation {item.Id}.");
                }

                item.ProcessedAt = DateTimeOffset.UtcNow;
                item.LastError = null;
                item.AttemptCount++;
                processed++;
            }
            catch (Exception exception) when (exception is not OperationCanceledException)
            {
                item.AttemptCount++;
                item.LastError = exception.Message;
            }

        await dbContext.SaveChangesAsync(cancellationToken);
        return processed;
    }
}