package controlplane

import (
	"testing"

	cp "github.com/tidbcloud/devops/entity/controlplane"
)

func TestGetMapper(t *testing.T) {
	tests := []struct {
		scenario cp.Scenario
		wantErr  bool
	}{
		{cp.ScenarioAWSPremiumRegion, false},
		{cp.ScenarioAWSBYOCRegion, false},
		{cp.Scenario("unsupported"), true},
	}

	for _, tt := range tests {
		t.Run(string(tt.scenario), func(t *testing.T) {
			_, err := GetMapper(tt.scenario)
			if (err != nil) != tt.wantErr {
				t.Errorf("GetMapper(%q) error = %v, wantErr %v", tt.scenario, err, tt.wantErr)
			}
		})
	}
}

func TestAWSPremiumMapper_Validate(t *testing.T) {
	m := &awsPremiumMapper{}

	validParams := map[string]string{
		"environment":        "dev",
		"region-name":        "us-west-2-premium-0",
		"aws-region":         "us-west-2",
		"aws-account":        "123456789012",
		"dataplane-accounts": "123456789012",
		"availability-zones": "usw2-az1,usw2-az2,usw2-az3",
	}
	if err := m.Validate(validParams); err != nil {
		t.Errorf("Validate with valid params: unexpected error: %v", err)
	}

	// Missing required param
	incomplete := map[string]string{
		"environment": "dev",
		"region-name": "us-west-2-premium-0",
	}
	if err := m.Validate(incomplete); err == nil {
		t.Error("Validate with missing params: expected error, got nil")
	}

	// Empty required param
	emptyParam := map[string]string{
		"environment":        "dev",
		"region-name":        "",
		"aws-region":         "us-west-2",
		"aws-account":        "123456789012",
		"dataplane-accounts": "123456789012",
		"availability-zones": "usw2-az1,usw2-az2,usw2-az3",
	}
	if err := m.Validate(emptyParam); err == nil {
		t.Error("Validate with empty required param: expected error, got nil")
	}
}

func TestAWSPremiumMapper_MapInputs(t *testing.T) {
	m := &awsPremiumMapper{}
	params := map[string]string{
		"environment":        "dev",
		"region-name":        "us-west-2-premium-0",
		"aws-region":         "us-west-2",
		"aws-account":        "123456789012",
		"dataplane-accounts": "123456789012",
		"availability-zones": "usw2-az1,usw2-az2,usw2-az3",
		"eks":                `{"version":"1.30"}`,
	}

	// Preview mode
	inputs := m.MapInputs(params, true)
	if inputs["preview"] != "true" {
		t.Errorf("preview mode: expected preview=true, got %q", inputs["preview"])
	}
	if inputs["destroy"] != "false" {
		t.Errorf("preview mode: expected destroy=false, got %q", inputs["destroy"])
	}
	if inputs["region-name"] != "us-west-2-premium-0" {
		t.Errorf("expected region-name=%q, got %q", "us-west-2-premium-0", inputs["region-name"])
	}
	if inputs["eks"] != `{"version":"1.30"}` {
		t.Errorf("expected eks optional param to be passed through")
	}

	// Apply mode
	inputs = m.MapInputs(params, false)
	if inputs["preview"] != "false" {
		t.Errorf("apply mode: expected preview=false, got %q", inputs["preview"])
	}
}

func TestAWSBYOCMapper_Validate(t *testing.T) {
	m := &awsBYOCMapper{}

	validParams := map[string]string{
		"environment":         "dev",
		"region-name":         "us-west-2-byoc-0",
		"aws-region":          "us-west-2",
		"aws-account":         "123456789012",
		"dataplane-account":   "234567890123",
		"o11y-global-account": "345678901234",
		"availability-zones":  "usw2-az1,usw2-az2,usw2-az3",
		"byoc-tenant-id":      "2130533",
		"byoc-ecr":            "234567890123.dkr.ecr.us-west-2.amazonaws.com",
	}
	if err := m.Validate(validParams); err != nil {
		t.Errorf("Validate with valid params: unexpected error: %v", err)
	}

	// Missing byoc-tenant-id
	incomplete := make(map[string]string)
	for k, v := range validParams {
		incomplete[k] = v
	}
	delete(incomplete, "byoc-tenant-id")
	if err := m.Validate(incomplete); err == nil {
		t.Error("Validate with missing byoc-tenant-id: expected error, got nil")
	}
}

