using Microsoft.Extensions.Options;
using Tiku.Application.Jobs;
using Tiku.Application.Security;
using Tiku.Application.Tenancy;
using Tiku.Infrastructure.Messaging;

namespace Tiku.Worker;

public class Worker(
    IServiceScopeFactory scopeFactory,
    IOptions<DomainLifecycleOptions> options,
    MessagingOptions messagingOptions,
    ILogger<Worker> logger) : BackgroundService
{
    private readonly string workerId = $"{Environment.MachineName}:{Guid.NewGuid():N}";

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                var domainCount = await ProcessTenantDomainsAsync(stoppingToken);
                var jobCount = await ProcessBackgroundJobsAsync(stoppingToken);
                if (domainCount > 0 || jobCount > 0)
                {
                    logger.LogInformation(
                        "Worker processed {DomainCount} pending tenant domains and {JobCount} background jobs.",
                        domainCount,
                        jobCount);
                }
            }
            catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
            {
                break;
            }
            catch (Exception exception)
            {
                logger.LogError(exception, "Tenant domain lifecycle processing failed.");
            }

            await Task.Delay(
                TimeSpan.FromSeconds(Math.Clamp(options.Value.PollSeconds, 10, 3600)),
                stoppingToken);
        }
    }

    private async Task<int> ProcessTenantDomainsAsync(CancellationToken stoppingToken)
    {
        await using var scope = scopeFactory.CreateAsyncScope();
        scope.ServiceProvider.GetRequiredService<ITenantContextInitializer>()
            .InitializeSystem(null, "Tenant domain DNS and TLS lifecycle worker");
        return await scope.ServiceProvider
            .GetRequiredService<ITenantDomainLifecycleService>()
            .ProcessPendingAsync(stoppingToken);
    }

    private async Task<int> ProcessBackgroundJobsAsync(CancellationToken stoppingToken)
    {
        await using var scope = scopeFactory.CreateAsyncScope();
        scope.ServiceProvider.GetRequiredService<ITenantContextInitializer>()
            .InitializeSystem(null, "Background job lease worker");
        return await scope.ServiceProvider
            .GetRequiredService<IBackgroundJobService>()
            .ProcessPendingAsync(
                workerId,
                20,
                includeImmediateJobs: !messagingOptions.IsConfigured,
                cancellationToken: stoppingToken);
    }
}
