using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.DependencyInjection.Extensions;
using StellaOps.Auth.Client;
using StellaOps.Scanner.Worker.Diagnostics;
using StellaOps.Scanner.Worker.Hosting;
using StellaOps.Scanner.Worker.Options;
using StellaOps.Scanner.Worker.Processing;

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddOptions<ScannerWorkerOptions>()
    .BindConfiguration(ScannerWorkerOptions.SectionName)
    .ValidateOnStart();

builder.Services.AddSingleton<IValidateOptions<ScannerWorkerOptions>, ScannerWorkerOptionsValidator>();
builder.Services.AddSingleton(TimeProvider.System);
builder.Services.AddSingleton<ScannerWorkerMetrics>();
builder.Services.AddSingleton<ScanProgressReporter>();
builder.Services.AddSingleton<ScanJobProcessor>();
builder.Services.AddSingleton<LeaseHeartbeatService>();
builder.Services.AddSingleton<IDelayScheduler, SystemDelayScheduler>();

builder.Services.TryAddSingleton<IScanJobSource, NullScanJobSource>();
builder.Services.TryAddSingleton<IScanAnalyzerDispatcher, NullScanAnalyzerDispatcher>();
builder.Services.AddSingleton<IScanStageExecutor, AnalyzerStageExecutor>();

builder.Services.AddSingleton<ScannerWorkerHostedService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<ScannerWorkerHostedService>());

var workerOptions = builder.Configuration.GetSection(ScannerWorkerOptions.SectionName).Get<ScannerWorkerOptions>() ?? new ScannerWorkerOptions();

builder.Services.Configure<HostOptions>(options =>
{
    options.ShutdownTimeout = workerOptions.Shutdown.Timeout;
});

builder.ConfigureScannerWorkerTelemetry(workerOptions);

if (workerOptions.Authority.Enabled)
{
    builder.Services.AddStellaOpsAuthClient(clientOptions =>
    {
        clientOptions.Authority = workerOptions.Authority.Issuer?.Trim() ?? string.Empty;
        clientOptions.ClientId = workerOptions.Authority.ClientId?.Trim() ?? string.Empty;
        clientOptions.ClientSecret = workerOptions.Authority.ClientSecret;
        clientOptions.EnableRetries = workerOptions.Authority.Resilience.EnableRetries ?? true;
        clientOptions.HttpTimeout = TimeSpan.FromSeconds(workerOptions.Authority.BackchannelTimeoutSeconds);

        clientOptions.DefaultScopes.Clear();
        foreach (var scope in workerOptions.Authority.Scopes)
        {
            if (string.IsNullOrWhiteSpace(scope))
            {
                continue;
            }

            clientOptions.DefaultScopes.Add(scope);
        }

        clientOptions.RetryDelays.Clear();
        foreach (var delay in workerOptions.Authority.Resilience.RetryDelays)
        {
            if (delay <= TimeSpan.Zero)
            {
                continue;
            }

            clientOptions.RetryDelays.Add(delay);
        }

        if (workerOptions.Authority.Resilience.AllowOfflineCacheFallback is bool allowOffline)
        {
            clientOptions.AllowOfflineCacheFallback = allowOffline;
        }

        if (workerOptions.Authority.Resilience.OfflineCacheTolerance is { } tolerance && tolerance > TimeSpan.Zero)
        {
            clientOptions.OfflineCacheTolerance = tolerance;
        }
    });
}

builder.Logging.Configure(options =>
{
    options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId
        | ActivityTrackingOptions.TraceId
        | ActivityTrackingOptions.ParentId;
});

var host = builder.Build();

await host.RunAsync();

public partial class Program;
