package cn.novalon.gym.manage.sys.primitive;

import cn.novalon.gym.manage.common.exception.ValidationException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static org.junit.jupiter.api.Assertions.*;

class UsernameTest {

    @Test
    void testOf_ValidUsername() {
        Username username = Username.of("test_user123");
        assertEquals("test_user123", username.getValue());
    }

    @Test
    void testOf_NullUsername() {
        ValidationException exception = assertThrows(
                ValidationException.class,
                () -> Username.of(null)
        );
        assertEquals("Username is required", exception.getMessage());
    }

    @Test
    void testOf_EmptyUsername() {
        ValidationException exception = assertThrows(
                ValidationException.class,
                () -> Username.of("")
        );
        assertEquals("Username is required", exception.getMessage());
    }

    @Test
    void testOf_WhitespaceOnlyUsername() {
        ValidationException exception = assertThrows(
                ValidationException.class,
                () -> Username.of("   ")
        );
        assertEquals("Username is required", exception.getMessage());
    }

    @Test
    void testOf_TooShortUsername() {
        ValidationException exception = assertThrows(
                ValidationException.class,
                () -> Username.of("ab")
        );
        assertEquals("Username must be at least 3 characters long", exception.getMessage());
    }

    @Test
    void testOf_TooLongUsername() {
        ValidationException exception = assertThrows(
                ValidationException.class,
                () -> Username.of("a".repeat(51))
        );
        assertEquals("Username must be at most 50 characters long", exception.getMessage());
    }

    @Test
    void testOf_WithSpecialCharacters() {
        ValidationException exception = assertThrows(
                ValidationException.class,
                () -> Username.of("user@name")
        );
        assertEquals("Username can only contain letters, numbers, and underscores", exception.getMessage());
    }

    @Test
    void testOf_WithSpaces() {
        ValidationException exception = assertThrows(
                ValidationException.class,
                () -> Username.of("user name")
        );
        assertEquals("Username can only contain letters, numbers, and underscores", exception.getMessage());
    }

    @Test
    void testOf_WithHyphens() {
        ValidationException exception = assertThrows(
                ValidationException.class,
                () -> Username.of("user-name")
        );
        assertEquals("Username can only contain letters, numbers, and underscores", exception.getMessage());
    }

    @Test
    void testOf_MinLengthBoundary() {
        Username username = Username.of("abc");
        assertEquals("abc", username.getValue());
    }

    @Test
    void testOf_MaxLengthBoundary() {
        Username username = Username.of("a".repeat(50));
        assertEquals("a".repeat(50), username.getValue());
    }

    @Test
    void testOf_WithLeadingTrailingWhitespace() {
        Username username = Username.of("  test_user  ");
        assertEquals("  test_user  ", username.getValue());
    }

    @Test
    void testOf_OnlyLetters() {
        Username username = Username.of("username");
        assertEquals("username", username.getValue());
    }

    @Test
    void testOf_OnlyNumbers() {
        Username username = Username.of("123456");
        assertEquals("123456", username.getValue());
    }

    @Test
    void testOf_OnlyUnderscores() {
        Username username = Username.of("___");
        assertEquals("___", username.getValue());
    }

    @Test
    void testEquals_SameValue() {
        Username username1 = Username.of("testuser");
        Username username2 = Username.of("testuser");
        assertEquals(username1, username2);
    }

    @Test
    void testEquals_DifferentValue() {
        Username username1 = Username.of("testuser1");
        Username username2 = Username.of("testuser2");
        assertNotEquals(username1, username2);
    }

    @Test
    void testEquals_SameObject() {
        Username username = Username.of("testuser");
        assertEquals(username, username);
    }

    @Test
    void testEquals_Null() {
        Username username = Username.of("testuser");
        assertNotEquals(username, null);
    }

    @Test
    void testEquals_DifferentClass() {
        Username username = Username.of("testuser");
        assertNotEquals(username, "testuser");
    }

    @Test
    void testHashCode_SameValue() {
        Username username1 = Username.of("testuser");
        Username username2 = Username.of("testuser");
        assertEquals(username1.hashCode(), username2.hashCode());
    }

    @Test
    void testHashCode_DifferentValue() {
        Username username1 = Username.of("testuser1");
        Username username2 = Username.of("testuser2");
        assertNotEquals(username1.hashCode(), username2.hashCode());
    }

    @Test
    void testToString() {
        Username username = Username.of("testuser");
        assertEquals("testuser", username.toString());
    }

    @ParameterizedTest
    @ValueSource(strings = {"user_123", "User_123", "USER_123", "123_user", "_user", "user_"})
    void testOf_ValidFormats(String validUsername) {
        Username username = Username.of(validUsername);
        assertEquals(validUsername.trim(), username.getValue());
    }
}
