package de.effigenix.domain.usermanagement;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static org.assertj.core.api.Assertions.*;

/**
 * Unit tests for UserId Value Object.
 * Tests validation, immutability, factory methods, and equality.
 */
@DisplayName("UserId Value Object")
class UserIdTest {

    @Test
    @DisplayName("should_CreateUserId_When_ValidValueProvided")
    void should_CreateUserId_When_ValidValueProvided() {
        // Arrange
        String validId = "user-123";

        // Act
        UserId userId = new UserId(validId);

        // Assert
        assertThat(userId.value()).isEqualTo(validId);
    }

    @Test
    @DisplayName("should_ThrowException_When_NullValueProvided")
    void should_ThrowException_When_NullValueProvided() {
        // Act & Assert
        assertThatThrownBy(() -> new UserId(null))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("UserId cannot be null or empty");
    }

    @Test
    @DisplayName("should_ThrowException_When_EmptyStringProvided")
    void should_ThrowException_When_EmptyStringProvided() {
        // Act & Assert
        assertThatThrownBy(() -> new UserId(""))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("UserId cannot be null or empty");
    }

    @Test
    @DisplayName("should_ThrowException_When_BlankStringProvided")
    void should_ThrowException_When_BlankStringProvided() {
        // Act & Assert
        assertThatThrownBy(() -> new UserId("   "))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("UserId cannot be null or empty");
    }

    @Test
    @DisplayName("should_GenerateUniqueUserId_When_GenerateMethodCalled")
    void should_GenerateUniqueUserId_When_GenerateMethodCalled() {
        // Act
        UserId userId1 = UserId.generate();
        UserId userId2 = UserId.generate();

        // Assert
        assertThat(userId1.value()).isNotBlank();
        assertThat(userId2.value()).isNotBlank();
        assertThat(userId1.value()).isNotEqualTo(userId2.value());
    }

    @Test
    @DisplayName("should_CreateUserIdFromString_When_OfMethodCalled")
    void should_CreateUserIdFromString_When_OfMethodCalled() {
        // Arrange
        String validId = "test-user-id";

        // Act
        UserId userId = UserId.of(validId);

        // Assert
        assertThat(userId.value()).isEqualTo(validId);
    }

    @Test
    @DisplayName("should_BeImmutable_When_RecordCreated")
    void should_BeImmutable_When_RecordCreated() {
        // Arrange
        UserId userId = UserId.generate();

        // Act & Assert - Records are immutable by design
        assertThat(userId).isNotNull();
        // Can't modify value as it's a record
    }

    @Test
    @DisplayName("should_BeEqual_When_SameValueProvided")
    void should_BeEqual_When_SameValueProvided() {
        // Arrange
        String value = "same-user-id";
        UserId userId1 = new UserId(value);
        UserId userId2 = new UserId(value);

        // Act & Assert
        assertThat(userId1).isEqualTo(userId2);
        assertThat(userId1.hashCode()).isEqualTo(userId2.hashCode());
    }

    @Test
    @DisplayName("should_NotBeEqual_When_DifferentValuesProvided")
    void should_NotBeEqual_When_DifferentValuesProvided() {
        // Arrange
        UserId userId1 = new UserId("user-1");
        UserId userId2 = new UserId("user-2");

        // Act & Assert
        assertThat(userId1).isNotEqualTo(userId2);
    }

    @ParameterizedTest
    @ValueSource(strings = {"", " ", "  \t  "})
    @DisplayName("should_ThrowException_When_InvalidStringsProvided")
    void should_ThrowException_When_InvalidStringsProvided(String invalidValue) {
        // Act & Assert
        assertThatThrownBy(() -> new UserId(invalidValue))
                .isInstanceOf(IllegalArgumentException.class);
    }
}
