package validator

import (
	"net/http"
	"reflect"
	"strings"
	"unicode"

	"github.com/go-playground/validator/v10"
	"github.com/labstack/echo/v4"
)

// CustomValidator wraps go-playground/validator for Echo
type CustomValidator struct {
	validator *validator.Validate
}

// NewCustomValidator creates a new validator instance
func NewCustomValidator() *CustomValidator {
	v := validator.New()

	// Use JSON tag names for field names in errors
	v.RegisterTagNameFunc(func(fld reflect.StructField) string {
		name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
		if name == "-" {
			return ""
		}
		return name
	})

	// Register custom password complexity validator
	v.RegisterValidation("password_complexity", validatePasswordComplexity)

	return &CustomValidator{validator: v}
}

// validatePasswordComplexity checks that a password contains at least one
// uppercase letter, one lowercase letter, and one digit.
// Minimum length is enforced separately via the "min" tag.
func validatePasswordComplexity(fl validator.FieldLevel) bool {
	password := fl.Field().String()
	var hasUpper, hasLower, hasDigit bool
	for _, ch := range password {
		switch {
		case unicode.IsUpper(ch):
			hasUpper = true
		case unicode.IsLower(ch):
			hasLower = true
		case unicode.IsDigit(ch):
			hasDigit = true
		}
		if hasUpper && hasLower && hasDigit {
			return true
		}
	}
	return hasUpper && hasLower && hasDigit
}

// Validate implements echo.Validator interface
func (cv *CustomValidator) Validate(i interface{}) error {
	if err := cv.validator.Struct(i); err != nil {
		return err
	}
	return nil
}

// ValidationErrorResponse is the structured error response format
type ValidationErrorResponse struct {
	Error  string                `json:"error"`
	Fields map[string]FieldError `json:"fields,omitempty"`
}

// FieldError represents a single field validation error
type FieldError struct {
	Message string `json:"message"`
	Tag     string `json:"tag"`
}

// FormatValidationErrors converts validator errors to structured response
func FormatValidationErrors(err error) *ValidationErrorResponse {
	if validationErrors, ok := err.(validator.ValidationErrors); ok {
		fields := make(map[string]FieldError)
		for _, fe := range validationErrors {
			fieldName := fe.Field()
			fields[fieldName] = FieldError{
				Message: formatMessage(fe),
				Tag:     fe.Tag(),
			}
		}
		return &ValidationErrorResponse{
			Error:  "Validation failed",
			Fields: fields,
		}
	}
	return &ValidationErrorResponse{Error: err.Error()}
}

// HTTPError returns an echo.HTTPError with validation details
func HTTPError(c echo.Context, err error) error {
	return c.JSON(http.StatusBadRequest, FormatValidationErrors(err))
}

func formatMessage(fe validator.FieldError) string {
	switch fe.Tag() {
	case "required":
		return "This field is required"
	case "required_without":
		return "This field is required when " + fe.Param() + " is not provided"
	case "required_with":
		return "This field is required when " + fe.Param() + " is provided"
	case "email":
		return "Must be a valid email address"
	case "min":
		return "Must be at least " + fe.Param() + " characters"
	case "max":
		return "Must be at most " + fe.Param() + " characters"
	case "len":
		return "Must be exactly " + fe.Param() + " characters"
	case "oneof":
		return "Must be one of: " + fe.Param()
	case "url":
		return "Must be a valid URL"
	case "uuid":
		return "Must be a valid UUID"
	case "password_complexity":
		return "Password must be at least 8 characters with at least one uppercase letter, one lowercase letter, and one digit"
	default:
		return "Invalid value"
	}
}
