package validators

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestNewPasswordValidator(t *testing.T) {
	validator := NewPasswordValidator()
	assert.NotNil(t, validator)
	assert.Equal(t, 12, validator.MinLength)
}

func TestPasswordValidator_Validate_ValidPassword(t *testing.T) {
	validator := NewPasswordValidator()

	tests := []struct {
		name     string
		password string
	}{
		{
			name:     "valid password with all requirements",
			password: "SecurePass123!",
		},
		{
			name:     "valid password with special chars",
			password: "MyP@ssw0rd!Test",
		},
		{
			name:     "valid password with multiple special chars",
			password: "Test#123$Pass",
		},
		{
			name:     "valid password exactly 12 chars",
			password: "Test123!Pass",
		},
		{
			name:     "valid password longer than 12 chars",
			password: "VerySecurePassword123!@#",
		},
		{
			name:     "valid password with parentheses",
			password: "Test(123)Pass",
		},
		{
			name:     "valid password with comma",
			password: "Test,123Pass",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			strength, err := validator.Validate(tt.password)
			assert.NoError(t, err)
			assert.True(t, strength.Valid, "Password should be valid: %s", tt.password)
			assert.Equal(t, 4, strength.Score, "Password should have score 4")
			assert.Empty(t, strength.Details, "Should have no validation errors")
		})
	}
}

func TestPasswordValidator_Validate_InvalidLength(t *testing.T) {
	validator := NewPasswordValidator()

	tests := []struct {
		name     string
		password string
	}{
		{
			name:     "password too short - 11 chars",
			password: "Test123!Pas",
		},
		{
			name:     "password too short - 5 chars",
			password: "Test1",
		},
		{
			name:     "empty password",
			password: "",
		},
		{
			name:     "password too short - 1 char",
			password: "T",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			strength, err := validator.Validate(tt.password)
			assert.NoError(t, err)
			assert.False(t, strength.Valid, "Password should be invalid: %s", tt.password)
			assert.Contains(t, strength.Details, "Password must be at least 12 characters long")
		})
	}
}

func TestPasswordValidator_Validate_MissingUpperCase(t *testing.T) {
	validator := NewPasswordValidator()

	tests := []struct {
		name     string
		password string
	}{
		{
			name:     "password without uppercase",
			password: "testpassword123!",
		},
		{
			name:     "password without uppercase - all lowercase",
			password: "lowercase123!@#",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			strength, err := validator.Validate(tt.password)
			assert.NoError(t, err)
			assert.False(t, strength.Valid, "Password should be invalid: %s", tt.password)
			assert.Contains(t, strength.Details, "Must contain uppercase letter")
		})
	}
}

func TestPasswordValidator_Validate_MissingLowerCase(t *testing.T) {
	validator := NewPasswordValidator()

	tests := []struct {
		name     string
		password string
	}{
		{
			name:     "password without lowercase",
			password: "TESTPASSWORD123!",
		},
		{
			name:     "password without lowercase - all uppercase",
			password: "UPPERCASE123!@#",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			strength, err := validator.Validate(tt.password)
			assert.NoError(t, err)
			assert.False(t, strength.Valid, "Password should be invalid: %s", tt.password)
			assert.Contains(t, strength.Details, "Must contain lowercase letter")
		})
	}
}

func TestPasswordValidator_Validate_MissingNumber(t *testing.T) {
	validator := NewPasswordValidator()

	tests := []struct {
		name     string
		password string
	}{
		{
			name:     "password without number",
			password: "TestPassword!",
		},
		{
			name:     "password without number - only letters and special",
			password: "TestPass!@#$",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			strength, err := validator.Validate(tt.password)
			assert.NoError(t, err)
			assert.False(t, strength.Valid, "Password should be invalid: %s", tt.password)
			assert.Contains(t, strength.Details, "Must contain number")
		})
	}
}

func TestPasswordValidator_Validate_MissingSpecialChar(t *testing.T) {
	validator := NewPasswordValidator()

	tests := []struct {
		name     string
		password string
	}{
		{
			name:     "password without special char",
			password: "TestPassword123",
		},
		{
			name:     "password without special char - only alphanumeric",
			password: "TestPass9753", // Pas de pattern séquentiel (9753 n'est pas dans les séquences)
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			strength, err := validator.Validate(tt.password)
			assert.NoError(t, err)
			assert.False(t, strength.Valid, "Password should be invalid: %s", tt.password)
			assert.Contains(t, strength.Details, "Must contain special character")
		})
	}
}

