using GameList.Contracts;
using GameList.Data;
using GameList.Domain;
using GameList.Infrastructure;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;

namespace GameList.Endpoints;

public static class AuthEndpoints
{
    public static void MapAuthEndpoints(this IEndpointRouteBuilder app)
    {
        var group = app.MapGroup("/api/auth");

        group.MapPost("/register", async ([FromBody] RegisterRequest request, HttpContext ctx, AppDbContext db, IConfiguration config) =>
        {
            var username = request.Username?.Trim();
            if (string.IsNullOrWhiteSpace(username) || username.Length > 24)
                return Results.BadRequest(new { error = "Username is required and must be <= 24 characters." });

            if (string.IsNullOrWhiteSpace(request.Password))
                return Results.BadRequest(new { error = "Password is required." });

            var displayName = EndpointHelpers.TrimTo(request.DisplayName, 16);
            if (string.IsNullOrWhiteSpace(displayName))
                return Results.BadRequest(new { error = "Display name is required." });
            var normalized = username.ToLowerInvariant();

            var exists = await db.Players.AnyAsync(p => p.NormalizedUsername == normalized);
            if (exists)
                return Results.Conflict(new { error = "Username already taken." });

            var (hash, salt) = PasswordHasher.HashPassword(request.Password);
            var adminKey = EndpointHelpers.TrimTo(request.AdminKey, 128);
            var expectedAdminKey = config["ADMIN_PASSWORD"];
            var wantsAdmin = !string.IsNullOrWhiteSpace(adminKey);
            if (wantsAdmin)
            {
                if (string.IsNullOrWhiteSpace(expectedAdminKey) || adminKey != expectedAdminKey)
                    return Results.BadRequest(new { error = "Invalid admin key." });
            }
            var isAdmin = wantsAdmin;

            var player = new Player
            {
                Id = Guid.NewGuid(),
                Username = username,
                NormalizedUsername = normalized,
                PasswordHash = hash,
                PasswordSalt = salt,
                DisplayName = displayName,
                IsAdmin = isAdmin,
                CreatedAt = DateTimeOffset.UtcNow,
                LastLoginAt = DateTimeOffset.UtcNow
            };

            db.Players.Add(player);
            await db.SaveChangesAsync();

            PlayerIdentityExtensions.IssuePlayerCookie(ctx, player.Id, player.Username);

            return Results.Ok(new { player.Id, player.Username, player.DisplayName, player.IsAdmin });
        });

        group.MapPost("/login", async ([FromBody] LoginRequest request, HttpContext ctx, AppDbContext db) =>
        {
            var username = request.Username?.Trim();
            if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(request.Password))
                return Results.BadRequest(new { error = "Username and password are required." });
            if (username.Length > 24)
                return Results.BadRequest(new { error = "Username must be <= 24 characters." });

            var normalized = username.ToLowerInvariant();
            var player = await db.Players.FirstOrDefaultAsync(p => p.NormalizedUsername == normalized);
            if (player == null || !PasswordHasher.Verify(request.Password, player.PasswordHash, player.PasswordSalt))
                return Results.Json(new { error = "Invalid username or password." }, statusCode: StatusCodes.Status401Unauthorized);

            if (string.IsNullOrWhiteSpace(player.DisplayName))
            {
                player.DisplayName = EndpointHelpers.TrimTo(player.Username, 16);
            }
            player.LastLoginAt = DateTimeOffset.UtcNow;
            await db.SaveChangesAsync();

            PlayerIdentityExtensions.IssuePlayerCookie(ctx, player.Id, player.Username);

            return Results.Ok(new { player.Id, player.Username, player.DisplayName, player.IsAdmin });
        });

        group.MapPost("/logout", (HttpContext ctx) =>
        {
            PlayerIdentityExtensions.ClearPlayerCookie(ctx);
            return Results.NoContent();
        });
    }
}
