package byoc

import (
	"context"
	"os"
	"path/filepath"
	"strings"
	"testing"

	awssdk "github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/ec2/types"
	taggingtypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types"
)

type runtimeCommandRecorder struct {
	commands []Command
	output   []byte
	err      error
}

//nolint:gocritic // The production Runner contract deliberately passes immutable commands by value.
func (r *runtimeCommandRecorder) Run(_ context.Context, command Command) ([]byte, error) {
	r.commands = append(r.commands, command)
	return r.output, r.err
}

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

	runner := &runtimeCommandRecorder{}
	driver := &AWSDriver{kubernetesRunner: runner}
	input := Input{KubeconfigPath: "/tmp/kubeconfig", KubeContext: "cp9-byoc"}
	if err := driver.quiescePremiumDataplaneController(context.Background(), input); err != nil {
		t.Fatalf("quiescePremiumDataplaneController: %v", err)
	}
	if len(runner.commands) != 1 {
		t.Fatalf("scale command count = %d, want 1", len(runner.commands))
	}
	if got := runner.commands[0].String(); !strings.Contains(got, "-n cp9-premium scale deployment/dataplane-controller --replicas=0") {
		t.Fatalf("scale command = %q", got)
	}

	runner.output = []byte(`{"spec":{"replicas":0}}`)
	if !driver.premiumDataplaneControllerQuiesced(context.Background(), input) {
		t.Fatal("zero-replica Premium dataplane controller must be quiesced")
	}
	runner.output = []byte(`{"spec":{"replicas":1}}`)
	if driver.premiumDataplaneControllerQuiesced(context.Background(), input) {
		t.Fatal("nonzero Premium dataplane controller must require reconcile")
	}
}

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

	runner := &runtimeCommandRecorder{output: []byte(`{
  "items": [{
    "kind": "Cluster",
    "metadata": {
      "name": "tidbcloud-appbox-cluster",
      "labels": {
        "cloud.pingcap.com/dataplaneId": "dp-exact",
        "cp9-session": "cp9-exact",
        "cp9-account": "024848481520"
      },
      "annotations": {"crossplane.io/external-name": "tidbcloud-appbox-cluster"}
    }
  }]
}`)}
	driver := &AWSDriver{kubernetesRunner: runner}
	input := Input{
		Session: "cp9-exact", CustomerAccountID: "024848481520",
		KubeconfigPath: "/tmp/kubeconfig", KubeContext: "cp9-byoc",
	}
	names, err := driver.dataplaneClusterNames(context.Background(), input, "dp-exact")
	if err != nil {
		t.Fatalf("dataplaneClusterNames: %v", err)
	}
	if len(names) != 1 || names[0] != "tidbcloud-appbox-cluster" {
		t.Fatalf("cluster names = %v, want exact cluster", names)
	}
	if len(runner.commands) != 1 || !strings.Contains(
		runner.commands[0].String(),
		"cloud.pingcap.com/dataplaneId=dp-exact,cp9-session=cp9-exact,cp9-account=024848481520",
	) {
		t.Fatalf("cluster discovery command = %v", runner.commands)
	}

	available := types.NetworkInterfaceStatus("available")
	interfaceType := types.NetworkInterfaceType("interface")
	detached := types.NetworkInterface{
		NetworkInterfaceId: awssdk.String("eni-exact"), Status: available, InterfaceType: interfaceType,
		RequesterManaged: awssdk.Bool(false),
		TagSet: []types.Tag{
			{Key: awssdk.String("cluster.k8s.amazonaws.com/name"), Value: awssdk.String("tidbcloud-appbox-cluster")},
			{Key: awssdk.String("eks:eni:owner"), Value: awssdk.String("amazon-vpc-cni")},
			{Key: awssdk.String("node.k8s.amazonaws.com/instance_id"), Value: awssdk.String("i-exact")},
		},
	}
	foreign := detached
	foreign.NetworkInterfaceId = awssdk.String("eni-foreign")
	foreign.TagSet = append([]types.Tag(nil), detached.TagSet...)
	foreign.TagSet[0].Value = awssdk.String("tidbcloud-foreign-cluster")
	attached := detached
	attached.NetworkInterfaceId = awssdk.String("eni-attached")
	attached.Attachment = &types.NetworkInterfaceAttachment{AttachmentId: awssdk.String("eni-attach-exact")}
	managed := detached
	managed.NetworkInterfaceId = awssdk.String("eni-managed")
	managed.RequesterManaged = awssdk.Bool(true)

	if got := detachedDataplaneENIIDs(
		[]types.NetworkInterface{foreign, attached, detached, managed},
		"tidbcloud-appbox-cluster",
	); len(got) != 1 || got[0] != "eni-exact" {
		t.Fatalf("detached ENI selection = %v, want [eni-exact]", got)
	}
}

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

	batches := regionalRuntimeNamespaceDeletionBatches()
	if len(batches) != 2 || strings.Join(batches[0], ",") != "byoc-dataplane-metadata,byoc-ticdc-service-meta" ||
		len(batches[1]) != 1 || batches[1][0] != "cp-byoc" {
		t.Fatalf("regional runtime namespace deletion batches = %v", batches)
	}
}

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

	input := Input{
		Session: "cp9-exact", Owner: "alice@example.com", CustomerAccountID: "024848481520", TenantID: "1440002",
	}
	bucket := regionalRuntimeBucket{}
	bucket.Metadata.Name = "dp-exact-pool-data"
	bucket.Metadata.Labels = map[string]string{
		"tags.tidbcloud.com/dataplane-id": "dp-exact",
		"tags.tidbcloud.com/environment":  "cp9",
		"tags.tidbcloud.com/servicetype":  "byoc",
		"tags.tidbcloud.com/usedby":       "cp9-byoc",
		"tags.tidbcloud.com/tenant":       "1440002",
	}
	bucket.Status.Bucket.Name = "tidbcloud-exact-bucket"
	tags := map[string]string{
		"cp9-session":     "cp9-exact",
		"cp9-account":     "024848481520",
		"cp9-owner":       "alice-example.com",
		"cp9-managed":     "true",
		"cp9-persona":     "byoc",
		"dataplane-id":    "dp-exact",
		"crossplane-name": "tidbcloud-exact-bucket",
	}
	if err := validateRegionalRuntimeBucketOwnership(bucket, tags, input, false); err != nil {
		t.Fatalf("validate exact BYOC bucket ownership: %v", err)
	}
	tags["cp9-session"] = "cp9-foreign"
	if err := validateRegionalRuntimeBucketOwnership(bucket, tags, input, false); err == nil {
		t.Fatal("foreign AWS bucket session tag must fail closed")
	}
}

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

	input := Input{
		Session: "cp9-exact", Owner: "alice@example.com", CustomerAccountID: "024848481520",
	}
	tags := []taggingtypes.Tag{
		{Key: awssdk.String("cp9-session"), Value: awssdk.String("cp9-exact")},
		{Key: awssdk.String("cp9-account"), Value: awssdk.String("024848481520")},
		{Key: awssdk.String("cp9-owner"), Value: awssdk.String("alice-example.com")},
		{Key: awssdk.String("cp9-managed"), Value: awssdk.String("true")},
		{Key: awssdk.String("cp9-persona"), Value: awssdk.String("byoc")},
		{Key: awssdk.String("environment"), Value: awssdk.String("cp9")},
		{Key: awssdk.String("usedby"), Value: awssdk.String("cp9-byoc")},
	}
	if err := validateCustomerResidualTags(tags, input); err != nil {
		t.Fatalf("validate exact residual tags: %v", err)
	}
	tags[0].Value = awssdk.String("cp9-foreign")
	if err := validateCustomerResidualTags(tags, input); err == nil {
		t.Fatal("foreign residual session tag must fail closed")
	}
	if got := customerTaggedResourceID("arn:aws:ec2:us-west-2:024848481520:volume/vol-exact", "volume/"); got != "vol-exact" {
		t.Fatalf("resource id = %q, want vol-exact", got)
	}
}

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

	input := Input{
		Session:               "cp9-alice-byoc",
		Owner:                 "alice@example.com",
		Region:                "us-west-2",
		ControlPlaneAccountID: "804837308162",
		CustomerAccountID:     "024848481520",
		ImageSyncRegion:       "us-west-2",
		RemoteRuntimeRoleARN:  "arn:aws:iam::804837308162:role/cp9-alice-byoc-runtime",
		AvailabilityZones: []AvailabilityZone{
			{Name: "us-west-2a", ID: "usw2-az2"},
			{Name: "us-west-2b", ID: "usw2-az1"},
		},
	}
	closure, err := DiscoverCoreImageClosure(filepath.Join("..", ".."))
	if err != nil {
		t.Fatalf("DiscoverCoreImageClosure: %v", err)
	}
	closureImages := make(map[string]bool, len(closure.Images))
	for _, image := range closure.Images {
		closureImages[image.Repository+":"+image.Tag] = true
	}
	for repository, tag := range map[string]string{
		"tidbcloud/ticdc":                         "v8.5.2",
		"tidbcloud/tidb-operator-prestop-checker": "v2.2.0-alpha.4",
	} {
		if !closureImages[repository+":"+tag] {
			t.Fatalf("customer image closure is missing %s:%s", repository, tag)
		}
	}
	rendered, _, err := renderRegionalRuntime(input, regionalRuntimeNetwork{PublicAccessCIDR: "52.89.12.192/32"}, []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
  name: byoc-ticdc-service-controller
spec:
  template:
    spec:
      containers:
        - name: ticdc-service
          image: cp9.local/ticdc-service:placeholder
          resources:
            requests:
              cpu: 500m
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: byoc-cluster-ng-server
spec:
  template:
    spec:
      containers:
        - name: cluster-ng-server
          image: cp9.local/cluster-ng-server:placeholder
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: byoc-cluster-ng-executor
spec:
  template:
    spec:
      containers:
        - name: cluster-ng-executor
          image: cp9.local/cluster-ng-executor:placeholder
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: byoc-regional-server-root-config
data:
  root-config.yaml: |
    regionConfig:
      nodeZones:
        - us-west-2a
      zones:
        - us-west-2a
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: byoc-cluster-ng-server-root-config
data:
  config.yaml: |
    server:
      image:
        registry: 385595570414.dkr.ecr.us-west-2.amazonaws.com
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: byoc-ticdc-service-config
data:
  config.yaml: |
    ticdcBaseImage: 385595570414.dkr.ecr.us-west-2.amazonaws.com/tidbcloud/ticdc
    ticdcPreStopImage: 385595570414.dkr.ecr.us-west-2.amazonaws.com/tidbcloud/tidb-operator-prestop-checker:v2.2.0-alpha.4
`))
	if err != nil {
		t.Fatalf("renderRegionalRuntime: %v", err)
	}
	manifest := string(rendered)

	for _, want := range []string{
		"image: 385595570414.dkr.ecr.us-west-2.amazonaws.com/tidbcloud/ticdc-service-ng:ff9e90936a147b60627c3301990bfad506c13517",
		"name: cp9-staging-ecr-pull",
		"cpu: 25m",
		"nodeZones:",
		"registry: 024848481520.dkr.ecr.us-west-2.amazonaws.com",
		"ticdcBaseImage: 024848481520.dkr.ecr.us-west-2.amazonaws.com/tidbcloud/ticdc",
		"ticdcPreStopImage: 024848481520.dkr.ecr.us-west-2.amazonaws.com/tidbcloud/tidb-operator-prestop-checker:v2.2.0-alpha.4",
		"kind: HTTPRoute",
		"name: byoc-regional-api",
		"value: /cp9/byoc/api/premium",
		"name: byoc-regional-dataplane-proxy",
		"value: /cp9/byoc/dataplane/proxy",
		"name: byoc-cluster-ng-server-proxy",
		"name: shared-regional-kong",
		"namespace: shared-regional-kong-system",
		"plan: byoc",
		"accountListPath: \"\"",
		"authorizedSubjects:",
		"arn:aws:iam::804837308162:role/cp9-alice-byoc-byoc-dataplane-manager",
		"enableK8sPublicAccess: true",
		"- 52.89.12.192/32",
		"name: byoc-aws-authenticator-wrapper",
		"mountPath: /opt/cp9-byoc/bin",
		"name: AWS_ROLE_ARN",
		"name: PATH",
		"unset AWS_CONFIG_FILE AWS_PROFILE AWS_DEFAULT_PROFILE AWS_SDK_LOAD_CONFIG AWS_ROLE_ARN",
		"exec /usr/local/bin/aws-iam-authenticator \"$@\"",
	} {
		if !strings.Contains(manifest, want) {
			t.Fatalf("rendered runtime missing %q:\n%s", want, manifest)
		}
	}
	if got := strings.Count(manifest, "mountPath: /etc/cp9-byoc-aws/config"); got != 3 {
		t.Fatalf("AWS role config mount count = %d, want 3:\n%s", got, manifest)
	}
	if got := strings.Count(manifest, "mountPath: /opt/cp9-byoc/bin"); got != 1 {
		t.Fatalf("isolated authenticator wrapper mount count = %d, want 1:\n%s", got, manifest)
	}
	premiumRoot, err := os.ReadFile(filepath.Join("..", "..", "personas", "app-delivery", "envs", "premium", "dataplane-manager", "root-config.yaml"))
	if err != nil {
		t.Fatalf("read premium dataplane root config: %v", err)
	}
	if !strings.Contains(string(premiumRoot), "operator: NotIn\n      values: [byoc]") {
		t.Fatalf("premium dataplane predicate must exclude BYOC resources:\n%s", premiumRoot)
	}
	if got := catalogArtifacts(input)["service_address"]; got != byocRegionalAPIServiceAddress {
		t.Fatalf("service address = %q, want %q", got, byocRegionalAPIServiceAddress)
	}
	for _, forbidden := range []string{
		"cp9.local/ticdc-service:placeholder",
		"image: 024848481520.dkr.ecr.us-west-2.amazonaws.com/tidbcloud/ticdc-service-ng",
		"nodeZones: []",
		"kind: ReferenceGrant",
		"namespace: kong-system",
	} {
		if strings.Contains(manifest, forbidden) {
			t.Fatalf("rendered runtime contains forbidden %q:\n%s", forbidden, manifest)
		}
	}

	proof, err := renderRoleProofJob(input)
	if err != nil {
		t.Fatalf("renderRoleProofJob: %v", err)
	}
	proofManifest := string(proof)
	if strings.Contains(proofManifest, "AWS_PROFILE= aws") {
		t.Fatalf("role proof must not pass an empty AWS profile:\n%s", proofManifest)
	}
	if strings.Count(proofManifest, "env -u AWS_PROFILE -u AWS_DEFAULT_PROFILE AWS_CONFIG_FILE=/dev/null") != 3 {
		t.Fatalf("role proof must bypass the configured role for all three direct-credential calls:\n%s", proofManifest)
	}
}

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

	data := map[string]any{"root-config.yaml": `predicate:
  matchLabels:
    tags.tidbcloud.com/tenant: "old"
    tags.tidbcloud.com/servicetype: byoc
  matchExpressions:
    - key: tags.tidbcloud.com/shared-pool
      operator: Exists
    - key: tags.tidbcloud.com/servicetype
      operator: NotIn
      values: [byoc]
`}
	input := Input{TenantID: "1440002"}
	if err := patchDataplaneRootConfig(data, input, regionalRuntimeNetwork{}); err != nil {
		t.Fatalf("patchDataplaneRootConfig: %v", err)
	}
	root, err := parseEmbeddedYAML(data, "root-config.yaml")
	if err != nil {
		t.Fatalf("parse patched root config: %v", err)
	}
	predicate := nestedMap(root, "predicate")
	labels := nestedMap(predicate, "matchLabels")
	if got := stringMapValue(labels, "tags.tidbcloud.com/tenant"); got != input.TenantID {
		t.Fatalf("tenant predicate = %q, want %q", got, input.TenantID)
	}
	if _, exists := labels["tags.tidbcloud.com/servicetype"]; exists {
		t.Fatalf("service-type matchLabel must be removed: %#v", labels)
	}
	expressions, ok := predicate["matchExpressions"].([]any)
	if !ok {
		t.Fatalf("matchExpressions is invalid: %#v", predicate["matchExpressions"])
	}
	for _, value := range expressions {
		if expression, ok := value.(map[string]any); ok &&
			stringMapValue(expression, "key") == "tags.tidbcloud.com/servicetype" {
			t.Fatalf("service-type matchExpression must be removed: %#v", expressions)
		}
	}
}

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

	const want = "http://byoc-dataplane-manager.cp-byoc.svc.cluster.local:80/api/v1/dataplanes/dp-1"
	if got := byocDataplaneManagerURL("dp-1"); got != want {
		t.Fatalf("dataplane manager URL = %q, want %q", got, want)
	}
}
