using System.Globalization;
using System.Security.Claims;
using NLog.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.DependencyInjection;
using Couchbase.Lite;
using WorkTracker.Components;
using WorkTracker.Configuration;
using WorkTracker.Services.Auth;
using WorkTracker.Services.Festivities;
using WorkTracker.Services.Settings;
using WorkTracker.Services.Storage;
using WorkTracker.Services.WorkDays;

var builder = WebApplication.CreateBuilder(args);

builder.Logging.ClearProviders();
builder.Host.UseNLog();

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

builder.Services.AddCascadingAuthenticationState();
builder.Services.AddAuthorization();

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/login";
        options.LogoutPath = "/logout";
        options.AccessDeniedPath = "/login";
        options.SlidingExpiration = true;
        options.ExpireTimeSpan = TimeSpan.FromDays(14);
    });

builder.Services.AddLocalization();

builder.Services.Configure<CouchbaseLiteOptions>(builder.Configuration.GetSection(CouchbaseLiteOptions.SectionName));
builder.Services.Configure<SingleUserOptions>(builder.Configuration.GetSection(SingleUserOptions.SectionName));

builder.Services.AddSingleton<CouchbaseLiteDatabaseProvider>();
builder.Services.AddScoped<IAppSettingsService, CouchbaseLiteAppSettingsService>();
builder.Services.AddSingleton<IAuthService, CouchbaseLiteAuthService>();
builder.Services.AddSingleton<IItalianFestivitySource, ItalianFestivitySource>();
builder.Services.AddScoped<IWorkDayService, CouchbaseLiteWorkDayService>();
builder.Services.AddHostedService<SingleUserSeedService>();

var app = builder.Build();

var italianCulture = new CultureInfo("it-IT");
CultureInfo.DefaultThreadCurrentCulture = italianCulture;
CultureInfo.DefaultThreadCurrentUICulture = italianCulture;

var localizationOptions = new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture(italianCulture),
    SupportedCultures = [italianCulture],
    SupportedUICultures = [italianCulture]
};

// 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();
}

var useHttpsRedirection = app.Configuration.GetValue("UseHttpsRedirection", !app.Environment.IsDevelopment());
if (useHttpsRedirection)
{
    app.UseHttpsRedirection();
}

app.UseStaticFiles();

app.UseRequestLocalization(localizationOptions);

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

app.UseAntiforgery();

app.MapPost("/api/login", async (HttpContext context) =>
{
    var authService = context.RequestServices.GetRequiredService<IAuthService>();
    var logger = context.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger("Auth.Login");
    var form = await context.Request.ReadFormAsync();
    var username = form["username"].ToString();
    var password = form["password"].ToString();
    var returnUrl = form["returnUrl"].ToString();

    if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
    {
        context.Response.Redirect($"/login?error=Missing%20credentials&returnUrl={Uri.EscapeDataString(returnUrl)}");
        return;
    }

    var user = await authService.ValidateCredentialsAsync(username, password, context.RequestAborted);
    if (user is null)
    {
        logger.LogWarning("Login failed for username {Username}", username);
        context.Response.Redirect($"/login?error=Invalid%20credentials&returnUrl={Uri.EscapeDataString(returnUrl)}");
        return;
    }

    var claims = new List<Claim>
    {
        new(ClaimTypes.NameIdentifier, user.Id),
        new(ClaimTypes.Name, user.Username)
    };

    var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme));
    await context.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);

    // If the seeded or existing user must change password, redirect to change-password flow
    if (user.MustChangePassword)
    {
        context.Response.Redirect("/change-password");
        return;
    }

    var safeReturnUrl = string.IsNullOrWhiteSpace(returnUrl) || !Uri.IsWellFormedUriString(returnUrl, UriKind.Relative)
        ? "/"
        : returnUrl;

    context.Response.Redirect(safeReturnUrl);
    return;
}).DisableAntiforgery();

app.MapPost("/api/logout", async (HttpContext context) =>
{
    await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    context.Response.Redirect("/login");
    return;
}).DisableAntiforgery();

