using System.Threading.RateLimiting;
using Hangfire;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.RateLimiting;
using Microsoft.EntityFrameworkCore;
using Serilog;
using RealCV.Application.Interfaces;
using RealCV.Domain.Enums;
using RealCV.Infrastructure;
using RealCV.Infrastructure.Data;
using RealCV.Infrastructure.Identity;
using RealCV.Web;
using RealCV.Web.Components;
using RealCV.Web.Services;

// Configure Serilog
Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateBootstrapLogger();

try
{
    Log.Information("Starting RealCV web application");

    var builder = WebApplication.CreateBuilder(args);

    // Configure Serilog from appsettings
    builder.Host.UseSerilog((context, services, configuration) => configuration
        .ReadFrom.Configuration(context.Configuration)
        .ReadFrom.Services(services)
        .Enrich.FromLogContext());

    // Add services to the container.
    builder.Services.AddRazorComponents()
        .AddInteractiveServerComponents();

    // Add Infrastructure services (DbContext, Hangfire, HttpClients, Services)
    builder.Services.AddInfrastructure(builder.Configuration);

    // Add Web services
    builder.Services.AddScoped<IPdfReportService, PdfReportService>();

    // Add Identity with secure password requirements
    builder.Services.AddIdentity<ApplicationUser, IdentityRole<Guid>>(options =>
    {
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireUppercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequiredLength = 12;
        options.Password.RequiredUniqueChars = 4;
        options.SignIn.RequireConfirmedAccount = false;
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;
    })
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

    builder.Services.ConfigureApplicationCookie(options =>
    {
        options.LoginPath = "/account/login";
        options.LogoutPath = "/account/logout";
        options.AccessDeniedPath = "/account/login";
    });

    // Add Cascading Authentication State
    builder.Services.AddCascadingAuthenticationState();
    builder.Services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<ApplicationUser>>();

    // Add health checks
    builder.Services.AddHealthChecks()
        .AddDbContextCheck<ApplicationDbContext>("database");

    // Add rate limiting for login endpoint
    builder.Services.AddRateLimiter(options =>
    {
        options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
        options.AddFixedWindowLimiter("login", limiterOptions =>
        {
            limiterOptions.PermitLimit = 5;
            limiterOptions.Window = TimeSpan.FromMinutes(1);
            limiterOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
            limiterOptions.QueueLimit = 0;
        });
    });

    var app = builder.Build();

    // Seed default admin user and clear company cache
    using (var scope = app.Services.CreateScope())
    {
        var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
        var defaultEmail = builder.Configuration["DefaultAdmin:Email"];
        var defaultPassword = builder.Configuration["DefaultAdmin:Password"];

        if (!string.IsNullOrEmpty(defaultEmail) && !string.IsNullOrEmpty(defaultPassword))
        {
            if (await userManager.FindByEmailAsync(defaultEmail) == null)
            {
                var adminUser = new ApplicationUser
                {
                    UserName = defaultEmail,
                    Email = defaultEmail,
                    EmailConfirmed = true
                };
                var result = await userManager.CreateAsync(adminUser, defaultPassword);
                if (result.Succeeded)
                {
                    Log.Information("Created default admin user: {Email}", defaultEmail);
                }
                else
                {
                    Log.Warning("Failed to create admin user: {Errors}", string.Join(", ", result.Errors.Select(e => e.Description)));
                }
            }
        }
        else
        {
            Log.Information("No default admin credentials configured - skipping admin user seeding");
        }

        // Clear company cache on startup to ensure fresh API lookups
        var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
        var cacheCount = await dbContext.CompanyCache.CountAsync();
        if (cacheCount > 0)
        {
            dbContext.CompanyCache.RemoveRange(dbContext.CompanyCache);
            await dbContext.SaveChangesAsync();
            Log.Information("Cleared {Count} entries from company cache", cacheCount);
        }
    }

    // Configure the HTTP request pipeline.
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Error", createScopeForErrors: true);
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }

    // Only use HTTPS redirection when not running in Docker/container
    if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER")))
    {
        // Running in container - skip HTTPS redirect (handled by reverse proxy)
    }
    else
    {
        app.UseHttpsRedirection();
    }

    app.UseStaticFiles();
    app.UseAntiforgery();

    // Add Serilog request logging
    app.UseSerilogRequestLogging();

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

    // Add Hangfire Dashboard (only in development, requires authentication)
    if (app.Environment.IsDevelopment())
    {
        app.UseHangfireDashboard("/hangfire", new DashboardOptions
        {
            Authorization = [new HangfireAuthorizationFilter()]
        });
    }

    // Schedule recurring jobs
    var recurringJobManager = app.Services.GetRequiredService<IRecurringJobManager>();

    // Reset monthly usage at 00:05 UTC daily
    recurringJobManager.AddOrUpdate<RealCV.Infrastructure.Jobs.ResetMonthlyUsageJob>(
        "reset-monthly-usage",
        job => job.ExecuteAsync(CancellationToken.None),
        Cron.Daily(0, 5));

    // GDPR: Run data retention cleanup at 02:00 UTC daily
    recurringJobManager.AddOrUpdate<RealCV.Infrastructure.Jobs.DataRetentionJob>(
        "gdpr-data-retention",
        job => job.ExecuteAsync(CancellationToken.None),
        Cron.Daily(2, 0));

    // Login endpoint
    app.MapPost("/account/perform-login", async (
        HttpContext context,
        SignInManager<ApplicationUser> signInManager) =>
    {
        var form = await context.Request.ReadFormAsync();
        var email = form["email"].ToString();
        var password = form["password"].ToString();
        var rememberMe = form["rememberMe"].ToString() == "true";
        var returnUrl = form["returnUrl"].ToString();

        Log.Information("Login attempt for {Email}", email);

        // Validate returnUrl is local to prevent open redirect attacks
        if (string.IsNullOrEmpty(returnUrl) || !Uri.IsWellFormedUriString(returnUrl, UriKind.Relative) || returnUrl.StartsWith("//"))
        {
            returnUrl = "/dashboard";
        }

        var result = await signInManager.PasswordSignInAsync(email, password, rememberMe, lockoutOnFailure: true);

        if (result.Succeeded)
        {
            Log.Information("User {Email} logged in successfully", email);
            return Results.LocalRedirect(returnUrl);
        }
        else if (result.IsLockedOut)
        {
            Log.Warning("User {Email} account is locked out", email);
            return Results.Redirect("/account/login?error=Account+locked.+Try+again+later.");
        }
        else
        {
            Log.Warning("Failed login attempt for {Email}", email);
            return Results.Redirect("/account/login?error=Invalid+email+or+password.");
        }
    }).RequireRateLimiting("login");

    // Logout endpoint
    app.MapPost("/account/logout", async (SignInManager<ApplicationUser> signInManager) =>
    {
        await signInManager.SignOutAsync();
        return Results.Redirect("/");
    }).RequireAuthorization();

    // Health check endpoint
    app.MapHealthChecks("/health");

    // Stripe webhook endpoint (must be anonymous - called by Stripe)
    app.MapPost("/api/stripe/webhook", async (
        HttpContext context,
        IStripeService stripeService) =>
    {
        var json = await new StreamReader(context.Request.Body).ReadToEndAsync();
        var signature = context.Request.Headers["Stripe-Signature"].FirstOrDefault();

        if (string.IsNullOrEmpty(signature))
        {
            Log.Warning("Stripe webhook received without signature");
            return Results.BadRequest("Missing Stripe-Signature header");
        }

        try
        {
            await stripeService.HandleWebhookAsync(json, signature);
            return Results.Ok();
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Error processing Stripe webhook");
            return Results.BadRequest("Webhook processing failed");
        }
    });

    // Create checkout session endpoint
    app.MapPost("/api/billing/create-checkout", async (
        HttpContext context,
        IStripeService stripeService,
        UserManager<ApplicationUser> userManager,
        IUserContextService userContext) =>
    {
        var userId = await userContext.GetCurrentUserIdAsync();
        if (!userId.HasValue)
        {
            return Results.Unauthorized();
        }

        var user = await userManager.FindByIdAsync(userId.Value.ToString());
        if (user == null)
        {
            return Results.NotFound("User not found");
        }

        var form = await context.Request.ReadFormAsync();
        var planString = form["plan"].ToString();

        if (!Enum.TryParse<UserPlan>(planString, out var targetPlan) ||
            targetPlan == UserPlan.Free)
        {
            return Results.BadRequest("Invalid plan");
        }

        var baseUrl = $"{context.Request.Scheme}://{context.Request.Host}";
        var successUrl = $"{baseUrl}/checkout-success";
        var cancelUrl = $"{baseUrl}/pricing";

        try
        {
            var checkoutUrl = await stripeService.CreateCheckoutSessionAsync(
                userId.Value,
                user.Email!,
                targetPlan,
                successUrl,
                cancelUrl);

            return Results.Redirect(checkoutUrl);
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Failed to create checkout session for user {UserId}", userId);
            return Results.Redirect("/pricing?error=checkout_failed");
        }
    }).RequireAuthorization();

    // Customer portal endpoint
    app.MapPost("/api/billing/portal", async (
        HttpContext context,
        IStripeService stripeService,
        UserManager<ApplicationUser> userManager,
        IUserContextService userContext) =>
    {
        var userId = await userContext.GetCurrentUserIdAsync();
        if (!userId.HasValue)
        {
            return Results.Unauthorized();
        }

        var user = await userManager.FindByIdAsync(userId.Value.ToString());
        if (user == null || string.IsNullOrEmpty(user.StripeCustomerId))
        {
            return Results.BadRequest("No billing account found");
        }

        var baseUrl = $"{context.Request.Scheme}://{context.Request.Host}";
        var returnUrl = $"{baseUrl}/account/billing";

        try
        {
            var portalUrl = await stripeService.CreateCustomerPortalSessionAsync(
                user.StripeCustomerId,
                returnUrl);

            return Results.Redirect(portalUrl);
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Failed to create portal session for user {UserId}", userId);
            return Results.Redirect("/account/billing?error=portal_failed");
        }
    }).RequireAuthorization();

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

    app.Run();
}
catch (Exception ex)
{
    Log.Fatal(ex, "Application terminated unexpectedly");
}
finally
{
    Log.CloseAndFlush();
}
