package main

import (
	"fmt"
	"math/rand"
	"strconv"
	"strings"
)

type gcloud struct {
	opts *Options
}

type gkeInstance struct {
	Name string
	Zone string
}

func (g *gcloud) SetupKubectl() error {
	args := []string{
		"container",
		"clusters",
		"get-credentials",
		g.opts.ClusterName,
		"--project", g.opts.ProjectID,
	}

	location, err := g.getGKELocation()
	if err != nil {
		return err
	}

	if len(strings.Split(location, "-")) == 3 {
		args = append(args, "--zone", location)
	} else {
		args = append(args, "--region", location)
	}

	kubectlContext := fmt.Sprintf("gke_%s_%s_%s", g.opts.ProjectID, location, g.opts.ClusterName)

	msg := fmt.Sprintf("Setting %s context in kubeconfig\n", kubectlContext)
	_, err = Exec("gcloud", args, msg)
	if err != nil {
		return err
	}

	err = g.setupKubectlProxy(kubectlContext, location)
	if err != nil {
		return err
	}

	return nil
}

func (g *gcloud) setupKubectlProxy(kubectlContext, location string) error {
	args := []string{
		"config",
		"set-cluster",
		"--proxy-url", fmt.Sprintf("http://127.0.0.1:%d", g.opts.LocalPort),
		fmt.Sprintf("gke_%s_%s_%s", g.opts.ProjectID, location, g.opts.ClusterName),
	}

	msg := fmt.Sprintf("Setting http://127.0.0.1:%d HTTP proxy for %s context\n", g.opts.LocalPort, kubectlContext)
	_, err := Exec("kubectl", args, msg)
	if err != nil {
		return err
	}

	return nil
}

func (g *gcloud) StartTunnel() error {
	instances, err := g.getGKEInstances()
	if err != nil {
		return err
	}

	instance := GetRandomGKEInstance(instances)

	args := []string{
		"compute",
		"start-iap-tunnel",
		"--project", g.opts.ProjectID,
		"--zone", instance.Zone,
		"--local-host-port", fmt.Sprintf("127.0.0.1:%d", g.opts.LocalPort),
		instance.Name, strconv.Itoa(g.opts.InstancePort),
	}

	msg := fmt.Sprintf("Listening on port [%d]\n", g.opts.LocalPort)
	_, err = Exec("gcloud", args, msg)
	if err != nil {
		return err
	}

	return nil
}

func (g *gcloud) getGKELocation() (string, error) {
	args := []string{
		"container",
		"clusters",
		"list",
		"--project", g.opts.ProjectID,
		"--filter", fmt.Sprintf("name=%s", g.opts.ClusterName),
		"--format", "value(location)",
	}

	msg := fmt.Sprintf("Fetching '%s' GKE cluster location in '%s'\n", g.opts.ClusterName, g.opts.ProjectID)
	stdout, err := Exec("gcloud", args, msg)
	if err != nil {
		return "", err
	}

	return strings.TrimSpace(stdout), nil
}

func (g *gcloud) getGKEInstances() ([]gkeInstance, error) {
	args := []string{
		"compute",
		"instances",
		"list",
		"--project", g.opts.ProjectID,
		"--filter", fmt.Sprintf("name~^gke-%s-", g.opts.ClusterName),
		"--format", "csv[no-heading](name,zone)",
	}

	msg := fmt.Sprintf("Fetching instances of '%s' GKE cluster in '%s'\n", g.opts.ClusterName, g.opts.ProjectID)
	stdout, err := Exec("gcloud", args, msg)
	if err != nil {
		return []gkeInstance{}, err
	}

	var instances []gkeInstance

	rows := strings.TrimSpace(stdout)

	for _, row := range strings.Split(rows, "\n") {
		data := strings.Split(row, ",")
		instances = append(instances, gkeInstance{
			Name: data[0],
			Zone: data[1],
		})
	}

	return instances, nil
}

func GetActiveProject() string {
	args := []string{
		"config",
		"get",
		"project",
	}
	stdout, _ := Exec("gcloud", args, "")
	return strings.TrimSpace(stdout)
}

func GetRandomGKEInstance(instances []gkeInstance) gkeInstance {
	return instances[rand.Intn(len(instances))]
}