app.MapPost("/api/change-password", async (HttpContext context) =>
{
    var authService = context.RequestServices.GetRequiredService<IAuthService>();
    if (!context.User?.Identity?.IsAuthenticated ?? true)
    {
        await context.ChallengeAsync();
        return;
    }

    var form = await context.Request.ReadFormAsync();
    var newPassword = form["newPassword"].ToString();
    var confirm = form["confirmPassword"].ToString();

    if (string.IsNullOrWhiteSpace(newPassword) || string.IsNullOrWhiteSpace(confirm) || newPassword != confirm)
    {
        context.Response.Redirect("/change-password?error=Passwords%20do%20not%20match");
        return;
    }

    var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (string.IsNullOrWhiteSpace(userId))
    {
        await context.ChallengeAsync();
        return;
    }

    var changed = await authService.ChangePasswordAsync(userId, newPassword, context.RequestAborted);
    if (!changed)
    {
        context.Response.Redirect("/change-password?error=Unable%20to%20change%20password");
        return;
    }

    context.Response.Redirect("/");
    return;
}).DisableAntiforgery();

// Development-only endpoint to reset the seeded Admin password (protected by secret in URL)
if (app.Environment.IsDevelopment())
{
    app.MapGet("/debug/reset-admin/{secret}", (HttpContext context, string secret) =>
    {
        const string expected = "81f58012-fe0b-4dcf-a638-77f9b99f92e3";
        if (!string.Equals(secret, expected, StringComparison.Ordinal))
        {
            return Results.StatusCode(403);
        }

        var provider = context.RequestServices.GetRequiredService<CouchbaseLiteDatabaseProvider>();
        var opts = context.RequestServices.GetRequiredService<IOptions<SingleUserOptions>>();

        var username = opts.Value.Username.Trim();
        var id = username.Trim().ToUpperInvariant();

        // Hash the configured seed password
        var passwordHasher = new Microsoft.AspNetCore.Identity.PasswordHasher<WorkTracker.Services.Auth.AuthUser>();
        var tempUser = new WorkTracker.Services.Auth.AuthUser { Id = id, Username = username, UsernameNormalized = id };
        var newHash = passwordHasher.HashPassword(tempUser, opts.Value.Password);

        var doc = provider.Users.GetDocument(id);
        if (doc is null)
        {
            var mutable = new MutableDocument(id);
            mutable.SetString("username", username);
            mutable.SetString("usernameNormalized", id);
            mutable.SetString("passwordHash", newHash);
            mutable.SetBoolean("mustChangePassword", true);
            provider.Users.Save(mutable);
        }
        else
        {
            var mutable = doc.ToMutable();
            mutable.SetString("passwordHash", newHash);
            mutable.SetBoolean("mustChangePassword", true);
            provider.Users.Save(mutable);
        }

        return Results.Json(new { ok = true, id, username });
    });

    app.MapGet("/debug/users/{secret}", (HttpContext context, string secret) =>
    {
        const string expected = "81f58012-fe0b-4dcf-a638-77f9b99f92e3";
        if (!string.Equals(secret, expected, StringComparison.Ordinal))
        {
            return Results.StatusCode(403);
        }

        var provider = context.RequestServices.GetRequiredService<CouchbaseLiteDatabaseProvider>();
        var opts = context.RequestServices.GetRequiredService<IOptions<SingleUserOptions>>();

        var username = opts.Value.Username.Trim();
        var id = username.Trim().ToUpperInvariant();

        var doc = provider.Users.GetDocument(id);
        if (doc is null)
        {
            return Results.NotFound();
        }

        var result = new
        {
            id = doc.Id,
            username = doc.GetString("username") ?? doc.GetString("email"),
            usernameNormalized = doc.GetString("usernameNormalized") ?? doc.GetString("emailNormalized"),
            passwordHash = doc.GetString("passwordHash"),
            mustChangePassword = doc.GetBoolean("mustChangePassword")
        };

        return Results.Json(result);
    });
}

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

app.Run();
