package implement

import (
	"context"
	"testing"

	"k8s.io/apimachinery/pkg/runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"

	agentv2 "github.com/tidbcloud/observability/agent-control-server/api/v2"
	"github.com/tidbcloud/observability/agent-control-server/pkg/agentcontrol/api"
)

func TestAgentControlUsesConfiguredNamespaceForLifecycle(t *testing.T) {
	t.Parallel()

	ctx := context.Background()
	cli := buildClient(t)
	agentControl := NewAgentControlImpl(cli, "dev", "aws", "us-west-2", "cp9-dedicated")

	_, err := agentControl.CreateK8sAgent(ctx, &api.CreateK8sAgentRequest{
		TenantID:  "tenant-1",
		ProjectID: "project-1",
		Dataplane: api.DataplaneConfig{ClusterName: "dataplane-1"},
	})
	if err != nil {
		t.Fatalf("create k8s agent: %v", err)
	}
	assertAgentNamespace(t, ctx, cli, &agentv2.O11yK8sAgent{}, "cp9-dedicated", "project-1")

	if _, err := agentControl.GetK8sAgent(ctx, &api.GetK8sAgentRequest{ProjectID: "project-1"}); err != nil {
		t.Fatalf("get k8s agent: %v", err)
	}
	if _, err := agentControl.DeleteK8sAgent(ctx, &api.DeleteK8sAgentRequest{ProjectID: "project-1"}); err != nil {
		t.Fatalf("delete k8s agent: %v", err)
	}

	_, err = agentControl.CreateTiDBAgent(ctx, &api.CreateTiDBAgentRequest{
		TenantID:  "tenant-1",
		ProjectID: "project-1",
		ClusterID: "cluster-1",
		Dataplane: api.DataplaneConfig{ClusterName: "dataplane-1"},
	})
	if err != nil {
		t.Fatalf("create TiDB agent: %v", err)
	}
	assertAgentNamespace(t, ctx, cli, &agentv2.O11yTiDBAgent{}, "cp9-dedicated", "cluster-1")

	if _, err := agentControl.PauseTiDBAgent(ctx, &api.PauseTiDBAgentRequest{ClusterID: "cluster-1"}); err != nil {
		t.Fatalf("pause TiDB agent: %v", err)
	}
	if _, err := agentControl.ResumeTiDBAgent(ctx, &api.ResumeTiDBAgentRequest{ClusterID: "cluster-1"}); err != nil {
		t.Fatalf("resume TiDB agent: %v", err)
	}
	if _, err := agentControl.ScaleTiDBAgent(ctx, &api.ScaleTiDBAgentRequest{ClusterID: "cluster-1"}); err != nil {
		t.Fatalf("scale TiDB agent: %v", err)
	}
	if _, err := agentControl.DeleteTiDBAgent(ctx, &api.DeleteTiDBAgentRequest{ClusterID: "cluster-1"}); err != nil {
		t.Fatalf("delete TiDB agent: %v", err)
	}
}

func TestAgentControlUsesConfiguredBizTypeForDedicatedAgents(t *testing.T) {
	t.Parallel()

	ctx := context.Background()
	cli := buildClient(t)
	agentControl := NewAgentControlImplWithConfig(cli, "dev", "aws", "us-west-2", AgentControlConfig{
		Namespace: "cp9-dedicated",
		BizType:   "cp9-session-1",
	})

	if _, err := agentControl.CreateK8sAgent(ctx, &api.CreateK8sAgentRequest{ProjectID: "project-1"}); err != nil {
		t.Fatalf("create k8s agent: %v", err)
	}
	k8sAgent := &agentv2.O11yK8sAgent{}
	assertAgentNamespace(t, ctx, cli, k8sAgent, "cp9-dedicated", "project-1")
	if got := k8sAgent.Spec.O11y.BizType; got != "cp9-session-1" {
		t.Fatalf("k8s agent bizType = %q, want %q", got, "cp9-session-1")
	}

	if _, err := agentControl.CreateTiDBAgent(ctx, &api.CreateTiDBAgentRequest{ClusterID: "cluster-1"}); err != nil {
		t.Fatalf("create TiDB agent: %v", err)
	}
	tidbAgent := &agentv2.O11yTiDBAgent{}
	assertAgentNamespace(t, ctx, cli, tidbAgent, "cp9-dedicated", "cluster-1")
	if got := tidbAgent.Spec.O11y.BizType; got != "cp9-session-1" {
		t.Fatalf("TiDB agent bizType = %q, want %q", got, "cp9-session-1")
	}
}

func TestAgentControlDefaultsToObservabilityNamespace(t *testing.T) {
	t.Parallel()

	ctx := context.Background()
	cli := buildClient(t)
	agentControl := NewAgentControlImpl(cli, "dev", "aws", "us-west-2", "")

	if _, err := agentControl.CreateK8sAgent(ctx, &api.CreateK8sAgentRequest{ProjectID: "project-1"}); err != nil {
		t.Fatalf("create k8s agent: %v", err)
	}
	agent := &agentv2.O11yK8sAgent{}
	assertAgentNamespace(t, ctx, cli, agent, o11yNamespace, "project-1")
	if got := agent.Spec.O11y.BizType; got != "dev-tidbcloud" {
		t.Fatalf("k8s agent bizType = %q, want %q", got, "dev-tidbcloud")
	}
}

func buildClient(t *testing.T) client.Client {
	t.Helper()

	scheme := runtime.NewScheme()
	if err := agentv2.AddToScheme(scheme); err != nil {
		t.Fatalf("add agent scheme: %v", err)
	}
	return fake.NewClientBuilder().WithScheme(scheme).Build()
}

func assertAgentNamespace(t *testing.T, ctx context.Context, cli client.Client, obj client.Object, namespace, name string) {
	t.Helper()

	if err := cli.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, obj); err != nil {
		t.Fatalf("get %T from %s/%s: %v", obj, namespace, name, err)
	}
}
