using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Integration tests for authentication flow: login, refresh, logout, and role-based access.
/// </summary>
[Collection("Database")]
public class AuthTests : IAsyncLifetime
{
    private readonly ApiFixture _fixture;

    public AuthTests(ApiFixture fixture) => _fixture = fixture;

    public async Task InitializeAsync()
    {
        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        await DbResetHelper.ResetAsync(db);
        await DataSeeder.SeedAsync(db);
    }

    public Task DisposeAsync() => Task.CompletedTask;

    [Fact]
    public async Task Login_ValidCredentials_ReturnsTokenAndProfile()
    {
        var client = _fixture.CreateClient();

        var response = await client.PostAsJsonAsync("/api/v1/auth/login",
            new { username = "entry1", password = "password" });

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await response.Content.ReadFromJsonAsync<JsonElement>();
        var data = body.GetProperty("data");
        data.GetProperty("token").GetString().Should().NotBeNullOrEmpty();
        data.GetProperty("refreshToken").GetString().Should().NotBeNullOrEmpty();
        data.GetProperty("username").GetString().Should().Be("entry1");
        data.GetProperty("role").GetString().Should().Be("DATA_ENTRY_CLERK");
    }

    [Fact]
    public async Task Login_InvalidPassword_Returns422()
    {
        var client = _fixture.CreateClient();

        var response = await client.PostAsJsonAsync("/api/v1/auth/login",
            new { username = "entry1", password = "wrongpassword" });

        response.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
    }

    [Fact]
    public async Task Login_NonexistentUser_Returns422()
    {
        var client = _fixture.CreateClient();

        var response = await client.PostAsJsonAsync("/api/v1/auth/login",
            new { username = "nonexistent", password = "password" });

        response.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
    }

    [Fact]
    public async Task ProtectedEndpoint_NoToken_Returns401()
    {
        var client = _fixture.CreateClient();

        var response = await client.GetAsync("/api/v1/digitization-batches");

        response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
    }

    [Fact]
    public async Task RoleRestricted_WrongRole_Returns403()
    {
        // entry1 is DATA_ENTRY_CLERK — cannot access admin dashboard
        var client = await AuthHelper.LoginAsync(_fixture, "entry1");

        var response = await client.GetAsync("/api/v1/work-queue/overview");

        response.StatusCode.Should().Be(HttpStatusCode.Forbidden);
    }

    [Fact]
    public async Task Refresh_ValidToken_ReturnsNewTokens()
    {
        var client = _fixture.CreateClient();

        // Login to get refresh token
        var loginResponse = await client.PostAsJsonAsync("/api/v1/auth/login",
            new { username = "verifier1", password = "password" });
        loginResponse.EnsureSuccessStatusCode();

        var loginBody = await loginResponse.Content.ReadFromJsonAsync<JsonElement>();
        var refreshToken = loginBody.GetProperty("data").GetProperty("refreshToken").GetString();

        // Refresh
        var refreshResponse = await client.PostAsJsonAsync("/api/v1/auth/refresh",
            new { refreshToken });

        refreshResponse.StatusCode.Should().Be(HttpStatusCode.OK);

        var refreshBody = await refreshResponse.Content.ReadFromJsonAsync<JsonElement>();
        refreshBody.GetProperty("data").GetProperty("token").GetString().Should().NotBeNullOrEmpty();
        refreshBody.GetProperty("data").GetProperty("refreshToken").GetString()
            .Should().NotBe(refreshToken, "refresh should rotate the token");
    }

    [Fact]
    public async Task Logout_RevokesRefreshToken()
    {
        var client = _fixture.CreateClient();

        // Login
        var loginResponse = await client.PostAsJsonAsync("/api/v1/auth/login",
            new { username = "intake1", password = "password" });
        loginResponse.EnsureSuccessStatusCode();

        var loginBody = await loginResponse.Content.ReadFromJsonAsync<JsonElement>();
        var refreshToken = loginBody.GetProperty("data").GetProperty("refreshToken").GetString();

        // Logout
        var logoutResponse = await client.PostAsJsonAsync("/api/v1/auth/logout",
            new { refreshToken });
        logoutResponse.StatusCode.Should().Be(HttpStatusCode.NoContent);

        // Attempt to refresh with revoked token
        var refreshResponse = await client.PostAsJsonAsync("/api/v1/auth/refresh",
            new { refreshToken });
        refreshResponse.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
    }

    [Fact]
    public async Task Me_Authenticated_ReturnsProfile()
    {
        var client = await AuthHelper.LoginAsync(_fixture, "approver1");

        var response = await client.GetAsync("/api/v1/auth/me");

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await response.Content.ReadFromJsonAsync<JsonElement>();
        var data = body.GetProperty("data");
        data.GetProperty("username").GetString().Should().Be("approver1");
        data.GetProperty("role").GetString().Should().Be("CLINICAL_APPROVER");
    }
}
