package cn.novalon.manage.sys.primitive;

import cn.novalon.manage.common.exception.ErrorCode;
import cn.novalon.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.*;

/**
 * Password详细测试 - 提升分支覆盖率
 * 
 * @author 张翔
 * @date 2026-03-24
 */
class PasswordDetailedTest {

    @Test
    void testValidPassword() {
        Password password = Password.of("Valid@123");
        assertNotNull(password);
        assertEquals("Valid@123", password.getValue());
    }

    @Test
    void testNullPassword() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of(null);
        });
        assertEquals(ErrorCode.VALIDATION_REQUIRED, exception.getErrorCode());
    }

    @Test
    void testEmptyPassword() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("");
        });
        assertEquals(ErrorCode.VALIDATION_REQUIRED, exception.getErrorCode());
    }

    @Test
    void testWhitespacePassword() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("   ");
        });
        assertEquals(ErrorCode.VALIDATION_REQUIRED, exception.getErrorCode());
    }

    @Test
    void testTooShortPassword() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("Short1@");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_LENGTH, exception.getErrorCode());
        assertTrue(exception.getMessage().contains("at least 8 characters"));
    }

    @Test
    void testExactlyMinLengthPassword() {
        Password password = Password.of("Valid12@");
        assertNotNull(password);
        assertEquals("Valid12@", password.getValue());
    }

    @Test
    void testPasswordWithoutUppercase() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("lowercase1@");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
        assertTrue(exception.getMessage().contains("uppercase letter"));
    }

    @Test
    void testPasswordWithoutLowercase() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("UPPERCASE1@");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
        assertTrue(exception.getMessage().contains("lowercase letter"));
    }

    @Test
    void testPasswordWithoutDigit() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("NoDigits@");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
        assertTrue(exception.getMessage().contains("digit"));
    }

    @Test
    void testPasswordWithoutSpecialCharacter() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("NoSpecial123");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
        assertTrue(exception.getMessage().contains("special character"));
    }

    @ParameterizedTest
    @ValueSource(strings = {
        "Valid@123",
        "Another@456",
        "Test@789",
        "Complex@Pass123",
        "Simple@Pass456"
    })
    void testMultipleValidPasswords(String password) {
        Password pwd = Password.of(password);
        assertNotNull(pwd);
        assertEquals(password, pwd.getValue());
    }

    @ParameterizedTest
    @ValueSource(strings = {
        "lowercase@123",
        "UPPERCASE@123",
        "MixedCase@abc",
        "MixedCase123"
    })
    void testMultipleInvalidPasswords(String password) {
        assertThrows(ValidationException.class, () -> {
            Password.of(password);
        });
    }

    @Test
    void testPasswordWithOnlyUppercaseAndDigit() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("UPPERCASE123");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
    }

    @Test
    void testPasswordWithOnlyLowercaseAndDigit() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("lowercase123");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
    }

    @Test
    void testPasswordWithOnlyUppercaseAndSpecial() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("UPPERCASE@");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
    }

    @Test
    void testPasswordWithOnlyLowercaseAndSpecial() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("lowercase@");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
    }

    @Test
    void testPasswordWithOnlyDigitAndSpecial() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("12345678@");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
    }

    @Test
    void testPasswordWithMultipleSpecialCharacters() {
        Password password = Password.of("Valid@#$123");
        assertNotNull(password);
        assertEquals("Valid@#$123", password.getValue());
    }

    @Test
    void testPasswordWithSpaces() {
        Password password = Password.of("Valid @123");
        assertNotNull(password);
        assertEquals("Valid @123", password.getValue());
    }

    @Test
    void testVeryLongPassword() {
        Password password = Password.of("VeryLongPassword@1234567890");
        assertNotNull(password);
        assertEquals("VeryLongPassword@1234567890", password.getValue());
    }

    @Test
    void testPasswordEquals() {
        Password password1 = Password.of("Valid@123");
        Password password2 = Password.of("Valid@123");
        assertEquals(password1, password2);
    }

    @Test
    void testPasswordNotEquals() {
        Password password1 = Password.of("Valid@123");
        Password password2 = Password.of("Different@456");
        assertNotEquals(password1, password2);
    }

    @Test
    void testPasswordEqualsNull() {
        Password password = Password.of("Valid@123");
        assertNotEquals(password, null);
    }

    @Test
    void testPasswordEqualsDifferentClass() {
        Password password = Password.of("Valid@123");
        assertNotEquals(password, "Valid@123");
    }

    @Test
    void testPasswordEqualsSameInstance() {
        Password password = Password.of("Valid@123");
        assertEquals(password, password);
    }

    @Test
    void testPasswordHashCode() {
        Password password1 = Password.of("Valid@123");
        Password password2 = Password.of("Valid@123");
        assertEquals(password1.hashCode(), password2.hashCode());
    }

    @Test
    void testPasswordHashCodeDifferent() {
        Password password1 = Password.of("Valid@123");
        Password password2 = Password.of("Different@456");
        assertNotEquals(password1.hashCode(), password2.hashCode());
    }

    @Test
    void testPasswordToString() {
        Password password = Password.of("Valid@123");
        String toString = password.toString();
        assertEquals("********", toString);
        assertFalse(toString.contains("Valid"));
        assertFalse(toString.contains("123"));
    }

    @Test
    void testPasswordWithUnicodeCharacters() {
        Password password = Password.of("密码测试Abc@123");
        assertNotNull(password);
        assertEquals("密码测试Abc@123", password.getValue());
    }

    @Test
    void testPasswordWithNumbersOnly() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("12345678");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
    }

    @Test
    void testPasswordWithLettersOnly() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("abcdefgh");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
    }

    @Test
    void testPasswordWithSpecialOnly() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("@#$%^&*()");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
    }

    @Test
    void testPasswordWithUppercaseLowercaseOnly() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("AbCdEfGh");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
    }

    @Test
    void testPasswordWithUppercaseDigitOnly() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("ABCDEFGH12345678");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
    }

    @Test
    void testPasswordWithLowercaseDigitOnly() {
        ValidationException exception = assertThrows(ValidationException.class, () -> {
            Password.of("abcdefgh12345678");
        });
        assertEquals(ErrorCode.VALIDATION_INVALID_VALUE, exception.getErrorCode());
    }
}