func TestAWSBYOCMapper_MapInputs(t *testing.T) {
	m := &awsBYOCMapper{}
	params := map[string]string{
		"environment":         "dev",
		"region-name":         "us-west-2-byoc-0",
		"aws-region":          "us-west-2",
		"aws-account":         "123456789012",
		"dataplane-account":   "234567890123",
		"o11y-global-account": "345678901234",
		"availability-zones":  "usw2-az1,usw2-az2,usw2-az3",
		"byoc-tenant-id":      "2130533",
		"byoc-ecr":            "234567890123.dkr.ecr.us-west-2.amazonaws.com",
		"pca-arn":             "arn:aws:acm-pca:us-west-2:234567890123:certificate-authority/abc",
	}

	inputs := m.MapInputs(params, true)
	if inputs["preview"] != "true" {
		t.Errorf("expected preview=true, got %q", inputs["preview"])
	}
	if inputs["byoc-tenant-id"] != "2130533" {
		t.Errorf("expected byoc-tenant-id=2130533, got %q", inputs["byoc-tenant-id"])
	}
	if inputs["pca-arn"] != params["pca-arn"] {
		t.Errorf("expected pca-arn optional param to be passed through")
	}
}

func TestWorkflowFileName(t *testing.T) {
	m1 := &awsPremiumMapper{}
	if got := m1.WorkflowFileName(); got != "deploy-aws-premium-region.yml" {
		t.Errorf("awsPremiumMapper.WorkflowFileName() = %q, want %q", got, "deploy-aws-premium-region.yml")
	}

	m2 := &awsBYOCMapper{}
	if got := m2.WorkflowFileName(); got != "deploy-aws-byoc-region.yml" {
		t.Errorf("awsBYOCMapper.WorkflowFileName() = %q, want %q", got, "deploy-aws-byoc-region.yml")
	}
}

