// pkg/utils/validation.go
package utils

import (
	"fmt"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"

	"go.uber.org/zap"
)

// ValidationError представляет ошибку валидации
type ValidationError struct {
	Field   string `json:"field"`
	Message string `json:"message"`
}

func (e ValidationError) Error() string {
	return fmt.Sprintf("%s: %s", e.Field, e.Message)
}

// ValidationResult содержит результат валидации
type ValidationResult struct {
	IsValid bool
	Errors  []ValidationError
}

// TagOptions содержит опции из тега validate
type TagOptions struct {
	Required  bool
	Min       *float64
	Max       *float64
	MinInt    *int64
	MaxInt    *int64
	OneOf     []string
	Email     bool
	MaxLength *int
	MinLength *int
	Custom    string
}

// ValidateStruct валидирует структуру на основе тегов validate
func ValidateStruct(s interface{}) error {
	val := reflect.ValueOf(s)
	if val.Kind() == reflect.Ptr {
		val = val.Elem()
	}

	if val.Kind() != reflect.Struct {
		return fmt.Errorf("ValidateStruct expects a struct, got %T", s)
	}

	var errors []ValidationError
	typ := val.Type()

	for i := 0; i < val.NumField(); i++ {
		field := val.Field(i)
		fieldType := typ.Field(i)

		// Пропускаем неэкспортируемые поля
		if !field.CanInterface() {
			continue
		}

		tag := fieldType.Tag.Get("validate")
		if tag == "" {
			continue
		}

		options := parseTagOptions(tag)
		fieldName := getFieldName(fieldType)

		// Валидация поля
		if err := validateField(field, fieldName, options); err != nil {
			errors = append(errors, err...)
		}
	}

	if len(errors) > 0 {
		return &ValidationResult{
			IsValid: false,
			Errors:  errors,
		}
	}

	return nil
}

// parseTagOptions парсит тег validate и возвращает опции
func parseTagOptions(tag string) TagOptions {
	options := TagOptions{}
	parts := strings.Split(tag, ",")

	for _, part := range parts {
		part = strings.TrimSpace(part)

		switch {
		case part == "required":
			options.Required = true
		case part == "email":
			options.Email = true
		case strings.HasPrefix(part, "min="):
			if val, err := strconv.ParseFloat(part[4:], 64); err == nil {
				options.Min = &val
			}
		case strings.HasPrefix(part, "max="):
			if val, err := strconv.ParseFloat(part[4:], 64); err == nil {
				options.Max = &val
			}
		case strings.HasPrefix(part, "minint="):
			if val, err := strconv.ParseInt(part[7:], 10, 64); err == nil {
				options.MinInt = &val
			}
		case strings.HasPrefix(part, "maxint="):
			if val, err := strconv.ParseInt(part[7:], 10, 64); err == nil {
				options.MaxInt = &val
			}
		case strings.HasPrefix(part, "oneof="):
			options.OneOf = strings.Split(part[6:], " ")
		case strings.HasPrefix(part, "maxlen="):
			if val, err := strconv.Atoi(part[7:]); err == nil {
				options.MaxLength = &val
			}
		case strings.HasPrefix(part, "minlen="):
			if val, err := strconv.Atoi(part[7:]); err == nil {
				options.MinLength = &val
			}
		case strings.HasPrefix(part, "custom="):
			options.Custom = part[7:]
		}
	}

	return options
}

// getFieldName возвращает имя поля для сообщений об ошибках
func getFieldName(field reflect.StructField) string {
	jsonTag := field.Tag.Get("json")
	if jsonTag != "" {
		parts := strings.Split(jsonTag, ",")
		if parts[0] != "" {
			return parts[0]
		}
	}
	return field.Name
}

// validateField валидирует отдельное поле
func validateField(field reflect.Value, fieldName string, options TagOptions) []ValidationError {
	var errors []ValidationError

	// Проверка required
	if options.Required {
		if isEmptyValue(field) {
			errors = append(errors, ValidationError{
				Field:   fieldName,
				Message: "field is required",
			})
			return errors // Если поле обязательно и пустое, дальше не проверяем
		}
	}

	// Если поле пустое и не обязательное, дальше не проверяем
	if isEmptyValue(field) {
		return errors
	}

	// Валидация в зависимости от типа поля
	switch field.Kind() {
	case reflect.String:
		errors = append(errors, validateString(field.String(), fieldName, options)...)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		errors = append(errors, validateInt(field.Int(), fieldName, options)...)
	case reflect.Float32, reflect.Float64:
		errors = append(errors, validateFloat(field.Float(), fieldName, options)...)
	case reflect.Struct:
		// Для time.Time и других структур
		if field.Type().String() == "time.Time" {
			errors = append(errors, validateTime(field.Interface().(time.Time), fieldName, options)...)
		}
	}

	return errors
}

