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 RoleId Value Object.
 * Tests validation, immutability, factory methods, and equality.
 */
@DisplayName("RoleId Value Object")
class RoleIdTest {

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

        // Act
        RoleId roleId = new RoleId(validId);

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

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

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

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

    @Test
    @DisplayName("should_GenerateUniqueRoleId_When_GenerateMethodCalled")
    void should_GenerateUniqueRoleId_When_GenerateMethodCalled() {
        // Act
        RoleId roleId1 = RoleId.generate();
        RoleId roleId2 = RoleId.generate();

        // Assert
        assertThat(roleId1.value()).isNotBlank();
        assertThat(roleId2.value()).isNotBlank();
        assertThat(roleId1.value()).isNotEqualTo(roleId2.value());
    }

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

        // Act
        RoleId roleId = RoleId.of(validId);

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

    @Test
    @DisplayName("should_BeImmutable_When_RecordCreated")
    void should_BeImmutable_When_RecordCreated() {
        // Arrange
        RoleId roleId = RoleId.generate();

        // Act & Assert
        assertThat(roleId).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-role-id";
        RoleId roleId1 = new RoleId(value);
        RoleId roleId2 = new RoleId(value);

        // Act & Assert
        assertThat(roleId1).isEqualTo(roleId2);
        assertThat(roleId1.hashCode()).isEqualTo(roleId2.hashCode());
    }

    @Test
    @DisplayName("should_NotBeEqual_When_DifferentValuesProvided")
    void should_NotBeEqual_When_DifferentValuesProvided() {
        // Arrange
        RoleId roleId1 = new RoleId("role-1");
        RoleId roleId2 = new RoleId("role-2");

        // Act & Assert
        assertThat(roleId1).isNotEqualTo(roleId2);
    }

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