namespace RpgRoller.Tests;

public sealed class AuthApiTests(WebApplicationFactory<Program> factory) : ApiTestBase(factory)
{
    [Fact]
    public async Task RegisterLoginAndMeFlow_WorksWithDuplicateUsernameGuard()
    {
        using var factory = CreateFactory(4, 4, 4);
        using var client = factory.CreateClient(new() { AllowAutoRedirect = false });

        var registerResult = await RegisterAsync(client, "alice", "Password123", "Alice");
        Assert.Equal("alice", registerResult.Username);
        Assert.Contains(registerResult.Roles, role => string.Equals(role, "admin", StringComparison.OrdinalIgnoreCase));

        var duplicate = await client.PostAsJsonAsync("/api/auth/register",
            new RegisterRequest("alice", "Password123", "Alice 2"));
        Assert.Equal(HttpStatusCode.BadRequest, duplicate.StatusCode);

        var loginResult = await client.PostAsJsonAsync("/api/auth/login", new LoginRequest("alice", "Password123"));
        Assert.Equal(HttpStatusCode.OK, loginResult.StatusCode);

        var me = await GetAsync<MeResponse>(client, "/api/me");
        Assert.Equal(registerResult.Id, me.User.Id);
        Assert.Null(me.ActiveCharacterId);
        Assert.Null(me.CurrentCampaignId);

        var invalidLogin = await client.PostAsJsonAsync("/api/auth/login", new LoginRequest("alice", "wrong-password"));
        Assert.Equal(HttpStatusCode.BadRequest, invalidLogin.StatusCode);
    }

    [Fact]
    public async Task UsernamesEndpoint_RequiresAuthAndReturnsAlphabeticalList()
    {
        using var factory = CreateFactory();
        using var client = factory.CreateClient(new() { AllowAutoRedirect = false });

        await RegisterAsync(client, "zoe", "Password123", "Zoe");
        await RegisterAsync(client, "amy", "Password123", "Amy");
        await RegisterAsync(client, "bob", "Password123", "Bob");

        var unauthorized = await client.GetAsync("/api/users/usernames");
        Assert.Equal(HttpStatusCode.Unauthorized, unauthorized.StatusCode);

        await LoginAsync(client, "bob", "Password123");
        var usernames = await GetAsync<IReadOnlyList<string>>(client, "/api/users/usernames");
        Assert.Equal(["amy", "bob", "zoe"], usernames);
    }

    [Fact]
    public async Task LoginCookie_IsMarkedSecure_WhenForwardedProtoIsHttps()
    {
        using var factory = CreateFactory();
        using var client = factory.CreateClient(new() { AllowAutoRedirect = false });

        await RegisterAsync(client, "proxy-user", "Password123", "Proxy User");

        using var request = new HttpRequestMessage(HttpMethod.Post, "/api/auth/login")
        {
            Content = JsonContent.Create(new LoginRequest("proxy-user", "Password123"))
        };
        request.Headers.TryAddWithoutValidation("X-Forwarded-Proto", "https");

        using var response = await client.SendAsync(request);
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        Assert.NotNull(response.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value);

        var setCookie = Assert.Single(response.Headers.GetValues("Set-Cookie"));
        Assert.Contains("rpgroller_session=", setCookie);
        Assert.Contains("secure", setCookie, StringComparison.OrdinalIgnoreCase);
    }
}