// validateString валидирует строковые поля
func validateString(value, fieldName string, options TagOptions) []ValidationError {
	var errors []ValidationError

	// Проверка email
	if options.Email {
		if !isValidEmail(value) {
			errors = append(errors, ValidationError{
				Field:   fieldName,
				Message: "invalid email format",
			})
		}
	}

	// Проверка длины строки
	if options.MinLength != nil && len(value) < *options.MinLength {
		errors = append(errors, ValidationError{
			Field:   fieldName,
			Message: fmt.Sprintf("minimum length is %d characters", *options.MinLength),
		})
	}

	if options.MaxLength != nil && len(value) > *options.MaxLength {
		errors = append(errors, ValidationError{
			Field:   fieldName,
			Message: fmt.Sprintf("maximum length is %d characters", *options.MaxLength),
		})
	}

	// Проверка oneof
	if len(options.OneOf) > 0 {
		valid := false
		for _, allowed := range options.OneOf {
			if value == allowed {
				valid = true
				break
			}
		}
		if !valid {
			errors = append(errors, ValidationError{
				Field:   fieldName,
				Message: fmt.Sprintf("must be one of: %s", strings.Join(options.OneOf, ", ")),
			})
		}
	}

	return errors
}

// validateInt валидирует целочисленные поля
func validateInt(value int64, fieldName string, options TagOptions) []ValidationError {
	var errors []ValidationError

	if options.MinInt != nil && value < *options.MinInt {
		errors = append(errors, ValidationError{
			Field:   fieldName,
			Message: fmt.Sprintf("minimum value is %d", *options.MinInt),
		})
	}

	if options.MaxInt != nil && value > *options.MaxInt {
		errors = append(errors, ValidationError{
			Field:   fieldName,
			Message: fmt.Sprintf("maximum value is %d", *options.MaxInt),
		})
	}

	return errors
}

// validateFloat валидирует поля с плавающей точкой
func validateFloat(value float64, fieldName string, options TagOptions) []ValidationError {
	var errors []ValidationError

	if options.Min != nil && value < *options.Min {
		errors = append(errors, ValidationError{
			Field:   fieldName,
			Message: fmt.Sprintf("minimum value is %.2f", *options.Min),
		})
	}

	if options.Max != nil && value > *options.Max {
		errors = append(errors, ValidationError{
			Field:   fieldName,
			Message: fmt.Sprintf("maximum value is %.2f", *options.Max),
		})
	}

	return errors
}

// validateTime валидирует временные поля
func validateTime(value time.Time, fieldName string, options TagOptions) []ValidationError {
	var errors []ValidationError

	// Проверка, что дата не нулевая
	if value.IsZero() && options.Required {
		errors = append(errors, ValidationError{
			Field:   fieldName,
			Message: "date is required",
		})
	}

	// Проверка, что дата не в будущем (пример кастомной валидации)
	if options.Custom == "not_future" && value.After(time.Now()) {
		errors = append(errors, ValidationError{
			Field:   fieldName,
			Message: "date cannot be in the future",
		})
	}

	return errors
}

// isEmptyValue проверяет, является ли значение пустым
func isEmptyValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.String:
		return v.String() == ""
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Struct:
		if v.Type().String() == "time.Time" {
			return v.Interface().(time.Time).IsZero()
		}
	case reflect.Ptr, reflect.Interface:
		return v.IsNil()
	case reflect.Slice, reflect.Map, reflect.Array:
		return v.Len() == 0
	}
	return false
}

// isValidEmail проверяет валидность email
func isValidEmail(email string) bool {
	emailRegex := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
	matched, _ := regexp.MatchString(emailRegex, email)
	return matched
}

// Error возвращает строковое представление ошибок валидации
func (vr *ValidationResult) Error() string {
	var errorMessages []string
	for _, err := range vr.Errors {
		errorMessages = append(errorMessages, err.Error())
	}
	return strings.Join(errorMessages, "; ")
}

// GetValidationErrors возвращает ошибки валидации в структурированном виде
func GetValidationErrors(err error) []ValidationError {
	if vr, ok := err.(*ValidationResult); ok {
		return vr.Errors
	}
	return nil
}

// LogValidationErrors логирует ошибки валидации
func LogValidationErrors(logger *zap.Logger, err error, context string) {
	if vr, ok := err.(*ValidationResult); ok {
		for _, validationErr := range vr.Errors {
			logger.Warn("validation error",
				zap.String("context", context),
				zap.String("field", validationErr.Field),
				zap.String("error", validationErr.Message),
			)
		}
	}
}

// ParseUintFromQuery парсит uint из query параметра
func ParseUintFromQuery(queryParam string, defaultValue uint) (uint, error) {
	if queryParam == "" {
		return defaultValue, nil
	}

	value, err := strconv.ParseUint(queryParam, 10, 32)
	if err != nil {
		return defaultValue, err
	}

	return uint(value), nil
}

// ParseIntFromQuery парсит int из query параметра
func ParseIntFromQuery(queryParam string, defaultValue int) (int, error) {
	if queryParam == "" {
		return defaultValue, nil
	}

	value, err := strconv.Atoi(queryParam)
	if err != nil {
		return defaultValue, err
	}

	return value, nil
}

// ParseBoolFromQuery парсит bool из query параметра
func ParseBoolFromQuery(queryParam string, defaultValue bool) bool {
	if queryParam == "" {
		return defaultValue
	}

	return strings.ToLower(queryParam) == "true" || queryParam == "1"
}