func TestPasswordValidator_Validate_MultipleMissing(t *testing.T) {
	validator := NewPasswordValidator()

	tests := []struct {
		name           string
		password       string
		expectedErrors []string
	}{
		{
			name:           "missing uppercase and lowercase",
			password:       "9753108642!!", // 12 caractères, pas de pattern séquentiel (mélange de chiffres non séquentiels)
			expectedErrors: []string{"Must contain uppercase letter", "Must contain lowercase letter"},
		},
		{
			name:           "missing number and special char",
			password:       "TestPassword",
			expectedErrors: []string{"Must contain number", "Must contain special character"},
		},
		{
			name:           "missing uppercase, number and special",
			password:       "testpassword",
			expectedErrors: []string{"Must contain uppercase letter", "Must contain number", "Must contain special character"},
		},
		{
			name:           "missing everything except length",
			password:       "alllowercase",
			expectedErrors: []string{"Must contain uppercase letter", "Must contain number", "Must contain special character"},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			strength, err := validator.Validate(tt.password)
			assert.NoError(t, err)
			assert.False(t, strength.Valid, "Password should be invalid: %s", tt.password)
			for _, expectedError := range tt.expectedErrors {
				assert.Contains(t, strength.Details, expectedError)
			}
		})
	}
}

func TestPasswordValidator_Validate_Score(t *testing.T) {
	validator := NewPasswordValidator()

	tests := []struct {
		name          string
		password      string
		expectedScore int
		expectedValid bool
	}{
		{
			name:          "valid password - score 4",
			password:      "Test123!Pass",
			expectedScore: 4,
			expectedValid: true,
		},
		{
			name:          "missing uppercase - score 3",
			password:      "test123!pass",
			expectedScore: 3,
			expectedValid: false,
		},
		{
			name:          "missing lowercase - score 3",
			password:      "TEST123!PASS",
			expectedScore: 3,
			expectedValid: false,
		},
		{
			name:          "missing number - score 3",
			password:      "TestPass!Word",
			expectedScore: 3,
			expectedValid: false,
		},
		{
			name:          "missing special - score 3",
			password:      "TestPass9753", // Pas de pattern séquentiel (9753 n'est pas dans les séquences)
			expectedScore: 3,
			expectedValid: false,
		},
		{
			name:          "missing 3 requirements - score 1 (only lowercase)",
			password:      "testpassword",
			expectedScore: 1,
			expectedValid: false,
		},
		{
			name:          "too short - returns early with score 0",
			password:      "testonly",
			expectedScore: 0,
			expectedValid: false,
		},
		{
			name:          "only lowercase - score 1",
			password:      "lowercaseonly",
			expectedScore: 1,
			expectedValid: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			strength, err := validator.Validate(tt.password)
			assert.NoError(t, err)
			assert.Equal(t, tt.expectedValid, strength.Valid)
			assert.Equal(t, tt.expectedScore, strength.Score)
		})
	}
}

func TestPasswordValidator_Validate_SpecialCharacters(t *testing.T) {
	validator := NewPasswordValidator()

	specialChars := []string{"!", "@", "#", "$", "%", "^", "&", "*", "(", ")", ",", ".", "?", ":", "\"", "{", "}", "|", "<", ">"}

	for _, special := range specialChars {
		t.Run("special char: "+special, func(t *testing.T) {
			password := "TestPass123" + special
			strength, err := validator.Validate(password)
			assert.NoError(t, err)
			assert.True(t, strength.Valid, "Password with special char %s should be valid", special)
		})
	}
}

func TestPasswordValidator_Validate_EdgeCases(t *testing.T) {
	validator := NewPasswordValidator()

	tests := []struct {
		name     string
		password string
		want     bool
	}{
		{
			name:     "password with unicode characters",
			password: "Test123!Passé",
			want:     true,
		},
		{
			name:     "password with spaces",
			password: "Test 123!Pass",
			want:     true, // Spaces are allowed, just not in the special char regex
		},
		{
			name:     "password with tabs",
			password: "Test123!Pass\t",
			want:     true,
		},
		{
			name:     "password with newlines",
			password: "Test123!Pass\n",
			want:     true,
		},
		{
			name:     "password with special chars not in regex",
			password: "Test123-Pass_", // - and _ are not in the special char regex
			want:     false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			strength, err := validator.Validate(tt.password)
			assert.NoError(t, err)
			assert.Equal(t, tt.want, strength.Valid)
		})
	}
}
