using ColaFlow.Modules.Mcp.Domain.Entities;
using ColaFlow.Modules.Mcp.Domain.ValueObjects;
using FluentAssertions;
using Xunit;

namespace ColaFlow.Modules.Mcp.Tests.Domain;

public class McpApiKeyTests
{
    [Fact]
    public void Create_ShouldGenerateValidApiKey()
    {
        // Arrange
        var name = "Test API Key";
        var tenantId = Guid.NewGuid();
        var userId = Guid.NewGuid();
        var permissions = ApiKeyPermissions.ReadOnly();

        // Act
        var (apiKey, plainKey) = McpApiKey.Create(name, tenantId, userId, permissions);

        // Assert
        apiKey.Should().NotBeNull();
        apiKey.Name.Should().Be(name);
        apiKey.TenantId.Should().Be(tenantId);
        apiKey.UserId.Should().Be(userId);
        apiKey.Status.Should().Be(ApiKeyStatus.Active);
        plainKey.Should().StartWith("cola_");
        plainKey.Length.Should().Be(41); // "cola_" (5) + 36 random chars
        apiKey.KeyPrefix.Should().Be(plainKey.Substring(0, 12));
    }

    [Fact]
    public void Create_ShouldHashApiKey()
    {
        // Arrange
        var name = "Test API Key";
        var tenantId = Guid.NewGuid();
        var userId = Guid.NewGuid();
        var permissions = ApiKeyPermissions.ReadOnly();

        // Act
        var (apiKey, plainKey) = McpApiKey.Create(name, tenantId, userId, permissions);

        // Assert
        apiKey.KeyHash.Should().NotBeNullOrEmpty();
        apiKey.KeyHash.Should().NotBe(plainKey); // Hash should not equal plain key
        apiKey.VerifyKey(plainKey).Should().BeTrue(); // But should verify correctly
    }

    [Fact]
    public void Create_WithInvalidName_ShouldThrowArgumentException()
    {
        // Arrange
        var tenantId = Guid.NewGuid();
        var userId = Guid.NewGuid();
        var permissions = ApiKeyPermissions.ReadOnly();

        // Act & Assert
        Assert.Throws<ArgumentException>(() =>
            McpApiKey.Create("", tenantId, userId, permissions));
    }

    [Fact]
    public void Create_WithInvalidTenantId_ShouldThrowArgumentException()
    {
        // Arrange
        var userId = Guid.NewGuid();
        var permissions = ApiKeyPermissions.ReadOnly();

        // Act & Assert
        Assert.Throws<ArgumentException>(() =>
            McpApiKey.Create("Test", Guid.Empty, userId, permissions));
    }

    [Fact]
    public void VerifyKey_WithCorrectKey_ShouldReturnTrue()
    {
        // Arrange
        var (apiKey, plainKey) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly());

        // Act
        var result = apiKey.VerifyKey(plainKey);

        // Assert
        result.Should().BeTrue();
    }

    [Fact]
    public void VerifyKey_WithIncorrectKey_ShouldReturnFalse()
    {
        // Arrange
        var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly());

        // Act
        var result = apiKey.VerifyKey("cola_wrongkey123456789012345678901234");

        // Assert
        result.Should().BeFalse();
    }

    [Fact]
    public void Revoke_ShouldMarkAsRevoked()
    {
        // Arrange
        var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly());
        var revokedBy = Guid.NewGuid();

        // Act
        apiKey.Revoke(revokedBy);

        // Assert
        apiKey.Status.Should().Be(ApiKeyStatus.Revoked);
        apiKey.RevokedAt.Should().NotBeNull();
        apiKey.RevokedBy.Should().Be(revokedBy);
    }

    [Fact]
    public void Revoke_WhenAlreadyRevoked_ShouldThrowInvalidOperationException()
    {
        // Arrange
        var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly());
        apiKey.Revoke(Guid.NewGuid());

        // Act & Assert
        Assert.Throws<InvalidOperationException>(() => apiKey.Revoke(Guid.NewGuid()));
    }

    [Fact]
    public void IsExpired_WhenNotExpired_ShouldReturnFalse()
    {
        // Arrange
        var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly(), expirationDays: 90);

        // Act
        var result = apiKey.IsExpired();

        // Assert
        result.Should().BeFalse();
    }

    [Fact]
    public void IsValid_WhenActiveAndNotExpired_ShouldReturnTrue()
    {
        // Arrange
        var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly());

        // Act
        var result = apiKey.IsValid();

        // Assert
        result.Should().BeTrue();
    }

    [Fact]
    public void IsValid_WhenRevoked_ShouldReturnFalse()
    {
        // Arrange
        var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly());
        apiKey.Revoke(Guid.NewGuid());

        // Act
        var result = apiKey.IsValid();

        // Assert
        result.Should().BeFalse();
    }

    [Fact]
    public void RecordUsage_ShouldIncrementUsageCount()
    {
        // Arrange
        var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly());
        var initialCount = apiKey.UsageCount;

        // Act
        apiKey.RecordUsage();

        // Assert
        apiKey.UsageCount.Should().Be(initialCount + 1);
        apiKey.LastUsedAt.Should().NotBeNull();
    }

    [Fact]
    public void IsIpAllowed_WithNoWhitelist_ShouldReturnTrue()
    {
        // Arrange
        var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly());

        // Act
        var result = apiKey.IsIpAllowed("192.168.1.1");

        // Assert
        result.Should().BeTrue();
    }

    [Fact]
    public void IsIpAllowed_WithWhitelist_ShouldValidateCorrectly()
    {
        // Arrange
        var permissions = ApiKeyPermissions.ReadOnly();
        var ipWhitelist = new List<string> { "192.168.1.1", "10.0.0.1" };
        var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), permissions, ipWhitelist: ipWhitelist);

        // Act & Assert
        apiKey.IsIpAllowed("192.168.1.1").Should().BeTrue();
        apiKey.IsIpAllowed("10.0.0.1").Should().BeTrue();
        apiKey.IsIpAllowed("172.16.0.1").Should().BeFalse();
    }
}
