package validate

import (
	"errors"
	"fmt"
	"reflect"
	"strings"

	"github.com/go-playground/locales/en"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	entranslations "github.com/go-playground/validator/v10/translations/en"
)

// Validate validates structs using go-playground/validator tags and custom options.
type Validate interface {
	ValidateAll(obj any) error            // 驗證整個結構體
	BindToValidator(opts ...Option) error // 綁定客製化驗證規則
	GetValidator() *validator.Validate    // 取得原始 validator
	GetTranslator() ut.Translator         // 新增此方法以獲取翻譯器
}

// Validator is the concrete implementation of the Validate interface.
type Validator struct {
	V     *validator.Validate
	Trans ut.Translator
}

// GetTranslator returns the universal translator instance.
func (v *Validator) GetTranslator() ut.Translator {
	return v.Trans
}

// GetValidator returns the raw *validator.Validate instance.
func (v *Validator) GetValidator() *validator.Validate {
	return v.V
}

// ValidateAll validates a struct.
func (v *Validator) ValidateAll(obj any) error {
	err := v.V.Struct(obj)
	if err != nil {
		var validationErrors validator.ValidationErrors
		if errors.As(err, &validationErrors) {
			return translateValidationErrors(validationErrors, v.Trans)
		}
		// This could be an invalid argument error (e.g., not a struct)
		return err
	}
	return nil
}

// BindToValidator registers custom validation rules and their translations.
func (v *Validator) BindToValidator(opts ...Option) error {
	for _, item := range opts {
		// Register validation function
		if err := v.V.RegisterValidation(item.ValidatorName, item.ValidatorFunc); err != nil {
			return fmt.Errorf("failed to register validator '%s': %w", item.ValidatorName, err)
		}

		// Register translation message if provided
		if item.TranslationFunc != nil && item.RegisterTranslationFunc != nil {
			if err := v.V.RegisterTranslation(item.ValidatorName, v.Trans, item.RegisterTranslationFunc, item.TranslationFunc); err != nil {
				return fmt.Errorf("failed to register translation for validator '%s': %w", item.ValidatorName, err)
			}
		}
	}
	return nil
}

// NewValidator creates a new validator instance with a given translator.
// This is the most flexible way to create a validator, allowing for any locale.
func NewValidator(trans ut.Translator, opts ...Option) (Validate, error) {
	v := validator.New()

	if err := entranslations.RegisterDefaultTranslations(v, trans); err != nil {
		return nil, fmt.Errorf("failed to register default translations: %w", err)
	}

	v.RegisterTagNameFunc(func(fld reflect.StructField) string {
		name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
		if name == "-" {
			return ""
		}
		return name
	})

	validatorInstance := &Validator{
		V:     v,
		Trans: trans,
	}

	if err := validatorInstance.BindToValidator(opts...); err != nil {
		return nil, fmt.Errorf("failed to bind validator options: %w", err)
	}

	return validatorInstance, nil
}

// NewWithDefaultEN creates a new validator instance with English as the default language.
// It's a convenience wrapper around NewValidator.
func NewWithDefaultEN(opts ...Option) (Validate, error) {
	enLocale := en.New()
	uni := ut.New(enLocale, enLocale)
	trans, found := uni.GetTranslator("en")
	if !found {
		return nil, errors.New("failed to find 'en' translator")
	}
	return NewValidator(trans, opts...)
}
