package migration

import (
	"encoding/json"
	"testing"

	"github.com/stretchr/testify/assert"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"

	"github.com/tidbcloud/observability/vm-migration/internal/jsonio"
	"github.com/tidbcloud/observability/vm-migration/internal/schema"
)

func testVmselectSpec() VmselectSpec {
	return VmselectSpec{
		Name: schema.VmselectMigrationName, Namespace: "observability", ClusterName: schema.VmclusterName,
		StorageIDs: []int{30, 31}, Image: "vmselect:v1", Replicas: 1,
		Resources: corev1.ResourceRequirements{
			Requests: corev1.ResourceList{
				corev1.ResourceCPU:    resource.MustParse("4"),
				corev1.ResourceMemory: resource.MustParse("32Gi"),
			},
			Limits: corev1.ResourceList{
				corev1.ResourceCPU:    resource.MustParse("8"),
				corev1.ResourceMemory: resource.MustParse("64Gi"),
			},
		},
	}
}

func TestServiceSelectorMatchesDeployment(t *testing.T) {
	spec := testVmselectSpec()
	dep, svc := spec.Deployment(), spec.Service()
	assert.Equal(t, dep.Spec.Template.Labels[kubeNameLabel], svc.Spec.Selector[kubeNameLabel])
}

func TestKubeObjectsJSON(t *testing.T) {
	spec := testVmselectSpec()
	dep, err := json.Marshal(spec.Deployment())
	assert.NoError(t, err)
	assert.Contains(t, string(dep), `"readinessProbe"`)
	assert.Contains(t, string(dep), `"path":"/health"`)
	assert.Contains(t, string(dep), `"containerPort":8481`)
	assert.Contains(t, string(dep), "-storageNode=vmstorage-vmcluster-30.vmstorage-vmcluster.observability.svc.cluster.local:8401,vmstorage-vmcluster-31.vmstorage-vmcluster.observability.svc.cluster.local:8401")
	assert.Contains(t, string(dep), "-cacheDataPath=/select-cache")
	assert.Contains(t, string(dep), `-dedup.minScrapeInterval=1ms`)
	assert.Contains(t, string(dep), `"nodeSelector":{"node_type":"worker"}`)
	assert.Contains(t, string(dep), `"emptyDir":{}`)
	assert.Contains(t, string(dep), `"cpu":"4"`)
	assert.Contains(t, string(dep), `"memory":"32Gi"`)

	svc, err := json.Marshal(spec.Service())
	assert.NoError(t, err)
	assert.Contains(t, string(svc), `"targetPort":"http"`)
}

func TestVmselectSpecs(t *testing.T) {
	var doc schema.CompiledDoc
	assert.NoError(t, jsonio.ReadFileStrict("../../testdata/golden/plan-compile.json", &doc))
	pair, err := VmselectSpecs(&doc)
	assert.NoError(t, err)

	assert.Equal(t, schema.VmselectMigrationName, pair.Migration.Name)
	assert.Equal(t, schema.VmselectCandidateName, pair.Candidate.Name)
	assert.Equal(t, "observability", pair.Migration.Namespace)
	assert.Equal(t, schema.VmclusterName, pair.Migration.ClusterName)
	assert.Equal(t, []int{3, 4}, pair.Migration.StorageIDs)
	assert.Equal(t, []int{0, 1, 2}, pair.Candidate.StorageIDs)
	assert.Equal(t, 1, pair.Migration.Replicas)
	quants := pair.Migration.TuneQuantities()
	assert.Equal(t, "500m", quants.Requests.CPU, "nil plan quant falls back to the 0.5 default, canonicalized")
	assert.Equal(t, "4Gi", pair.Candidate.TuneQuantities().Limits.Memory)
	assert.Contains(t, pair.Migration.StorageNodes(),
		"vmstorage-vmcluster-3.vmstorage-vmcluster.observability.svc.cluster.local:8401")
}

func TestVmselectSpecsBadQuantity(t *testing.T) {
	bad := "not-a-quantity"
	doc := schema.CompiledDoc{Vmselects: schema.CompiledVmselects{
		Migration: schema.CompiledVmselectTarget{
			Name:      "vmselect-migration",
			Resources: schema.CompiledResources{Requests: schema.CompiledResourceQuant{CPU: &bad}},
		},
	}}
	_, err := VmselectSpecs(&doc)
	assert.ErrorContains(t, err, "vmselect-migration requests.cpu")
}

func TestVmselectSpecsBadReplicas(t *testing.T) {
	replicas := 5000000000 // wraps int32 in the Deployment field
	doc := schema.CompiledDoc{
		TargetReplicaCount: 3, SourceReplicaCount: 5,
		Vmselects: schema.CompiledVmselects{
			Migration: schema.CompiledVmselectTarget{Name: "vmselect-migration", Replicas: &replicas},
			Candidate: schema.CompiledVmselectTarget{Name: "vmselect-candidate"},
		},
	}
	_, err := VmselectSpecs(&doc)
	assert.ErrorContains(t, err, "vmselect-migration replicas")
}
