using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using ScadaLink.Security;

namespace ScadaLink.CentralUI.Auth;

/// <summary>
/// Minimal API endpoints for login/logout. These run outside Blazor Server (standard HTTP POST).
/// On success, signs in via ASP.NET Core cookie authentication and redirects to dashboard.
/// </summary>
public static class AuthEndpoints
{
    public static IEndpointRouteBuilder MapAuthEndpoints(this IEndpointRouteBuilder endpoints)
    {
        endpoints.MapPost("/auth/login", async (HttpContext context) =>
        {
            var form = await context.Request.ReadFormAsync();
            var username = form["username"].ToString();
            var password = form["password"].ToString();

            if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
            {
                context.Response.Redirect("/login?error=Username+and+password+are+required.");
                return;
            }

            var ldapAuth = context.RequestServices.GetRequiredService<LdapAuthService>();
            var jwtService = context.RequestServices.GetRequiredService<JwtTokenService>();
            var roleMapper = context.RequestServices.GetRequiredService<RoleMapper>();

            var authResult = await ldapAuth.AuthenticateAsync(username, password);
            if (!authResult.Success)
            {
                var errorMsg = Uri.EscapeDataString(authResult.ErrorMessage ?? "Authentication failed.");
                context.Response.Redirect($"/login?error={errorMsg}");
                return;
            }

            // Map LDAP groups to roles
            var roleMappingResult = await roleMapper.MapGroupsToRolesAsync(authResult.Groups ?? []);

            var expiresAt = DateTimeOffset.UtcNow.AddMinutes(30);

            // Build claims from LDAP auth + role mapping
            var claims = new List<Claim>
            {
                new(ClaimTypes.Name, authResult.Username ?? username),
                new(JwtTokenService.DisplayNameClaimType, authResult.DisplayName ?? username),
                new(JwtTokenService.UsernameClaimType, authResult.Username ?? username),
                new("expires_at", expiresAt.ToUnixTimeSeconds().ToString()),
            };

            foreach (var role in roleMappingResult.Roles)
            {
                claims.Add(new Claim(JwtTokenService.RoleClaimType, role));
            }

            if (!roleMappingResult.IsSystemWideDeployment)
            {
                foreach (var siteId in roleMappingResult.PermittedSiteIds)
                {
                    claims.Add(new Claim(JwtTokenService.SiteIdClaimType, siteId));
                }
            }

            var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
            var principal = new ClaimsPrincipal(identity);

            await context.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                principal,
                new AuthenticationProperties
                {
                    IsPersistent = true,
                    ExpiresUtc = expiresAt
                });

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

        endpoints.MapPost("/auth/token", async (HttpContext context) =>
        {
            var form = await context.Request.ReadFormAsync();
            var username = form["username"].ToString();
            var password = form["password"].ToString();

            if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
            {
                return Results.Json(new { error = "Username and password are required." }, statusCode: 400);
            }

            var ldapAuth = context.RequestServices.GetRequiredService<LdapAuthService>();
            var jwtService = context.RequestServices.GetRequiredService<JwtTokenService>();
            var roleMapper = context.RequestServices.GetRequiredService<RoleMapper>();

            var authResult = await ldapAuth.AuthenticateAsync(username, password);
            if (!authResult.Success)
            {
                return Results.Json(
                    new { error = authResult.ErrorMessage ?? "Authentication failed." },
                    statusCode: 401);
            }

            var roleMappingResult = await roleMapper.MapGroupsToRolesAsync(authResult.Groups ?? []);

            var token = jwtService.GenerateToken(
                authResult.DisplayName ?? username,
                authResult.Username ?? username,
                roleMappingResult.Roles,
                roleMappingResult.IsSystemWideDeployment ? null : roleMappingResult.PermittedSiteIds);

            return Results.Json(new
            {
                access_token = token,
                token_type = "Bearer",
                username = authResult.Username ?? username,
                display_name = authResult.DisplayName ?? username,
                roles = roleMappingResult.Roles,
            });
        }).DisableAntiforgery();

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

        // GET /logout — allows direct navigation to logout (redirects to login after sign-out)
        endpoints.MapGet("/logout", async (HttpContext context) =>
        {
            await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            return Results.Redirect("/login");
        });

        return endpoints;
    }
}