func TestAWSBYOCMapper_Validate_AvailabilityZoneXOR(t *testing.T) {
	m := &awsBYOCMapper{}

	baseParams := map[string]string{
		"environment":         "dev",
		"region-name":         "us-west-2-byoc-0",
		"aws-region":          "us-west-2",
		"aws-account":         "123456789012",
		"dataplane-account":   "234567890123",
		"o11y-global-account": "345678901234",
		"byoc-tenant-id":      "2130533",
		"byoc-ecr":            "234567890123.dkr.ecr.us-west-2.amazonaws.com",
	}

	tests := []struct {
		name    string
		azIDs   string
		azNames string
		wantErr bool
	}{
		{
			name:    "only zone ids provided",
			azIDs:   "usw2-az1,usw2-az2,usw2-az3",
			azNames: "",
			wantErr: false,
		},
		{
			name:    "only zone names provided",
			azIDs:   "",
			azNames: "us-west-2a,us-west-2b,us-west-2c",
			wantErr: false,
		},
		{
			name:    "both empty",
			azIDs:   "",
			azNames: "",
			wantErr: true,
		},
		{
			name:    "both filled",
			azIDs:   "usw2-az1,usw2-az2,usw2-az3",
			azNames: "us-west-2a,us-west-2b,us-west-2c",
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			params := make(map[string]string, len(baseParams)+2)
			for k, v := range baseParams {
				params[k] = v
			}
			if tt.azIDs != "" {
				params["availability-zones"] = tt.azIDs
			}
			if tt.azNames != "" {
				params["availability-zone-names"] = tt.azNames
			}
			err := m.Validate(params)
			if (err != nil) != tt.wantErr {
				t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestAWSBYOCMapper_MapInputs_AvailabilityZoneForwarding(t *testing.T) {
	m := &awsBYOCMapper{}

	baseParams := map[string]string{
		"environment":         "dev",
		"region-name":         "us-west-2-byoc-0",
		"aws-region":          "us-west-2",
		"aws-account":         "123456789012",
		"dataplane-account":   "234567890123",
		"o11y-global-account": "345678901234",
		"byoc-tenant-id":      "2130533",
		"byoc-ecr":            "234567890123.dkr.ecr.us-west-2.amazonaws.com",
	}

	t.Run("only zone ids provided forwards availability-zones", func(t *testing.T) {
		params := make(map[string]string, len(baseParams)+1)
		for k, v := range baseParams {
			params[k] = v
		}
		params["availability-zones"] = "usw2-az1,usw2-az2,usw2-az3"
		inputs := m.MapInputs(params, true)
		if inputs["availability-zones"] != "usw2-az1,usw2-az2,usw2-az3" {
			t.Errorf("expected availability-zones forwarded, got %q", inputs["availability-zones"])
		}
		if _, ok := inputs["availability-zone-names"]; ok {
			t.Errorf("expected availability-zone-names NOT to be set, got %q", inputs["availability-zone-names"])
		}
	})

	t.Run("only zone names provided forwards availability-zone-names", func(t *testing.T) {
		params := make(map[string]string, len(baseParams)+1)
		for k, v := range baseParams {
			params[k] = v
		}
		params["availability-zone-names"] = "us-west-2a,us-west-2b,us-west-2c"
		inputs := m.MapInputs(params, true)
		if inputs["availability-zone-names"] != "us-west-2a,us-west-2b,us-west-2c" {
			t.Errorf("expected availability-zone-names forwarded, got %q", inputs["availability-zone-names"])
		}
		if _, ok := inputs["availability-zones"]; ok {
			t.Errorf("expected availability-zones NOT to be set, got %q", inputs["availability-zones"])
		}
	})
}

func TestAWSPremiumMapper_MapInputs_ServicePlan(t *testing.T) {
	m := &awsPremiumMapper{}

	baseParams := map[string]string{
		"environment":        "dev",
		"region-name":        "us-west-2-premium-0",
		"aws-region":         "us-west-2",
		"aws-account":        "123456789012",
		"dataplane-accounts": "123456789012",
		"availability-zones": "usw2-az1,usw2-az2,usw2-az3",
	}

	t.Run("service-plan set to Premium is forwarded", func(t *testing.T) {
		params := make(map[string]string, len(baseParams)+1)
		for k, v := range baseParams {
			params[k] = v
		}
		params["service-plan"] = "Premium"
		inputs := m.MapInputs(params, true)
		if inputs["service-plan"] != "Premium" {
			t.Errorf("expected service-plan=Premium forwarded, got %q", inputs["service-plan"])
		}
	})

	t.Run("service-plan set to EssentialV2 is forwarded", func(t *testing.T) {
		params := make(map[string]string, len(baseParams)+1)
		for k, v := range baseParams {
			params[k] = v
		}
		params["service-plan"] = "EssentialV2"
		inputs := m.MapInputs(params, true)
		if inputs["service-plan"] != "EssentialV2" {
			t.Errorf("expected service-plan=EssentialV2 forwarded, got %q", inputs["service-plan"])
		}
	})

	t.Run("service-plan not set is omitted from inputs", func(t *testing.T) {
		params := make(map[string]string, len(baseParams))
		for k, v := range baseParams {
			params[k] = v
		}
		inputs := m.MapInputs(params, true)
		if _, ok := inputs["service-plan"]; ok {
			t.Errorf("expected service-plan NOT to be set when not provided, got %q", inputs["service-plan"])
		}
	})
}
