using FrameProcessor.Configuration;
using FrameProcessor.ImagePipeline;
using FrameProcessor.Mqtt;
using FrameProcessor.Storage;

var builder = WebApplication.CreateBuilder(args);

builder.Configuration.AddJsonFile("frames.json", optional: false, reloadOnChange: true);

builder.Services.AddControllers();

builder.Services.AddOptions<MqttOptions>()
    .Bind(builder.Configuration.GetSection(MqttOptions.SectionName))
    .ValidateDataAnnotations()
    .ValidateOnStart();

builder.Services.AddOptions<StorageOptions>()
    .Bind(builder.Configuration.GetSection(StorageOptions.SectionName))
    .ValidateDataAnnotations()
    .ValidateOnStart();

builder.Services.AddOptions<UrlFetchOptions>()
    .Bind(builder.Configuration.GetSection(UrlFetchOptions.SectionName))
    .ValidateDataAnnotations()
    .ValidateOnStart();

builder.Services.AddOptions<ApiKeyOptions>()
    .Configure<IConfiguration>((opts, cfg) => opts.Value = cfg[ApiKeyOptions.SectionName] ?? string.Empty)
    .ValidateDataAnnotations()
    .ValidateOnStart();

// FramesOptions is bound but not validated via the options pipeline so that
// IOptionsMonitor<FramesOptions> can fire OnChange with invalid content during
// hot-reload without throwing. FramesRegistry takes responsibility for both
// strict startup validation (in its constructor) and lenient reload validation.
builder.Services.AddOptions<FramesOptions>()
    .Bind(builder.Configuration);
builder.Services.AddSingleton<FramesOptionsValidator>();
builder.Services.AddSingleton<FramesRegistry>();
builder.Services.AddSingleton<IImagePipeline, FrameProcessor.ImagePipeline.ImagePipeline>();
builder.Services.AddSingleton<ImageStore>();
builder.Services.AddSingleton<MqttPublisher>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<MqttPublisher>());

var app = builder.Build();

// Eagerly resolve FramesRegistry so an invalid frames.json fails startup fast.
_ = app.Services.GetRequiredService<FramesRegistry>();

app.MapControllers();

app.Run();
