using GameList.Contracts;
using GameList.Data;
using GameList.Domain;
using GameList.Infrastructure;
using Microsoft.AspNetCore.Mvc;
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) =>
        {
            if (!AuthValidator.TryValidateRegistration(request, out var validated, out var registrationError))
                return EndpointHelpers.BadRequestError(registrationError);

            var exists = await db.Players.AnyAsync(p => p.NormalizedUsername == validated.NormalizedUsername);
            if (exists)
                return EndpointHelpers.ConflictError("Username already taken.");

            var (hash, salt) = PasswordHasher.HashPassword(request.Password);
            var expectedAdminKey = config["ADMIN_PASSWORD"];
            var wantsAdmin = !string.IsNullOrWhiteSpace(validated.AdminKey);
            if (wantsAdmin)
            {
                if (string.IsNullOrWhiteSpace(expectedAdminKey) || validated.AdminKey != expectedAdminKey)
                    return EndpointHelpers.BadRequestError("Invalid admin key.");
            }

            var isAdmin = wantsAdmin;

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

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

            await PlayerIdentityExtensions.SignInPlayerAsync(ctx, player);

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

        group.MapPost("/login", async ([FromBody] LoginRequest request, HttpContext ctx, AppDbContext db) =>
        {
            if (!AuthValidator.TryValidateLogin(request, out _, out var normalizedUsername, out var loginError))
                return EndpointHelpers.BadRequestError(loginError);

            var player = await db.Players.FirstOrDefaultAsync(p => p.NormalizedUsername == normalizedUsername);
            if (player == null || !PasswordHasher.Verify(request.Password, player.PasswordHash, player.PasswordSalt))
                return EndpointHelpers.UnauthorizedError("Invalid username or password.");

            if (string.IsNullOrWhiteSpace(player.DisplayName))
            {
                player.DisplayName = EndpointHelpers.TrimTo(player.Username, AuthValidator.MaxDisplayNameLength);
            }

            player.LastLoginAt = DateTimeOffset.UtcNow;
            await db.SaveChangesAsync();

            await PlayerIdentityExtensions.SignInPlayerAsync(ctx, player);

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

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