using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using EntKube.Web.Client.Pages;
using EntKube.Web.Components;
using EntKube.Web.Components.Account;
using EntKube.Web.Data;

namespace EntKube.Web;

public class Program
{
    public static void Main(string[] args)
    {
        WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

        // Register Blazor components with both server and WebAssembly interactivity.
        // This BFF hosts the UI and proxies API calls to the backend microservices.

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

        builder.Services.AddCascadingAuthenticationState();
        builder.Services.AddScoped<IdentityRedirectManager>();
        builder.Services.AddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();

        // Configure ASP.NET Identity authentication using cookie-based schemes.

        builder.Services.AddAuthentication(options =>
            {
                options.DefaultScheme = IdentityConstants.ApplicationScheme;
                options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
            })
            .AddIdentityCookies();

        // Register the database context. SQLite for local development.

        string connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
            ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");

        builder.Services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlite(connectionString));

        builder.Services.AddDatabaseDeveloperPageExceptionFilter();

        // Set up ASP.NET Identity with EF Core stores.

        builder.Services.AddIdentityCore<ApplicationUser>(options =>
            {
                options.SignIn.RequireConfirmedAccount = true;
                options.Stores.SchemaVersion = IdentitySchemaVersions.Version3;
            })
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddSignInManager()
            .AddDefaultTokenProviders();

        builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();

        // Register HTTP clients for communicating with backend microservices.
        // Each service has its own base URL configured via appsettings.

        builder.Services.AddHttpClient("ClustersApi", client =>
        {
            client.BaseAddress = new Uri(builder.Configuration["Services:Clusters:BaseUrl"] ?? "https://localhost:5010");
        });

        builder.Services.AddHttpClient("ProvisioningApi", client =>
        {
            client.BaseAddress = new Uri(builder.Configuration["Services:Provisioning:BaseUrl"] ?? "https://localhost:5020");
        });

        builder.Services.AddHttpClient("IdentityApi", client =>
        {
            client.BaseAddress = new Uri(builder.Configuration["Services:Identity:BaseUrl"] ?? "https://localhost:5030");
        });

        WebApplication app = builder.Build();

        // Apply pending database migrations on startup with retry logic.

        MigrateDatabase(app);

        // Configure the HTTP request pipeline.

        if (app.Environment.IsDevelopment())
        {
            app.UseWebAssemblyDebugging();
            app.UseMigrationsEndPoint();
        }
        else
        {
            app.UseExceptionHandler("/Error", createScopeForErrors: true);
            app.UseHsts();
        }

        app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
        app.UseHttpsRedirection();

        app.UseAntiforgery();

        app.MapStaticAssets();

        app.MapRazorComponents<App>()
            .AddInteractiveServerRenderMode()
            .AddInteractiveWebAssemblyRenderMode()
            .AddAdditionalAssemblies(typeof(EntKube.Web.Client._Imports).Assembly);

        // Add additional endpoints required by the Identity /Account Razor components.

        app.MapAdditionalIdentityEndpoints();

        app.Run();
    }

    /// <summary>
    /// Applies any pending EF Core migrations when the application starts.
    /// Retries with exponential backoff for containerized environments where
    /// the database may not be immediately available.
    /// </summary>
    private static void MigrateDatabase(WebApplication app)
    {
        using IServiceScope scope = app.Services.CreateScope();
        ApplicationDbContext db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
        ILogger<Program> logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();

        int maxRetries = 5;
        int delayMs = 1000;

        for (int attempt = 1; attempt <= maxRetries; attempt++)
        {
            try
            {
                db.Database.Migrate();
                logger.LogInformation("Database migrations applied successfully on attempt {Attempt}.", attempt);
                return;
            }
            catch (Exception ex) when (attempt < maxRetries)
            {
                logger.LogWarning(
                    ex,
                    "Database migration attempt {Attempt}/{MaxRetries} failed. Retrying in {DelayMs}ms...",
                    attempt,
                    maxRetries,
                    delayMs);

                Thread.Sleep(delayMs);
                delayMs *= 2;
            }
        }
    }
}
