package it

import (
	"context"
	"fmt"
	"math/rand/v2"
	"os"
	"testing"
	"time"

	"github.com/stretchr/testify/suite"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/module/managed_resources"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/testing/testhelpers"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/pkg/agentcfg"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	"k8s.io/utils/ptr"
)

func TestIntegration(t *testing.T) {
	if os.Getenv("RUN_IT") == "" {
		t.Skip("Skipping integration test. Set RUN_IT to run these tests")
	}
	suite.Run(t, new(integrationSuite))
}

func (s *integrationSuite) TestAgentkConnectsToKas_Simple() {
	gitalySrv := s.startGitaly()
	s.setupGitLabMocks(gitalySrv, true)
	s.setupGitalyMocksForAgentConfig(&agentcfg.ConfigurationFile{
		Observability: &agentcfg.ObservabilityCF{
			Logging: &agentcfg.LoggingCF{
				Level:     agentcfg.LogLevelEnum_debug,
				GrpcLevel: ptr.To(agentcfg.LogLevelEnum_debug),
			},
		},
	})

	gitLabSrv := s.startGitLab()

	kas := s.startKAS(kasOptions{
		name:       "kas",
		cfg:        s.baseKASConfig(gitLabSrv),
		gitalyPort: gitalySrv.port,
	})

	s.startAgentk(agentkOptions{
		kasURL: kas.agentServerURL,
		token:  agentkToken1,
		name:   "agent1",
		ns:     s.ns,
	})

	k8sClient := s.k8sClientForAgentKey(kas, testhelpers.AgentkKey1)
	s.loadTest(k8sClient, 5, 10, 10*time.Millisecond)
}

func (s *integrationSuite) TestAgentkConnectsToKas_RequestToKas2ToKas1ToAgentk() {
	gitalySrv := s.startGitaly()
	s.setupGitLabMocks(gitalySrv, true)
	s.setupGitalyMocksForAgentConfig(&agentcfg.ConfigurationFile{
		Observability: &agentcfg.ObservabilityCF{
			Logging: &agentcfg.LoggingCF{
				Level:     agentcfg.LogLevelEnum_debug,
				GrpcLevel: ptr.To(agentcfg.LogLevelEnum_debug),
			},
		},
	})

	gitLabSrv := s.startGitLab()

	kasCfg := s.baseKASConfig(gitLabSrv)
	kas1 := s.startKAS(kasOptions{
		name:       "kas1",
		cfg:        kasCfg,
		gitalyPort: gitalySrv.port,
	})
	kas2 := s.startKAS(kasOptions{
		name:       "kas2",
		cfg:        kasCfg,
		gitalyPort: gitalySrv.port,
	})

	s.startAgentk(agentkOptions{
		kasURL: kas1.agentServerURL,
		token:  agentkToken1,
		name:   "agent1",
		ns:     s.ns,
	})

	k8sClient := s.k8sClientForAgentKey(kas2, testhelpers.AgentkKey1)
	s.loadTest(k8sClient, 5, 10, 10*time.Millisecond)
}

func (s *integrationSuite) TestKasConnectsToAgentk_Simple() {
	gitalySrv := s.startGitaly()
	s.setupGitLabMocks(gitalySrv, false)
	s.setupGitalyMocksForAgentConfig(&agentcfg.ConfigurationFile{
		Observability: &agentcfg.ObservabilityCF{
			Logging: &agentcfg.LoggingCF{
				Level:     agentcfg.LogLevelEnum_debug,
				GrpcLevel: ptr.To(agentcfg.LogLevelEnum_debug),
			},
		},
	})

	agentk := s.startAgentk(agentkOptions{
		token: agentkToken1,
		name:  "agent1",
		ns:    s.ns,
	})
	s.setupGitLabMocksForReceptiveAgents(agentk)

	gitLabSrv := s.startGitLab()

	kas := s.startKAS(kasOptions{
		name:       "kas",
		cfg:        s.baseKASConfig(gitLabSrv),
		gitalyPort: gitalySrv.port,
	})

	s.waitForAgentToRegister(kas)

	k8sClient := s.k8sClientForAgentKey(kas, testhelpers.AgentkKey1)
	s.loadTest(k8sClient, 5, 10, 10*time.Millisecond)
}

func (s *integrationSuite) loadTest(c kubernetes.Interface, concurrency, requestsPerThread int, delay time.Duration) {
	s.T().Log("Running load test")
	defer s.T().Log("Load test done")

	pods := c.CoreV1().Pods(corev1.NamespaceAll)
	var wg wait.Group
	for range concurrency {
		wg.Start(func() {
			for range requestsPerThread {
				_, err := pods.List(context.Background(), metav1.ListOptions{})
				if err != nil {
					s.Error(err)
				}
				time.Sleep(rand.N(delay))
			}
		})
	}
	wg.Wait()
}

func templatingInfo() *managed_resources.TemplatingInfo {
	n := rand.N(int64(10000))
	return &managed_resources.TemplatingInfo{
		Agent: &managed_resources.Agent{
			Id:   testhelpers.AgentkKey1.ID,
			Name: agentName,
			Url:  "https://gitlab.example.com/agent",
		},
		Environment: &managed_resources.Environment{
			Id:      n,
			Name:    fmt.Sprintf("my great environment %d", n),
			Slug:    fmt.Sprintf("slug-%d", n),
			Url:     ptr.To("https://prod.example.com/"),
			PageUrl: "https://gitlab.example.com/env/1",
			Tier:    "dev",
		},
		Project: &managed_resources.Project{
			Id:   testhelpers.ProjectID,
			Slug: "my-project",
			Path: "my-project",
			Url:  "https://gitlab.example.com/my-project",
		},
		Pipeline: &managed_resources.Pipeline{
			Id: rand.Int64(),
		},
		Job: &managed_resources.Job{
			Id: rand.Int64(),
		},
		User: &managed_resources.User{
			Id:       rand.Int64(),
			Username: "neo",
		},
	}
}
