using ZB.MOM.WW.Configuration;
using ZB.MOM.WW.Health;
using ZB.MOM.WW.Overview.Components;
using ZB.MOM.WW.Overview.Observability;
using ZB.MOM.WW.Overview.Polling;
using ZB.MOM.WW.Overview.Registry;
using ZB.MOM.WW.Telemetry;
using ZB.MOM.WW.Telemetry.Serilog;

var builder = WebApplication.CreateBuilder(args);

// The host calls this itself, but ONLY when the environment is literally "Development". Running the
// build output under any other name — checking a staging registry locally, say — leaves the Theme
// RCL's assets under _content/ unresolvable, and the page renders as a blank white screen with its
// markup fully present and every stylesheet 404. Calling it unconditionally is a no-op once
// published (the manifest is gone and the assets are real files on disk), so the only thing it
// changes is that failure.
builder.WebHost.UseStaticWebAssets();

// Pre-host presence check. The validator below covers shape and consistency, but it only runs once
// the options are resolved — a registry section that is missing entirely binds to an empty
// OverviewOptions, and the failure a reader can act on is "you configured no registry", not
// "Applications must have at least 1 entry". Fail here, before the host is built, with the key name.
ConfigPreflight.For(builder.Configuration)
    .RequireValue($"{OverviewOptions.SectionName}:Applications:0:Name")
    .RequireValue($"{OverviewOptions.SectionName}:Applications:0:Instances:0:BaseUrl")
    .ThrowIfInvalid();

builder.Services.AddValidatedOptions<OverviewOptions, OverviewOptionsValidator>(
    builder.Configuration, OverviewOptions.SectionName);

builder.AddZbSerilog(o => o.ServiceName = "overview");

builder.AddZbTelemetry(o =>
{
    o.ServiceName = "overview";

    // Meters is an ALLOWLIST, not a filter: a Meter absent from this array is never registered with
    // the MeterProvider, and its instruments vanish from /metrics with no warning anywhere. The
    // dashboard's own gauge is the only thing here that would go missing silently.
    o.Meters = [OverviewMetrics.MeterName];
});

builder.Services.AddOverviewPolling();
builder.Services.AddOverviewHealthChecks();

builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

var app = builder.Build();

app.UseStaticFiles();

// No UseAuthentication/UseAuthorization and no cascading auth state: the dashboard is anonymous and
// read-only by requirement (design §2). It renders health data that every one of these apps already
// serves unauthenticated, and it has no endpoint that mutates anything.
//
// UseAntiforgery IS required despite that. AddRazorComponents stamps antiforgery metadata onto every
// component endpoint unconditionally, and the endpoint middleware hard-fails a request whose endpoint
// carries that metadata with no middleware to honour it — so without this line every page returns
// 500. The alternative, MapRazorComponents(...).DisableAntiforgery(), would also boot, but it turns
// the first form anyone ever adds into a silent CSRF hole. This app has no forms, so the middleware
// is inert; it is here so that stays true by default rather than by vigilance.
app.UseAntiforgery();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

// The dashboard's own three-tier health surface and Prometheus endpoint. Both are anonymous, like
// every /health/* endpoint this app polls — which also means one Overview instance can be pointed
// at another and would render it correctly.
app.MapZbHealth();
app.MapZbMetrics();

await app.RunAsync();

/// <summary>
/// Exposed so <c>WebApplicationFactory&lt;Program&gt;</c> can boot the real host in tests
/// (top-level statements otherwise compile to an internal entry-point class).
/// </summary>
public partial class Program;
