package errors

import (
	"testing"

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

// TestAppError_Error teste le formatage des messages d'erreur
func TestAppError_Error(t *testing.T) {
	tests := []struct {
		name     string
		err      *AppError
		expected string
	}{
		{
			name:     "error without wrapped error",
			err:      New(ErrCodeValidation, "Invalid input"),
			expected: "[2000] Invalid input",
		},
		{
			name:     "error with wrapped error",
			err:      Wrap(ErrCodeDatabase, "Database query failed", assert.AnError),
			expected: "[9001] Database query failed: assert.AnError general error for testing",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tt.err.Error()
			assert.Contains(t, result, tt.expected)
		})
	}
}

// TestAppError_Unwrap teste la fonction Unwrap
func TestAppError_Unwrap(t *testing.T) {
	wrappedErr := assert.AnError
	err := Wrap(ErrCodeDatabase, "Database error", wrappedErr)

	assert.Equal(t, wrappedErr, err.Unwrap())
	assert.Nil(t, New(ErrCodeValidation, "Test").Unwrap())
}

// TestNew teste la création d'une nouvelle AppError
func TestNew(t *testing.T) {
	err := New(ErrCodeValidation, "Test message")

	assert.Equal(t, ErrCodeValidation, err.Code)
	assert.Equal(t, "Test message", err.Message)
	assert.Nil(t, err.Err)
	assert.Empty(t, err.Details)
}

// TestWrap teste l'enveloppement d'une erreur
func TestWrap(t *testing.T) {
	wrappedErr := assert.AnError
	err := Wrap(ErrCodeInternal, "Internal error", wrappedErr)

	assert.Equal(t, ErrCodeInternal, err.Code)
	assert.Equal(t, "Internal error", err.Message)
	assert.Equal(t, wrappedErr, err.Err)
}

// TestNewValidationError teste la création d'une erreur de validation
func TestNewValidationError(t *testing.T) {
	details := []ErrorDetail{
		{Field: "email", Message: "Invalid format"},
		{Field: "password", Message: "Too short"},
	}

	err := NewValidationError("Validation failed", details...)

	assert.Equal(t, ErrCodeValidation, err.Code)
	assert.Equal(t, "Validation failed", err.Message)
	assert.Len(t, err.Details, 2)
	assert.Equal(t, "email", err.Details[0].Field)
	assert.Equal(t, "Invalid format", err.Details[0].Message)
	assert.Equal(t, "password", err.Details[1].Field)
	assert.Equal(t, "Too short", err.Details[1].Message)
}

// TestNewValidationError_NoDetails teste la création sans détails
func TestNewValidationError_NoDetails(t *testing.T) {
	err := NewValidationError("Validation failed")

	assert.Equal(t, ErrCodeValidation, err.Code)
	assert.Equal(t, "Validation failed", err.Message)
	assert.Empty(t, err.Details)
}

// TestNewNotFoundError teste la création d'une erreur "not found"
func TestNewNotFoundError(t *testing.T) {
	err := NewNotFoundError("User")

	assert.Equal(t, ErrCodeNotFound, err.Code)
	assert.Equal(t, "User not found", err.Message)
}

// TestNewUnauthorizedError teste la création d'une erreur d'autorisation
func TestNewUnauthorizedError(t *testing.T) {
	err := NewUnauthorizedError("Invalid token")

	assert.Equal(t, ErrCodeUnauthorized, err.Code)
	assert.Equal(t, "Invalid token", err.Message)
}
