package e2e_arc

import (
	"bytes"
	"context"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
)

type podCountsByType struct {
	controllers int
	listeners   int
	runners     int
}

func getPodsByType(clientset *kubernetes.Clientset) podCountsByType {
	arc_namespace := "arc-systems"
	availableArcPods, err := clientset.CoreV1().Pods(arc_namespace).List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		panic(err.Error())
	}
	runners_namespace := "arc-runners"
	availableRunnerPods, err := clientset.CoreV1().Pods(runners_namespace).List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		panic(err.Error())
	}
	podsByType := podCountsByType{}
	for _, pod := range availableArcPods.Items {
		if strings.Contains(pod.Name, "controller") {
			podsByType.controllers += 1
		}
		if strings.Contains(pod.Name, "listener") {
			podsByType.listeners += 1
		}
	}
	for _, pod := range availableRunnerPods.Items {
		if strings.Contains(pod.Name, "runner") {
			podsByType.runners += 1
		}
	}
	return podsByType
}

func pollForClusterState(clientset *kubernetes.Clientset, expectedPodsCount podCountsByType, maxTime int) bool {
	sleepTime := 5
	maxRetries := maxTime / sleepTime
	success := false
	for i := 0; i <= maxRetries; i++ {
		time.Sleep(time.Second * time.Duration(sleepTime))
		availablePodsCount := getPodsByType(clientset)
		if availablePodsCount == expectedPodsCount {
			success = true
			break
		} else {
			fmt.Printf("%v", availablePodsCount)
		}
	}
	return success
}

func TestARCJobs(t *testing.T) {
	configFile := filepath.Join(
		os.Getenv("HOME"), ".kube", "config",
	)

	config, err := clientcmd.BuildConfigFromFlags("", configFile)
	if err != nil {
		t.Fatal(err)
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		t.Fatal(err)
	}

	t.Run("Get available pods before job run", func(t *testing.T) {
		expectedPodsCount := podCountsByType{1, 1, 0}
		success := pollForClusterState(clientset, expectedPodsCount, 60)
		if !success {
			t.Fatal("Expected pods count did not match available pods count before job run.")
		}
	},
	)
	t.Run("Get available pods during job run", func(t *testing.T) {
		c := http.Client{}
		targetArcName := os.Getenv("ARC_NAME")
		require.NotEmpty(t, targetArcName, "ARC_NAME environment variable is required for this test to run. (e.g. arc-e2e-test)")

		targetWorkflow := os.Getenv("WORKFLOW_FILE")
		require.NotEmpty(t, targetWorkflow, "WORKFLOW_FILE environment variable is required for this test to run. (e.g. e2e_test.yml)")

		ght := os.Getenv("GITHUB_TOKEN")
		require.NotEmpty(t, ght, "GITHUB_TOKEN environment variable is required for this test to run.")

		// We are triggering manually a workflow that already exists in the repo.
		// This workflow is expected to spin up a number of runner pods matching the runners value set in podCountsByType.
		url := "https://api.github.com/repos/actions-runner-controller/arc_e2e_test_dummy/actions/workflows/" + targetWorkflow + "/dispatches"
		jsonStr := []byte(fmt.Sprintf(`{"ref":"main", "inputs":{"arc_name":"%s"}}`, targetArcName))

		req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
		if err != nil {
			t.Fatal(err)
		}
		req.Header.Add("Accept", "application/vnd.github+json")
		req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", ght))
		req.Header.Add("X-GitHub-Api-Version", "2022-11-28")

		resp, err := c.Do(req)
		if err != nil {
			t.Fatal(err)
		}
		defer resp.Body.Close()

		expectedPodsCount := podCountsByType{1, 1, 3}
		success := pollForClusterState(clientset, expectedPodsCount, 120)
		if !success {
			t.Fatal("Expected pods count did not match available pods count during job run.")
		}
	},
	)
	t.Run("Get available pods after job run", func(t *testing.T) {
		expectedPodsCount := podCountsByType{1, 1, 0}
		success := pollForClusterState(clientset, expectedPodsCount, 120)
		if !success {
			t.Fatal("Expected pods count did not match available pods count after job run.")
		}
	},
	)
}
