// Package context provides the context required in the tests.
package context

import (
	"context"
	"flag"
	"fmt"
	"net/http"
	"os"

	"k8s.io/apimachinery/pkg/util/net"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/tools/portforward"
	"k8s.io/client-go/transport/spdy"
)

const (
	proxyNamespace = "tidb-admin"
	proxyName      = "cluster-proxy"
	proxyScheme    = "https"
	proxyPort      = "443"
)

var (
	kubeconfig = flag.String("kubeconfig", "", "Kubeconfig path of the cluster under test.")
	clusterId  = flag.Int("cluster-id", 42, "The ID of the cluster.")
)

type Context struct {
	clientset *kubernetes.Clientset
	config    *rest.Config
	ClusterId int
}

func New() (*Context, error) {
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		return nil, fmt.Errorf("error building kubeconfig from %s", *kubeconfig)
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, fmt.Errorf("error creating a client set from %s", *kubeconfig)
	}
	return &Context{
		config:    config,
		clientset: clientset,
		ClusterId: *clusterId,
	}, nil
}

func (c *Context) ProxyGet(path string, params map[string]string, headers map[string]string, reqbody []byte) (body []byte, err error) {
	req := c.clientset.CoreV1().RESTClient().Get().
		Namespace(proxyNamespace).
		Resource("services").
		SubResource("proxy").
		Name(net.JoinSchemeNamePort(proxyScheme, proxyName, proxyPort)).
		Suffix(path)
	for k, v := range params {
		req = req.Param(k, v)
	}
	for k, v := range headers {
		req.SetHeader(k, v)
	}
	req.Body(reqbody)
	return req.Do(context.Background()).Raw()
}

func (c *Context) Forward(pod, namespace, port string, stopChannel <-chan struct{}, readyChan chan struct{}) error {
	req := c.clientset.CoreV1().RESTClient().Post().
		Resource("pods").
		Namespace(namespace).
		Name(pod).
		SubResource("portforward")
	method := "POST"

	transport, upgrader, err := spdy.RoundTripperFor(c.config)
	if err != nil {
		return err
	}
	dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, method, req.URL())
	fw, err := portforward.NewOnAddresses(dialer, []string{"localhost"}, []string{port}, stopChannel, readyChan, os.Stdout, os.Stderr)
	if err != nil {
		return err
	}
	return fw.ForwardPorts()
}
