using System.Net;
using System.Net.Http.Json;
using ColaFlow.Modules.Identity.IntegrationTests.Infrastructure;
using FluentAssertions;

namespace ColaFlow.Modules.Identity.IntegrationTests.Identity;

/// <summary>
/// Integration tests for Refresh Token functionality (Day 5 - Phase 1)
/// Tests token refresh flow, token rotation, and refresh token revocation
/// </summary>
public class RefreshTokenTests(DatabaseFixture fixture) : IClassFixture<DatabaseFixture>
{
    private readonly HttpClient _client = fixture.Client;

    [Fact]
    public async Task RegisterTenant_ShouldReturnAccessAndRefreshTokens()
    {
        // Arrange
        var request = new
        {
            tenantName = "Test Corp",
            tenantSlug = $"test-{Guid.NewGuid():N}",
            subscriptionPlan = "Professional",
            adminEmail = $"admin-{Guid.NewGuid():N}@test.com",
            adminPassword = "Admin@1234",
            adminFullName = "Test Admin"
        };

        // Act
        var response = await _client.PostAsJsonAsync("/api/tenants/register", request);

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

        var result = await response.Content.ReadFromJsonAsync<RegisterResponse>();
        result.Should().NotBeNull();
        result!.AccessToken.Should().NotBeNullOrEmpty();
        result.RefreshToken.Should().NotBeNullOrEmpty();

        // Verify tokens are different
        result.AccessToken.Should().NotBe(result.RefreshToken);
    }

    [Fact]
    public async Task Login_ShouldReturnAccessAndRefreshTokens()
    {
        // Arrange - Register tenant first
        var tenantSlug = $"test-{Guid.NewGuid():N}";
        var email = $"admin-{Guid.NewGuid():N}@test.com";
        var password = "Admin@1234";

        await RegisterTenantAsync(tenantSlug, email, password);

        // Act - Login
        var loginRequest = new { tenantSlug, email, password };
        var response = await _client.PostAsJsonAsync("/api/auth/login", loginRequest);

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

        var result = await response.Content.ReadFromJsonAsync<LoginResponse>();
        result.Should().NotBeNull();
        result!.AccessToken.Should().NotBeNullOrEmpty();
        result.RefreshToken.Should().NotBeNullOrEmpty();
    }

    [Fact]
    public async Task RefreshToken_ShouldReturnNewTokenPair()
    {
        // Arrange - Register and get initial tokens
        var (accessToken, refreshToken) = await TestAuthHelper.RegisterAndGetTokensAsync(_client);

        // Wait a moment to ensure token expiry time changes
        await Task.Delay(1000);

        // Act - Refresh token
        var refreshRequest = new { refreshToken };
        var response = await _client.PostAsJsonAsync("/api/auth/refresh", refreshRequest);

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

        var result = await response.Content.ReadFromJsonAsync<RefreshResponse>();
        result.Should().NotBeNull();
        result!.AccessToken.Should().NotBeNullOrEmpty();
        result.RefreshToken.Should().NotBeNullOrEmpty();

        // New tokens should be different from old tokens
        result.AccessToken.Should().NotBe(accessToken);
        result.RefreshToken.Should().NotBe(refreshToken);
    }

    [Fact]
    public async Task RefreshToken_WithOldToken_ShouldFail()
    {
        // Arrange - Register and get initial tokens
        var (_, refreshToken) = await TestAuthHelper.RegisterAndGetTokensAsync(_client);

        // Act - Refresh once (invalidates old token)
        var firstRefresh = await _client.PostAsJsonAsync("/api/auth/refresh", new { refreshToken });
        firstRefresh.StatusCode.Should().Be(HttpStatusCode.OK);

        // Act - Try to reuse old refresh token
        var response = await _client.PostAsJsonAsync("/api/auth/refresh", new { refreshToken });

        // Assert - Should fail because token is already used
        response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
    }

    [Fact]
    public async Task RefreshToken_WithInvalidToken_ShouldFail()
    {
        // Arrange
        var invalidToken = "invalid-refresh-token";

        // Act
        var response = await _client.PostAsJsonAsync("/api/auth/refresh", new { refreshToken = invalidToken });

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

    [Fact]
    public async Task Logout_ShouldRevokeRefreshToken()
    {
        // Arrange - Register and get tokens
        var (_, refreshToken) = await TestAuthHelper.RegisterAndGetTokensAsync(_client);

        // Act - Logout
        var logoutResponse = await _client.PostAsJsonAsync("/api/auth/logout", new { refreshToken });

        // Assert - Logout should succeed
        logoutResponse.StatusCode.Should().Be(HttpStatusCode.OK);

        // Try to use revoked refresh token
        var refreshResponse = await _client.PostAsJsonAsync("/api/auth/refresh", new { refreshToken });

        // Should fail because token is revoked
        refreshResponse.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
    }

    [Fact]
    public async Task RefreshToken_ShouldMaintainUserIdentity()
    {
        // Arrange - Register and get tokens
        var (accessToken, refreshToken) = await TestAuthHelper.RegisterAndGetTokensAsync(_client);

        // Get original user info
        var originalUserId = TestAuthHelper.GetClaimValue(accessToken, "user_id");
        var originalTenantId = TestAuthHelper.GetClaimValue(accessToken, "tenant_id");

        // Act - Refresh token
        var refreshRequest = new { refreshToken };
        var response = await _client.PostAsJsonAsync("/api/auth/refresh", refreshRequest);
        var result = await response.Content.ReadFromJsonAsync<RefreshResponse>();

        // Assert - New token should have same user identity
        var newUserId = TestAuthHelper.GetClaimValue(result!.AccessToken, "user_id");
        var newTenantId = TestAuthHelper.GetClaimValue(result.AccessToken, "tenant_id");

        newUserId.Should().Be(originalUserId);
        newTenantId.Should().Be(originalTenantId);
    }

    [Fact]
    public async Task RefreshToken_Multiple_ShouldSucceed()
    {
        // Arrange - Register and get initial tokens
        var (_, refreshToken) = await TestAuthHelper.RegisterAndGetTokensAsync(_client);

        // Act & Assert - Refresh multiple times
        for (int i = 0; i < 5; i++)
        {
            var response = await _client.PostAsJsonAsync("/api/auth/refresh", new { refreshToken });
            response.StatusCode.Should().Be(HttpStatusCode.OK);

            var result = await response.Content.ReadFromJsonAsync<RefreshResponse>();
            result.Should().NotBeNull();

            // Update refresh token for next iteration
            refreshToken = result!.RefreshToken;

            await Task.Delay(500); // Small delay between requests
        }
    }

    [Fact]
    public async Task RefreshToken_Expired_ShouldFail()
    {
        // Note: This test requires the refresh token to be configured with a very short expiration time
        // In real scenarios, refresh tokens typically last 7-30 days
        // This test is a placeholder to document the expected behavior

        // For now, we test with an invalid/non-existent token which should fail
        var expiredToken = Guid.NewGuid().ToString();

        // Act
        var response = await _client.PostAsJsonAsync("/api/auth/refresh", new { refreshToken = expiredToken });

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

    #region Helper Methods

    private async Task RegisterTenantAsync(string tenantSlug, string email, string password)
    {
        var request = new
        {
            tenantName = "Test Corp",
            tenantSlug,
            subscriptionPlan = "Professional",
            adminEmail = email,
            adminPassword = password,
            adminFullName = "Test Admin"
        };

        var response = await _client.PostAsJsonAsync("/api/tenants/register", request);
        response.EnsureSuccessStatusCode();
    }

    #endregion
}
