using Microsoft.EntityFrameworkCore;
using Nexus.Api.Data;
using Nexus.Api.Helpers;
using Nexus.Api.Middleware;
using Nexus.Api.Services;

namespace Nexus.Api.Extensions;

/// <summary>
/// Extension methods for configuring the Nexus application pipeline and startup.
/// </summary>
public static class ApplicationBuilderExtensions
{
    /// <summary>
    /// Applies pending EF Core migrations and seeds the initial owner account if none exist.
    /// Uses a <see cref="SeedAudit"/> guard so the owner is never re-created even if all users
    /// are deleted — the DB is the single source of truth for the owner password after first seed.
    ///
    /// Single-transaction guarantee: if the seed block is entered at all (user creation needed
    /// or just the audit-log write), the SeedAudit row is written inside the same transaction
    /// so that a crash mid-way can never leave the DB in a re-seedable state.
    /// </summary>
    public static async Task EnsureDatabaseAsync(this WebApplication app)
    {
        var configuration = app.Configuration;

        await using (var scope = app.Services.CreateAsyncScope())
        {
            var db = scope.ServiceProvider.GetRequiredService<NexusDbContext>();
            await db.Database.MigrateAsync();

            const string seedKey = "owner_created";
            var alreadySeeded = await db.SeedAudits.AnyAsync(s => s.Key == seedKey);
            if (alreadySeeded)
                return;

            var ownerEmail = configuration["Bootstrap:OwnerEmail"]?.Trim().ToLowerInvariant();
            var ownerPassword = configuration["Bootstrap:OwnerPassword"];
            var hasUsers = await db.Users.AnyAsync();

            // ── Double-check SeedAudit after the migration — if another pod wrote it
            //    while we were reading, bail out early. ──
            alreadySeeded = await db.SeedAudits.AnyAsync(s => s.Key == seedKey);
            if (alreadySeeded)
                return;

            // ── Use a strategy-based transaction so the user + audit row are
            //    persisted atomically. If the DB crashes after SaveChanges the
            //    entire transaction is rolled back, preventing partial-seed states.
            var strategy = db.Database.CreateExecutionStrategy();
            await strategy.ExecuteAsync(async () =>
            {
                await using var tx = await db.Database.BeginTransactionAsync();

                if (!hasUsers)
                {
                    if (string.IsNullOrWhiteSpace(ownerEmail))
                        throw new InvalidOperationException("Bootstrap:OwnerEmail is required for initial setup.");
                    if (string.IsNullOrWhiteSpace(ownerPassword) || ownerPassword.Length < 10)
                        throw new InvalidOperationException(
                            "Bootstrap:OwnerPassword is required for initial setup and must contain at least 10 characters.");

                    var initialDisplayName = PasswordHelper.BuildOwnerDisplayName(ownerEmail);

                    db.Users.Add(new NexusUser
                    {
                        Email = ownerEmail,
                        NormalizedEmail = AuthService.NormalizeEmail(ownerEmail),
                        DisplayName = initialDisplayName,
                        PasswordHash = PasswordSecurity.Hash(ownerPassword),
                        Role = "owner"
                    });
                }

                // Record the seed attempt regardless of whether users already existed.
                // This prevents re-seeding even if the Users table is wiped.
                db.SeedAudits.Add(new SeedAudit { Key = seedKey });
                await db.SaveChangesAsync();
                await tx.CommitAsync();
            });
        }
    }

    /// <summary>
    /// Configures the HTTP middleware pipeline: forwarded headers, rate limiting, auth, security headers, and Swagger in development.
    /// </summary>
    public static IApplicationBuilder UseNexusPipeline(this IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseForwardedHeaders();
        app.UseExceptionHandler();
        app.UseStatusCodePages();
        app.UseRateLimiter();
        app.UseApiKeyAuthentication();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseSecurityHeaders();

        if (env.IsDevelopment())
        {
            app.UseSwagger();
            app.UseSwaggerUI();
        }

        return app;
    }
}
