package validators

import (
	"fmt"
	"strings"

	"github.com/go-playground/validator/v10"
	"veza-backend-api/internal/dto"
)

// Validator est un wrapper autour de go-playground/validator
// GO-013: Validation input centralisée avec go-validator
type Validator struct {
	validate *validator.Validate
}

// NewValidator crée une nouvelle instance de Validator
func NewValidator() *Validator {
	v := validator.New()

	// Enregistrer des validations personnalisées
	registerCustomValidations(v)

	return &Validator{
		validate: v,
	}
}

// Validate valide une structure et retourne des erreurs formatées
func (v *Validator) Validate(s interface{}) []dto.ValidationError {
	var validationErrors []dto.ValidationError

	err := v.validate.Struct(s)
	if err != nil {
		if validationErrs, ok := err.(validator.ValidationErrors); ok {
			for _, fieldErr := range validationErrs {
				validationErrors = append(validationErrors, dto.ValidationError{
					Field:   getFieldName(fieldErr),
					Message: getErrorMessage(fieldErr),
					Value:   fmt.Sprintf("%v", fieldErr.Value()),
				})
			}
		}
	}

	return validationErrors
}

// ValidateVar valide une variable unique
func (v *Validator) ValidateVar(field interface{}, tag string) error {
	return v.validate.Var(field, tag)
}

// getFieldName extrait le nom du champ depuis l'erreur de validation
// GO-013: Extrait le tag JSON si disponible via StructNamespace, sinon convertit en camelCase
func getFieldName(fieldErr validator.FieldError) string {
	// Utiliser StructNamespace qui donne le chemin complet (ex: "TestStruct.Name")
	// et extraire le dernier segment
	structNamespace := fieldErr.StructNamespace()
	if structNamespace != "" {
		parts := strings.Split(structNamespace, ".")
		if len(parts) > 0 {
			fieldName := parts[len(parts)-1]
			// Convertir en camelCase pour JSON (première lettre en minuscule)
			if len(fieldName) > 0 {
				return strings.ToLower(fieldName[:1]) + fieldName[1:]
			}
			return fieldName
		}
	}
	
	// Fallback: utiliser Field() et convertir en camelCase
	fieldName := fieldErr.Field()
	if len(fieldName) > 0 {
		return strings.ToLower(fieldName[:1]) + fieldName[1:]
	}
	
	return fieldName
}

// getErrorMessage génère un message d'erreur lisible depuis l'erreur de validation
func getErrorMessage(fieldErr validator.FieldError) string {
	fieldName := getFieldName(fieldErr)

	switch fieldErr.Tag() {
	case "required":
		return fmt.Sprintf("%s is required", fieldName)
	case "email":
		return fmt.Sprintf("%s must be a valid email address", fieldName)
	case "min":
		return fmt.Sprintf("%s must be at least %s characters", fieldName, fieldErr.Param())
	case "max":
		return fmt.Sprintf("%s must be at most %s characters", fieldName, fieldErr.Param())
	case "oneof":
		return fmt.Sprintf("%s must be one of: %s", fieldName, fieldErr.Param())
	case "eqfield":
		return fmt.Sprintf("%s must equal %s", fieldName, fieldErr.Param())
	case "uuid":
		return fmt.Sprintf("%s must be a valid UUID", fieldName)
	case "url":
		return fmt.Sprintf("%s must be a valid URL", fieldName)
	case "numeric":
		return fmt.Sprintf("%s must be numeric", fieldName)
	case "alpha":
		return fmt.Sprintf("%s must contain only letters", fieldName)
	case "alphanum":
		return fmt.Sprintf("%s must contain only letters and numbers", fieldName)
	case "gte":
		return fmt.Sprintf("%s must be greater than or equal to %s", fieldName, fieldErr.Param())
	case "lte":
		return fmt.Sprintf("%s must be less than or equal to %s", fieldName, fieldErr.Param())
	case "gt":
		return fmt.Sprintf("%s must be greater than %s", fieldName, fieldErr.Param())
	case "lt":
		return fmt.Sprintf("%s must be less than %s", fieldName, fieldErr.Param())
	default:
		return fmt.Sprintf("%s is invalid", fieldName)
	}
}

// registerCustomValidations enregistre des validations personnalisées
func registerCustomValidations(v *validator.Validate) {
	// Validation pour username (alphanumeric + underscore, 3-30 chars)
	v.RegisterValidation("username", func(fl validator.FieldLevel) bool {
		username := fl.Field().String()
		if len(username) < 3 || len(username) > 30 {
			return false
		}
		for _, char := range username {
			if !((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || 
				(char >= '0' && char <= '9') || char == '_') {
				return false
			}
		}
		return true
	})

	// Validation pour UUID string
	v.RegisterValidation("uuid_string", func(fl validator.FieldLevel) bool {
		uuidStr := fl.Field().String()
		if uuidStr == "" {
			return true // Optionnel
		}
		// Utiliser le même validator pour éviter la récursion
		uuidValidator := validator.New()
		err := uuidValidator.Var(uuidStr, "uuid")
		return err == nil
	})
}

