using Ablera.Serdica.DBModels.Oidc;
using Ablera.Serdica.Authority.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
using System.Threading;
using System;
using Microsoft.EntityFrameworkCore;
using Polly;

public class OidcInfrastructureHostedService(
        ILogger<OidcInfrastructureHostedService> logger,
        IServiceScopeFactory scopeFactory) : IHostedService, IDisposable
{
    private readonly TimeSpan _updateInterval = TimeSpan.FromMinutes(2);

    private Timer? _timer;
    private CancellationTokenSource? _stoppingCts;


    public async Task StartAsync(CancellationToken cancellationToken)
    {
        logger.LogInformation($"{nameof(OidcInfrastructureHostedService)} service starting...");

        // Apply migrations
        using var scope = scopeFactory.CreateScope();
        using var dbContext = scope.ServiceProvider.GetRequiredService<OidcDbContext>();
        await dbContext.Database.CreateExecutionStrategy().ExecuteAsync(async () =>
        {
            await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken);
            await dbContext.Database.MigrateAsync(cancellationToken);
            await transaction.CommitAsync(cancellationToken);
        });
        // Create a CTS that links the ASP.NET shutdown token with our own
        _stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

        // Schedule the first run immediately
        _ = RunOnceAsync(_stoppingCts.Token);

        // Then schedule recurring runs. Notice we capture the CTS token.
        _timer = new Timer(
            _ => _ = RunOnceAsync(_stoppingCts.Token),
            state: null,
            dueTime: _updateInterval,
            period: _updateInterval);
    }

    private async Task RunOnceAsync(CancellationToken token)
    {
        try
        {
            // Honor cancellation right at the top
            token.ThrowIfCancellationRequested();

            using var scope = scopeFactory.CreateScope();
            using var context = scope.ServiceProvider.GetRequiredService<OidcDbContext>();
            var sync = scope.ServiceProvider.GetRequiredService<OidcClientSynchronizer>();

            // Do the synchronization
            await sync.SynchronizeAsync(token);
        }
        catch (OperationCanceledException)
        {
            // Expected on shutdown; swallow.
        }
        catch (Exception ex)
        {
            logger.LogError(ex, $"Error while synchronizing {nameof(OidcInfrastructureHostedService)}.");
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        logger.LogInformation($"{nameof(OidcInfrastructureHostedService)} service stopping...");

        // Signal cancellation to the RunOnceAsync calls
        _stoppingCts?.Cancel();

        // Stop the timer from firing any more
        _timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _timer?.Dispose();
        _stoppingCts?.Dispose();
    }
}
