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

namespace ColaFlow.Modules.Identity.IntegrationTests.Identity;

/// <summary>
/// Integration tests for basic Authentication functionality (Day 4 Regression Tests)
/// Tests registration, login, password validation, and protected endpoints
/// </summary>
public class AuthenticationTests(DatabaseFixture fixture) : IClassFixture<DatabaseFixture>
{
    private readonly HttpClient _client = fixture.Client;

    [Fact]
    public async Task RegisterTenant_WithValidData_ShouldSucceed()
    {
        // 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();
    }

    [Fact]
    public async Task RegisterTenant_WithDuplicateSlug_ShouldFail()
    {
        // Arrange - Register first tenant
        var slug = $"test-{Guid.NewGuid():N}";
        var firstRequest = new
        {
            tenantName = "First Corp",
            tenantSlug = slug,
            subscriptionPlan = "Professional",
            adminEmail = $"admin1-{Guid.NewGuid():N}@test.com",
            adminPassword = "Admin@1234",
            adminFullName = "First Admin"
        };
        await _client.PostAsJsonAsync("/api/tenants/register", firstRequest);

        // Act - Try to register with same slug
        var secondRequest = new
        {
            tenantName = "Second Corp",
            tenantSlug = slug,
            subscriptionPlan = "Professional",
            adminEmail = $"admin2-{Guid.NewGuid():N}@test.com",
            adminPassword = "Admin@1234",
            adminFullName = "Second Admin"
        };
        var response = await _client.PostAsJsonAsync("/api/tenants/register", secondRequest);

        // Assert - Should fail with conflict or bad request
        response.StatusCode.Should().BeOneOf(HttpStatusCode.BadRequest, HttpStatusCode.Conflict);
    }

    [Fact]
    public async Task Login_WithCorrectCredentials_ShouldSucceed()
    {
        // Arrange - Register tenant
        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();
    }

    [Fact]
    public async Task Login_WithWrongPassword_ShouldFail()
    {
        // Arrange - Register tenant
        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 with wrong password
        var loginRequest = new { tenantSlug, email, password = "WrongPassword123" };
        var response = await _client.PostAsJsonAsync("/api/auth/login", loginRequest);

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

    [Fact]
    public async Task Login_WithNonExistentEmail_ShouldFail()
    {
        // Arrange
        var loginRequest = new
        {
            tenantSlug = "nonexistent",
            email = "nonexistent@test.com",
            password = "Password123"
        };

        // Act
        var response = await _client.PostAsJsonAsync("/api/auth/login", loginRequest);

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

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

        // Act - Access protected endpoint
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        var response = await _client.GetAsync("/api/auth/me");

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

        var userInfo = await response.Content.ReadFromJsonAsync<UserInfoResponse>();
        userInfo.Should().NotBeNull();
        userInfo!.Email.Should().NotBeNullOrEmpty();
        userInfo.FullName.Should().NotBeNullOrEmpty();
    }

    [Fact]
    public async Task AccessProtectedEndpoint_WithoutToken_ShouldFail()
    {
        // Arrange - No authorization header

        // Act
        var response = await _client.GetAsync("/api/auth/me");

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

    [Fact]
    public async Task AccessProtectedEndpoint_WithInvalidToken_ShouldFail()
    {
        // Arrange
        var invalidToken = "invalid.jwt.token";
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", invalidToken);

        // Act
        var response = await _client.GetAsync("/api/auth/me");

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

    [Fact]
    public async Task JwtToken_ShouldContainUserClaims()
    {
        // Arrange & Act
        var (accessToken, _) = await TestAuthHelper.RegisterAndGetTokensAsync(_client);

        // Assert - Parse token and verify claims
        var claims = TestAuthHelper.ParseJwtToken(accessToken).ToList();

        claims.Should().Contain(c => c.Type == "user_id");
        claims.Should().Contain(c => c.Type == "tenant_id");
        claims.Should().Contain(c => c.Type == "email");
        claims.Should().Contain(c => c.Type == "full_name");
        claims.Should().Contain(c => c.Type == "tenant_slug");
    }

    [Fact]
    public async Task PasswordHashing_ShouldNotStorePlainTextPasswords()
    {
        // This is a conceptual test - in real implementation, you'd query the database
        // to verify passwords are hashed. Here we just verify that login works with BCrypt.

        // Arrange - Register tenant
        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 with correct password should work
        var correctLogin = await _client.PostAsJsonAsync("/api/auth/login",
            new { tenantSlug, email, password });

        // Act - Login with wrong password should fail
        var wrongLogin = await _client.PostAsJsonAsync("/api/auth/login",
            new { tenantSlug, email, password = "WrongPassword" });

        // Assert
        correctLogin.StatusCode.Should().Be(HttpStatusCode.OK,
            "Correct password should be verified against hashed password");
        wrongLogin.StatusCode.Should().Be(HttpStatusCode.Unauthorized,
            "Wrong password should not match hashed password");
    }

    [Fact]
    public async Task CompleteAuthFlow_RegisterLoginAccess_ShouldWork()
    {
        // This test verifies the complete authentication flow

        // Step 1: Register
        var tenantSlug = $"test-{Guid.NewGuid():N}";
        var email = $"admin-{Guid.NewGuid():N}@test.com";
        var password = "Admin@1234";
        var registerResponse = await RegisterTenantAsync(tenantSlug, email, password);
        registerResponse.StatusCode.Should().Be(HttpStatusCode.OK);

        // Step 2: Login
        var (loginToken, _) = await TestAuthHelper.LoginAndGetTokensAsync(_client, tenantSlug, email, password);
        loginToken.Should().NotBeNullOrEmpty();

        // Step 3: Access Protected Endpoint
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", loginToken);
        var meResponse = await _client.GetAsync("/api/auth/me");
        meResponse.StatusCode.Should().Be(HttpStatusCode.OK);

        var userInfo = await meResponse.Content.ReadFromJsonAsync<UserInfoResponse>();
        userInfo!.Email.Should().Be(email);
    }

    #region Helper Methods

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

        return await _client.PostAsJsonAsync("/api/tenants/register", request);
    }

    #endregion
}
