package options

import (
	"fmt"

	"github.com/spf13/pflag"
	"k8s.io/client-go/tools/clientcmd"

	"github.com/tidbcloud/shared-provider/cmd/dispatcher/app/config"
)

// Options is flags of dispatcher.
type Options struct {
	InfraKubeconfig string
	Kubeconfig      string

	// TODO: extract common options
	MetricsBindAddress     string
	WebhookPort            int
	HealthProbeBindAddress string
	EnableLeaderElection   bool

	KubeClusterConfigMapName      string
	KubeClusterConfigMapNamespace string
	MaxPerKubeCluster             int

	RescheduleThreshold int
}

// New returns default running options
// nolint: gomnd // all constants are default config
func New() *Options {
	opt := &Options{
		Kubeconfig:      "",
		InfraKubeconfig: "",

		MetricsBindAddress:     ":8080",
		WebhookPort:            9443,
		HealthProbeBindAddress: ":8081",
		EnableLeaderElection:   false,

		KubeClusterConfigMapName:      "region-to-kube-clusters",
		KubeClusterConfigMapNamespace: "",
		// https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-limits.html
		// nlb-eni-per-vpc / 2
		// 2 is (public nlb + private link nlb)
		MaxPerKubeCluster: 600,

		RescheduleThreshold: 550,
	}

	return opt
}

// AddFlags adds flags for manager.
func (opt *Options) AddFlags(fs *pflag.FlagSet) {
	fs.StringVar(&opt.Kubeconfig, "kubeconfig", opt.Kubeconfig,
		"kubeconfig for cluster which dispatcher deployed to.",
	)
	fs.StringVar(&opt.InfraKubeconfig, "infra-kubeconfig", opt.InfraKubeconfig,
		"kubeconfig for cluster which hosts infra api.",
	)

	fs.StringVar(&opt.MetricsBindAddress, "metrics-bind-address", opt.MetricsBindAddress,
		"The address the metric endpoint binds to.",
	)
	fs.StringVar(&opt.HealthProbeBindAddress, "health-probe-bind-address", opt.HealthProbeBindAddress,
		"The address the probe endpoint binds to.",
	)
	fs.IntVar(&opt.WebhookPort, "webhook-port", opt.WebhookPort,
		"The port of manager webhook",
	)
	fs.BoolVar(&opt.EnableLeaderElection, "enable-leader-election", opt.EnableLeaderElection,
		"Enable leader election for controller manager. "+
			"Enabling this will ensure there is only one active controller manager.",
	)

	fs.StringVar(&opt.KubeClusterConfigMapName, "kube-cluster-config-map-name", opt.KubeClusterConfigMapName,
		"name of configmap which defines map from region to available kubernetes clusters.",
	)
	fs.StringVar(&opt.KubeClusterConfigMapNamespace,
		"kube-cluster-config-map-namespace",
		opt.KubeClusterConfigMapNamespace,
		"namespace of configmap which defines map from region to available kubernetes clusters.",
	)
	fs.IntVar(&opt.MaxPerKubeCluster,
		"max-per-kube-cluster",
		opt.MaxPerKubeCluster,
		"maximum of tidb clusters that can scheduled to one kubernetes cluster.",
	)

	fs.IntVar(&opt.RescheduleThreshold,
		"reschedule-threshold",
		opt.RescheduleThreshold,
		"maximum of tidb clusters that can trigger network reschedule.",
	)
}

// Config returns config of manager
// Validation of options can be done in here.
func (opt *Options) Config() (*config.Config, error) {
	kubeconfig, err := clientcmd.BuildConfigFromFlags("", opt.Kubeconfig)
	if err != nil {
		return nil, fmt.Errorf("can't parse kubeconfig: %w", err)
	}
	infraKubeconfig, err := clientcmd.BuildConfigFromFlags("", opt.InfraKubeconfig)
	if err != nil {
		return nil, fmt.Errorf("can't parse infra kubeconfig: %w", err)
	}

	return &config.Config{
		Kubeconfig:                    kubeconfig,
		InfraKubeconfig:               infraKubeconfig,
		MetricsBindAddress:            opt.MetricsBindAddress,
		WebhookPort:                   opt.WebhookPort,
		HealthProbeBindAddress:        opt.HealthProbeBindAddress,
		EnableLeaderElection:          opt.EnableLeaderElection,
		KubeClusterConfigMapName:      opt.KubeClusterConfigMapName,
		KubeClusterConfigMapNamespace: opt.KubeClusterConfigMapNamespace,
		MaxPerKubeCluster:             opt.MaxPerKubeCluster,
		RescheduleThreshold:           opt.RescheduleThreshold,
	}, nil
}
