using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects;
using ColaFlow.Modules.ProjectManagement.Domain.Exceptions;
using FluentAssertions;

namespace ColaFlow.Domain.Tests.ValueObjects;

/// <summary>
/// Unit tests for ProjectKey value object
/// </summary>
public class ProjectKeyTests
{
    [Fact]
    public void Create_WithValidKey_ShouldCreateProjectKey()
    {
        // Arrange
        var key = "COLA";

        // Act
        var projectKey = ProjectKey.Create(key);

        // Assert
        projectKey.Should().NotBeNull();
        projectKey.Value.Should().Be(key);
    }

    [Theory]
    [InlineData("A")]
    [InlineData("AB")]
    [InlineData("ABC")]
    [InlineData("ABCD")]
    [InlineData("ABCDEFGHIJ")] // 10 characters - max allowed
    public void Create_WithValidLengthKeys_ShouldSucceed(string key)
    {
        // Act
        var projectKey = ProjectKey.Create(key);

        // Assert
        projectKey.Value.Should().Be(key);
    }

    [Theory]
    [InlineData("TEST")]
    [InlineData("COLA")]
    [InlineData("FLOW")]
    [InlineData("PROJECT1")]
    [InlineData("ABC123")]
    public void Create_WithUppercaseLettersAndNumbers_ShouldSucceed(string key)
    {
        // Act
        var projectKey = ProjectKey.Create(key);

        // Assert
        projectKey.Value.Should().Be(key);
    }

    [Theory]
    [InlineData("")]
    [InlineData("   ")]
    [InlineData(null)]
    public void Create_WithEmptyKey_ShouldThrowDomainException(string invalidKey)
    {
        // Act
        Action act = () => ProjectKey.Create(invalidKey);

        // Assert
        act.Should().Throw<DomainException>()
            .WithMessage("Project key cannot be empty");
    }

    [Fact]
    public void Create_WithKeyExceeding10Characters_ShouldThrowDomainException()
    {
        // Arrange
        var key = "ABCDEFGHIJK"; // 11 characters

        // Act
        Action act = () => ProjectKey.Create(key);

        // Assert
        act.Should().Throw<DomainException>()
            .WithMessage("Project key cannot exceed 10 characters");
    }

    [Theory]
    [InlineData("test")] // lowercase
    [InlineData("Test")] // mixed case
    [InlineData("TEST-1")] // hyphen
    [InlineData("TEST_1")] // underscore
    [InlineData("TEST 1")] // space
    [InlineData("TEST.1")] // dot
    [InlineData("TEST@1")] // special char
    public void Create_WithInvalidCharacters_ShouldThrowDomainException(string invalidKey)
    {
        // Act
        Action act = () => ProjectKey.Create(invalidKey);

        // Assert
        act.Should().Throw<DomainException>()
            .WithMessage("Project key must contain only uppercase letters and numbers");
    }

    [Fact]
    public void Equals_WithSameValue_ShouldReturnTrue()
    {
        // Arrange
        var key = "TEST";
        var projectKey1 = ProjectKey.Create(key);
        var projectKey2 = ProjectKey.Create(key);

        // Act & Assert
        projectKey1.Should().Be(projectKey2);
        projectKey1.Equals(projectKey2).Should().BeTrue();
    }

    [Fact]
    public void Equals_WithDifferentValue_ShouldReturnFalse()
    {
        // Arrange
        var projectKey1 = ProjectKey.Create("TEST1");
        var projectKey2 = ProjectKey.Create("TEST2");

        // Act & Assert
        projectKey1.Should().NotBe(projectKey2);
        projectKey1.Equals(projectKey2).Should().BeFalse();
    }

    [Fact]
    public void GetHashCode_WithSameValue_ShouldReturnSameHashCode()
    {
        // Arrange
        var key = "TEST";
        var projectKey1 = ProjectKey.Create(key);
        var projectKey2 = ProjectKey.Create(key);

        // Act & Assert
        projectKey1.GetHashCode().Should().Be(projectKey2.GetHashCode());
    }

    [Fact]
    public void ToString_ShouldReturnKeyValue()
    {
        // Arrange
        var key = "COLA";
        var projectKey = ProjectKey.Create(key);

        // Act
        var result = projectKey.ToString();

        // Assert
        result.Should().Be(key);
    }

    [Fact]
    public void ValueObject_ShouldBeImmutable()
    {
        // Arrange
        var key = "TEST";
        var projectKey = ProjectKey.Create(key);
        var originalValue = projectKey.Value;

        // Act - Try to get the value multiple times
        var value1 = projectKey.Value;
        var value2 = projectKey.Value;

        // Assert
        value1.Should().Be(originalValue);
        value2.Should().Be(originalValue);
    }

    [Theory]
    [InlineData("123")]
    [InlineData("789")]
    [InlineData("1234567890")]
    public void Create_WithOnlyNumbers_ShouldSucceed(string key)
    {
        // Act
        var projectKey = ProjectKey.Create(key);

        // Assert
        projectKey.Value.Should().Be(key);
    }
}
