package validators

import (
	"errors"
	"fmt"
	"net/url"
	"reflect"
	"regexp"
	"strconv"
	"strings"

	"github.com/go-playground/validator/v10"
	"repository.basistech.ru/BASIS/dynamix-golang-sdk/v14/interfaces"
	"repository.basistech.ru/BASIS/dynamix-golang-sdk/v14/internal/multierror"
)

// protoValidator is used to validate Proto fields.
func protoValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, protoValues)
}

// apiGroupValidator is used to validate APIGroup fields
func apiGroupValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, apiGroupValues)
}

// accessTypeValidator is used to validate AccessType fields.
func accessTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, accessTypeValues)
}

// resTypesValidator is used to validate ResTypes fields.
func resTypesValidator(fe validator.FieldLevel) bool {
	fieldSlice, ok := fe.Field().Interface().([]string)
	if !ok {
		return false
	}

	for _, value := range fieldSlice {
		if !IsInSlice(value, resTypesValues) {
			return false
		}
	}

	return true
}

// accountCUTypeValidator is used to validate CUType field.
func accountCUTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, accountCUTypeValues)
}

// bserviceModeValidator is used to validate Mode field.
func bserviceModeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, bserviceModeValues)
}

// computeTopologyValidator is used to validate Topology field.
func computeTopologyValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, computeTopologyValues)
}

// computePolicyValidator is used to validate Policy field.
func computePolicyValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, computePolicyValues)
}

// computeModeValidator is used to validate Mode field.
func computeModeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, computeModeValues)
}

// computeNetTypeValidator is used to validate NetType field.
func computeNetTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, computeNetTypeValues)
}

// computex86NetTypeValidator is used to validate NetType field.
func computex86NetTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, computex86NetTypeValues)
}

// securityGroupDirectionValidator is used to validate Direction field
func securityGroupDirectionValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, securityGroupDirectionValues)
}

// securityGroupEthertypeValidator is used to validate Ethertype field
func securityGroupEthertypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, securityGroupEthertypeValues)
}

// securityGroupProtocolValidator is used to validate Protocol field
func securityGroupProtocolValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, securityGroupProtocolValues)
}

// computeOrderValidator is used to validate Order field.
func computeOrderValidator(fe validator.FieldLevel) bool {
	fieldSlice, ok := fe.Field().Interface().([]string)
	if !ok {
		return false
	}

	for _, value := range fieldSlice {
		if !IsInSlice(value, computeOrderValues) {
			return false
		}
	}

	return true
}

// computeDataDisksValidator is used to validate DataDisks field.
func computeDataDisksValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, computeDataDisksValues)
}

// flipgroupClientTypeValidator is used to validate ClientType field.
func flipgroupClientTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, flipgroupClientTypeValues)
}

// massCreateTypeValidator is used to validate net type field when mass creating kvm
func massCreateTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, massCreateNetTypeValues)
}

// lbAlgorithmValidator is used to validate Algorithm field.
func lbAlgorithmValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, lbAlgorithmValues)
}

// rgDefNetValidator is used to validate DefNet field.
func rgDefNetValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, rgDefNetValues)
}

// rgNetTypeValidator is used to validate NetType field.
func rgNetTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, rgNetTypeValues)
}

// vinsTypeValidator is used to validate Type field.
func vinsTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, vinsTypeValues)
}

// imageBootTypeValidator is used to validate BootType field.
func imageBootTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, imageBootTypeValues)
}

// imageTypeValidator is used to validate ImageType field.
func imageTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, imageTypeValues)
}

// imageArchitectureValidator is used to validate Architecture field.
func imageArchitectureValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, imageArchitectureValues)
}

// sepFieldTypeValidator is used to validate FieldType field.
func sepFieldTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, sepFieldTypeValues)
}

// hwPathValidator is used to validate HWPath field.
func hwPathValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	ok, _ := regexp.MatchString(`^\b[0-9a-f]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.\d{1}$`, fieldValue)

	return ok
}

// networkPluginValidator is used to validate NetworkPlugin field
func networkPluginValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()
	fieldValue = strings.ToLower(fieldValue)

	return IsInSlice(fieldValue, networkPluginValues)
}

// networkPluginsValidator is used to validate NetworkPlugins field
func networkPluginsValidator(fe validator.FieldLevel) bool {
	fieldSlice, ok := fe.Field().Interface().([]string)
	if !ok {
		return false
	}

	for _, item := range fieldSlice {
		item = strings.ToLower(item)

		if !IsInSlice(item, networkPluginValues) {
			return false
		}
	}

	return true
}

func interfaceStateValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()
	fieldValue = strings.ToLower(fieldValue)

	return IsInSlice(fieldValue, interfaceStateValues)
}

func interfaceTXModelValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()
	fieldValue = strings.ToLower(fieldValue)

	return IsInSlice(fieldValue, txModelValues)
}

func interfaceIOEventFDValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()
	fieldValue = strings.ToLower(fieldValue)

	return IsInSlice(fieldValue, ioEventFDValues)
}

func interfaceEventIDxValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()
	fieldValue = strings.ToLower(fieldValue)

	return IsInSlice(fieldValue, eventIDxValues)
}

func strictLooseValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()
	fieldValue = strings.ToLower(fieldValue)

	return IsInSlice(fieldValue, strictLooseValues)
}

// name workerGroup must be more 3 symbol
func workerGroupNameValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()
	fieldValue = strings.Trim(fieldValue, " ")

	return len(fieldValue) >= 3
}

func sortByValidator(fe validator.FieldLevel) bool {

	sortByRegexp := regexp.MustCompile(`^[+-][a-zA-Z_]+`)

	return sortByRegexp.MatchString(fe.Field().String())
}

func actionValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, actionValues)
}

func vmActionValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, vmActionValues)
}

func mtuValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().Uint()

	return fieldValue >= uint64(mtuMin) && fieldValue <= uint64(mtuMax)
}

func computeFeaturesValidator(fe validator.FieldLevel) bool {
	field := fe.Field()
	slice, ok := field.Interface().([]string)
	if !ok {
		return false
	}

	return IsSubSlice(slice, computeFeaturesValues)
}

func networkInterfaceNamingValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, networkInterfaceNamingValues)
}

func numaAffinityValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, numaAffinityValues)
}

// kvmx86NetTypeValidator is used to validate NetType field for x86 compute.
func kvmx86NetTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, kvmx86NetTypeValues)
}

func isBoolTypeValidator(fe validator.FieldLevel) bool {
	return fe.Field().CanConvert(reflect.TypeOf(true))
}

func isStringTypeValidator(fe validator.FieldLevel) bool {
	return fe.Field().Kind() == reflect.String
}

func urlValidartor(fl validator.FieldLevel) bool {
	fieldValues := fl.Field().String()

	_, err := url.ParseRequestURI(fieldValues)
	return err == nil
}

func chipsetValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, chipsetValues)
}

func preferredCPUValidator(fe validator.FieldLevel) bool {
	fieldSlice, ok := fe.Field().Interface().([]int64)
	if !ok {
		return false
	}

	for _, value := range fieldSlice {
		if value < -1 {
			return false
		}
	}

	return true
}

// loaderTypeValidator is used to validate loaderType fields
func loaderTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, loaderTypeValues)
}

// languageValidator is used to validate language fields
func languageValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, languageValues)
}

func userProviderValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, userProviders)
}

// sepTypeValidator is used to validate sepType fields
func sepTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, sepTypeValues)
}

// deviceValidator is used to validate extnet device fields
func deviceValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, deviceValues)
}

// ipTypesValidator is used to validate ip types version fields
func ipTypesValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, ipTypeValues)
}

// ValidateRAM checks if request contains RAM value that is positive integer divisible by divisibility passed.
// It is recommended to pass constants.RAM_DIVISIBILITY as divisility arguement
func ValidateRAM(r interfaces.RequestWithRAM, divisibility uint64) error {

	if divisibility == 0 {

		return errors.New("second argument of ValidateRAM should be greater than 0")
	}
	mapRAM := r.GetRAM()

	errs := make([]error, 0, len(mapRAM))

	for k, v := range mapRAM {

		if v%divisibility != 0 {

			errs = append(errs, fmt.Errorf("expected value of %s: \"%d\" should be divisible by %d", k, v, divisibility))
		}
	}
	return multierror.Join(errs...)
}

// trunkTagsValidator checks if trunk_tags is in range from 1 to 4095
func trunkTagsValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()
	numFieldValue, err := strconv.ParseInt(fieldValue, 10, 64)
	if err != nil {
		return false
	}
	return uint64(numFieldValue) >= uint64(trunkTagsMin) && uint64(numFieldValue) <= uint64(trunkTagsMax)
}

// addressPoolNetTypeValidator is used to validate NetAddressType fields
func addressPoolNetTypeValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, addressPoolNetTypeValues)
}

// sepTechStatusValidator is used to validate SepTechStatus fields
func sepTechStatusValidator(fe validator.FieldLevel) bool {
	fieldValue := fe.Field().String()

	return IsInSlice(fieldValue, sepTechStatusValues)
}
