package kubernetes

import (
	"fmt"
	"net"
	"os"
	"strings"

	"github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth"
)

func newAPIConfig(sdc *SDConfig, baseDir string, swcFunc ScrapeWorkConstructorFunc) (*apiConfig, error) {
	role := sdc.role()
	switch role {
	case "node", "pod", "service", "endpoints", "endpointslice", "ingress":
	default:
		return nil, fmt.Errorf("unexpected `role`: %q; must be one of `node`, `pod`, `service`, `endpoints`, `endpointslice` or `ingress`", role)
	}
	cc := &sdc.HTTPClientConfig
	ac, err := cc.NewConfig(baseDir)
	if err != nil {
		return nil, fmt.Errorf("cannot parse auth config: %w", err)
	}
	apiServer := sdc.APIServer

	if len(sdc.KubeConfigFile) > 0 {
		if len(apiServer) > 0 {
			return nil, fmt.Errorf("`api_server: %q` and `kubeconfig_file: %q` options cannot be set simultaneously", apiServer, sdc.KubeConfigFile)
		}
		kc, err := newKubeConfig(sdc.KubeConfigFile)
		if err != nil {
			return nil, fmt.Errorf("cannot build kube config from the specified `kubeconfig_file` config option: %w", err)
		}
		opts := &promauth.Options{
			BaseDir:         baseDir,
			BasicAuth:       kc.basicAuth,
			BearerToken:     kc.token,
			BearerTokenFile: kc.tokenFile,
			OAuth2:          cc.OAuth2,
			TLSConfig:       kc.tlsConfig,
			Headers:         cc.Headers,
		}
		acNew, err := opts.NewConfig()
		if err != nil {
			return nil, fmt.Errorf("cannot initialize auth config from `kubeconfig_file: %q`: %w", sdc.KubeConfigFile, err)
		}
		ac = acNew
		apiServer = kc.server
		sdc.ProxyURL = kc.proxyURL
	}

	if len(apiServer) == 0 {
		// Assume we run at k8s pod.
		// Discover apiServer and auth config according to k8s docs.
		// See https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#service-account-admission-controller
		host := os.Getenv("KUBERNETES_SERVICE_HOST")
		port := os.Getenv("KUBERNETES_SERVICE_PORT")
		if len(host) == 0 {
			return nil, fmt.Errorf("cannot find KUBERNETES_SERVICE_HOST env var; it must be defined when running in k8s; " +
				"probably, `kubernetes_sd_config->api_server` is missing in Prometheus configs?")
		}
		if len(port) == 0 {
			return nil, fmt.Errorf("cannot find KUBERNETES_SERVICE_PORT env var; it must be defined when running in k8s; "+
				"KUBERNETES_SERVICE_HOST=%q", host)
		}
		apiServer = "https://" + net.JoinHostPort(host, port)
		tlsConfig := promauth.TLSConfig{
			CAFile: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt",
		}
		opts := &promauth.Options{
			BaseDir:         baseDir,
			BearerTokenFile: "/var/run/secrets/kubernetes.io/serviceaccount/token",
			OAuth2:          cc.OAuth2,
			TLSConfig:       &tlsConfig,
			Headers:         cc.Headers,
		}
		acNew, err := opts.NewConfig()
		if err != nil {
			return nil, fmt.Errorf("cannot initialize service account auth: %w; probably, `kubernetes_sd_config->api_server` is missing in Prometheus configs?", err)
		}
		ac = acNew
	}
	if !strings.Contains(apiServer, "://") {
		proto := "http"
		if sdc.HTTPClientConfig.TLSConfig != nil {
			proto = "https"
		}
		apiServer = proto + "://" + apiServer
	}
	for strings.HasSuffix(apiServer, "/") {
		apiServer = apiServer[:len(apiServer)-1]
	}
	// pre-check tls config
	if _, err := ac.NewTLSConfig(); err != nil {
		return nil, fmt.Errorf("cannot initialize tls config: %w", err)
	}
	aw := newAPIWatcher(apiServer, ac, sdc, swcFunc)
	cfg := &apiConfig{
		aw: aw,
	}
	return cfg, nil
}