using DodoSSH.Api.Authorization;
using DodoSSH.Api.Features.Events;
using DodoSSH.Api.Features.Identity;
using DodoSSH.Api.Features.Sync;
using DodoSSH.Api.Features.Teams;
using DodoSSH.Api.Setup;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Policy;

var builder = WebApplication.CreateBuilder(args);

// Docker secrets, if mounted. Optional so the same image works with plain environment variables.
builder.Configuration.AddKeyPerFile("/run/secrets", optional: true);
builder.Configuration.AddEnvironmentVariables(prefix: "DODOSSH_");

builder.Services.AddDodoJson();
builder.Services.AddDodoOptions();
builder.Services.AddDodoPersistence(builder.Configuration);
builder.Services.AddDodoAuthentication();
builder.Services.AddDodoEndpoints();
builder.Services.AddDodoOpenApi();
builder.Services.AddDodoHealthChecks();

// DateTime.UtcNow is banned repo-wide (see BannedSymbols.txt); everything takes
// TimeProvider so time can be faked in tests.
builder.Services.AddSingleton(TimeProvider.System);

// FastEndpoints registers this too. Kept because CurrentUserContext needs it on its own merits, and
// the registration would be silently lost the day the endpoint framework changed again.
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ICurrentUserContext, CurrentUserContext>();
builder.Services.AddScoped<IVaultAccessService, VaultAccessService>();
builder.Services.AddScoped<SyncService>();
builder.Services.AddScoped<IdentityService>();
builder.Services.AddScoped<EnrollmentService>();
builder.Services.AddScoped<DeviceService>();
builder.Services.AddScoped<DirectoryService>();
builder.Services.AddScoped<KeyLogService>();
builder.Services.AddScoped<TeamService>();
builder.Services.AddScoped<VaultGrantService>();
builder.Services.AddScoped<IIdentityBindingVerifier, IdentityBindingVerifier>();
builder.Services.AddSingleton<ICursorKeyProvider, CursorKeyProvider>();

// A singleton, because the sockets it holds outlive the requests that opened them. Registered twice
// resolving to the same instance: the endpoint needs the whole hub — admit, remove, count — while the
// write paths that announce a change need only the two methods that announce one, and should not gain
// a reference to connection management to get them.
builder.Services.AddSingleton<VaultEventHub>();
builder.Services.AddSingleton<IVaultEventPublisher>(
    provider => provider.GetRequiredService<VaultEventHub>());

// Scoped rather than the AddAuthorization default of singleton: the handler reads the request's
// DbContext, and a singleton would capture one for the lifetime of the process.
builder.Services.AddScoped<IAuthorizationHandler, EnrolledHandler>();

// Replaces the framework default, which is also registered as a singleton. Last registration wins.
builder.Services.AddSingleton<IAuthorizationMiddlewareResultHandler, DodoAuthorizationResultHandler>();

builder.Services.AddProblemDetails();

var app = builder.Build();

// Deliberately no UseHttpsRedirection: the API is always fronted by a reverse proxy
// (Caddy in the reference compose stack) which terminates TLS. Redirecting here
// produces redirect loops behind a proxy. HTTPS in development comes from the
// launch profile instead.

app.BlockFastEndpointsRouteTable();

// Before the authentication middleware, because the upgrade handshake has to survive it: the events
// endpoint answers an ordinary authenticated request that happens to become a socket, and without
// this the upgrade is never offered and the handler sees a plain GET. No allow-list of origins is
// configured, deliberately — every client here is a native application sending a bearer token, so
// there is no browser origin to trust and nothing a cross-site request could reach without one.
app.UseWebSockets();

app.UseAuthentication();
app.UseAuthorization();

app.MapDodoHealthChecks();
app.MapDodoEndpoints();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.WarnOnRiskyConfiguration();

// Before the port opens, not after. See Persistence.MigrateDodoDatabaseAsync.
await app.MigrateDodoDatabaseAsync().ConfigureAwait(false);

await app.RunAsync().ConfigureAwait(false);
