package validator

import (
	"net/url"
	"strings"
	"sync"

	"github.com/go-playground/validator/v10"
)

const TokenRule = "max=2048"
const URLRule = "url,max=32768"
const VersionRule = "max=255"

var instance *validator.Validate
var once sync.Once

// GetValidator creates a singleton validator instance
func GetValidator() *validator.Validate {
	once.Do(func() {
		instance = validator.New()
		instance.RegisterValidation("sqlorder", validateSqlOrderBy)
		instance.RegisterValidation("can_id", validateCANID)
		instance.RegisterValidation("fleet", validateFleetName)
		instance.RegisterValidation("serial", validateCertSerial)
		instance.RegisterValidation("vin", validateVIN)
		instance.RegisterValidation("vins", validateVINs)
		instance.RegisterValidation("vinsuffix", validateVINSuffix)
		instance.RegisterValidation("vincheck", validateVinCheckDigit)
		instance.RegisterValidation("email", validateEmail)
		instance.RegisterValidation("ecu", validateECU)
		instance.RegisterValidation("ISO8601date", IsISO8601Date)
		instance.RegisterValidation("yyyymmdddate", IsDateYYYYMMDD)
		instance.RegisterValidation("sums_version", validateSUMSVersion)
		instance.RegisterValidation("user_consent_name", validateUserConsentName)
		instance.RegisterValidation("iccid", validateICCID)
	})
	return instance
}

// ValidateStruct validates declared fields within a struct
// variables must have "validate" tag declared within struct
func ValidateStruct(s interface{}) error {
	return GetValidator().Struct(s)
}

func ValidateField(field interface{}, tag string) error {
	return GetValidator().Var(field, tag)
}

func ValidateIDField(v int64) error {
	if v == 0 {
		return &FieldError{
			ErrorMsg: "id required",
		}
	}

	return nil
}

func ValidateURL(u string) bool {
	_, err := url.ParseRequestURI(u)

	return err == nil
}

func ValidateNonRequired(s interface{}) error {
	err := GetValidator().Struct(s)
	return getNonRequired(err)
}

func getNonRequired(err error) error {
	if err == nil {
		return nil
	}

	valerrs, ok := err.(validator.ValidationErrors)
	if ok {
		nonrequired := make(validator.ValidationErrors, 0)
		for _, val := range valerrs {
			if !strings.Contains(val.Tag(), "required") {
				nonrequired = append(nonrequired, val)
			}
		}
		if len(nonrequired) > 0 {
			return nonrequired
		}
	}

	return nil
}
