package validators

import (
	"testing"

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

// TestValidator_Validate_RequiredFields teste la validation des champs requis
func TestValidator_Validate_RequiredFields(t *testing.T) {
	v := NewValidator()

	type TestStruct struct {
		Name  string `json:"name" validate:"required"`
		Email string `json:"email" validate:"required,email"`
	}

	// Test avec champs manquants
	invalid := TestStruct{}
	errors := v.Validate(&invalid)
	assert.NotEmpty(t, errors, "Should return validation errors for missing required fields")
	assert.GreaterOrEqual(t, len(errors), 2, "Should have at least 2 errors (name and email)")

	// Vérifier que les erreurs contiennent les bons messages
	hasNameError := false
	hasEmailError := false
	for _, err := range errors {
		if err.Field == "name" {
			hasNameError = true
			assert.Contains(t, err.Message, "required")
		}
		if err.Field == "email" {
			hasEmailError = true
			assert.Contains(t, err.Message, "required")
		}
	}
	assert.True(t, hasNameError, "Should have name field error")
	assert.True(t, hasEmailError, "Should have email field error")

	// Test avec champs valides
	valid := TestStruct{
		Name:  "John Doe",
		Email: "john@example.com",
	}
	errors = v.Validate(&valid)
	assert.Empty(t, errors, "Should not return errors for valid struct")
}

// TestValidator_Validate_Email teste la validation d'email
func TestValidator_Validate_Email(t *testing.T) {
	v := NewValidator()

	type TestStruct struct {
		Email string `json:"email" validate:"required,email"`
	}

	testCases := []struct {
		name    string
		email   string
		wantErr bool
	}{
		{"Valid email", "user@example.com", false},
		{"Invalid email", "not-an-email", true},
		{"Empty email", "", true},
		{"Email with plus", "user+tag@example.com", false},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			s := TestStruct{Email: tc.email}
			errors := v.Validate(&s)
			if tc.wantErr {
				assert.NotEmpty(t, errors, "Should return error for invalid email: %s", tc.email)
			} else {
				assert.Empty(t, errors, "Should not return error for valid email: %s", tc.email)
			}
		})
	}
}

// TestValidator_Validate_MinMax teste la validation min/max
func TestValidator_Validate_MinMax(t *testing.T) {
	v := NewValidator()

	type TestStruct struct {
		Title string `json:"title" validate:"required,min=3,max=100"`
	}

	testCases := []struct {
		name    string
		title   string
		wantErr bool
	}{
		{"Valid title", "Valid Title", false},
		{"Too short", "AB", true},
		{"Too long", string(make([]byte, 101)), true},
		{"Empty", "", true},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			s := TestStruct{Title: tc.title}
			errors := v.Validate(&s)
			if tc.wantErr {
				assert.NotEmpty(t, errors, "Should return error for: %s", tc.name)
			} else {
				assert.Empty(t, errors, "Should not return error for: %s", tc.name)
			}
		})
	}
}

// TestValidator_Validate_OneOf teste la validation oneof
func TestValidator_Validate_OneOf(t *testing.T) {
	v := NewValidator()

	type TestStruct struct {
		Type string `json:"type" validate:"required,oneof=track pack service"`
	}

	testCases := []struct {
		name    string
		typ     string
		wantErr bool
	}{
		{"Valid: track", "track", false},
		{"Valid: pack", "pack", false},
		{"Valid: service", "service", false},
		{"Invalid: album", "album", true},
		{"Empty", "", true},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			s := TestStruct{Type: tc.typ}
			errors := v.Validate(&s)
			if tc.wantErr {
				assert.NotEmpty(t, errors, "Should return error for: %s", tc.name)
			} else {
				assert.Empty(t, errors, "Should not return error for: %s", tc.name)
			}
		})
	}
}

// TestValidator_Validate_UUID teste la validation UUID
func TestValidator_Validate_UUID(t *testing.T) {
	v := NewValidator()

	type TestStruct struct {
		ID string `json:"id" validate:"omitempty,uuid"`
	}

	testCases := []struct {
		name    string
		id      string
		wantErr bool
	}{
		{"Valid UUID", "123e4567-e89b-12d3-a456-426614174000", false},
		{"Invalid UUID", "not-a-uuid", true},
		{"Empty (optional)", "", false},
		{"Invalid format", "123-456-789", true},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			s := TestStruct{ID: tc.id}
			errors := v.Validate(&s)
			if tc.wantErr {
				assert.NotEmpty(t, errors, "Should return error for: %s", tc.name)
			} else {
				assert.Empty(t, errors, "Should not return error for: %s", tc.name)
			}
		})
	}
}

// TestValidator_Validate_Username teste la validation personnalisée username
func TestValidator_Validate_Username(t *testing.T) {
	v := NewValidator()

	type TestStruct struct {
		Username string `json:"username" validate:"required,username"`
	}

	testCases := []struct {
		name     string
		username string
		wantErr  bool
	}{
		{"Valid username", "user123", false},
		{"Valid with underscore", "user_name", false},
		{"Too short", "ab", true},
		{"Too long", string(make([]byte, 31)), true},
		{"Invalid chars", "user-name", true},
		{"Empty", "", true},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			s := TestStruct{Username: tc.username}
			errors := v.Validate(&s)
			if tc.wantErr {
				assert.NotEmpty(t, errors, "Should return error for: %s", tc.name)
			} else {
				assert.Empty(t, errors, "Should not return error for: %s", tc.name)
			}
		})
	}
}

// TestValidator_ErrorFormat teste le format des erreurs
func TestValidator_ErrorFormat(t *testing.T) {
	v := NewValidator()

	type TestStruct struct {
		Name string `json:"name" validate:"required"`
	}

	s := TestStruct{}
	errors := v.Validate(&s)

	assert.NotEmpty(t, errors, "Should return errors")
	if len(errors) > 0 {
		err := errors[0]
		assert.NotEmpty(t, err.Field, "Error should have field name")
		assert.NotEmpty(t, err.Message, "Error should have message")
		// Value peut être vide pour les champs manquants
	}
}

// TestValidator_ValidateVar teste la validation d'une variable unique
func TestValidator_ValidateVar(t *testing.T) {
	v := NewValidator()

	// Test email
	err := v.ValidateVar("invalid-email", "email")
	assert.Error(t, err, "Should return error for invalid email")

	err = v.ValidateVar("valid@example.com", "email")
	assert.NoError(t, err, "Should not return error for valid email")

	// Test UUID
	err = v.ValidateVar("not-uuid", "uuid")
	assert.Error(t, err, "Should return error for invalid UUID")

	err = v.ValidateVar("123e4567-e89b-12d3-a456-426614174000", "uuid")
	assert.NoError(t, err, "Should not return error for valid UUID")
}
