package controlplane

import (
	"fmt"

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

// WorkflowMapper maps portal parameters to GitHub Actions workflow inputs.
type WorkflowMapper interface {
	// Validate checks that all required parameters are present.
	Validate(params map[string]string) error
	// WorkflowFileName returns the workflow file name (e.g. "deploy-aws-premium-region.yml").
	WorkflowFileName() string
	// MapInputs converts portal parameters into workflow dispatch inputs.
	// preview=true for preview run, preview=false for apply run.
	MapInputs(params map[string]string, preview bool) map[string]string
}

var mappers = map[cp.Scenario]WorkflowMapper{
	cp.ScenarioAWSPremiumRegion: &awsPremiumMapper{},
	cp.ScenarioAWSBYOCRegion:    &awsBYOCMapper{},
}

// GetMapper returns the WorkflowMapper for the given scenario.
func GetMapper(scenario cp.Scenario) (WorkflowMapper, error) {
	m, ok := mappers[scenario]
	if !ok {
		return nil, fmt.Errorf("unsupported deployment scenario: %s", scenario)
	}
	return m, nil
}

// --- AWS Premium Region ---

var awsPremiumRequiredParams = []string{
	"environment",
	"region-name",
	"aws-region",
	"aws-account",
	"dataplane-accounts",
	"availability-zones",
}

type awsPremiumMapper struct{}

func (m *awsPremiumMapper) Validate(params map[string]string) error {
	return validateRequired(params, awsPremiumRequiredParams)
}

func (m *awsPremiumMapper) WorkflowFileName() string {
	return "deploy-aws-premium-region.yml"
}

func (m *awsPremiumMapper) MapInputs(params map[string]string, preview bool) map[string]string {
	inputs := map[string]string{
		"environment":        params["environment"],
		"region-name":        params["region-name"],
		"aws-region":         params["aws-region"],
		"aws-account":        params["aws-account"],
		"dataplane-accounts": params["dataplane-accounts"],
		"availability-zones": params["availability-zones"],
		"preview":            fmt.Sprintf("%t", preview),
		"destroy":            "false",
	}
	// Optional params
	for _, key := range []string{"eks", "vpc", "target", "service-plan"} {
		if v, ok := params[key]; ok && v != "" {
			inputs[key] = v
		}
	}
	return inputs
}

// --- AWS BYOC Region ---

var awsBYOCRequiredParams = []string{
	"environment",
	"region-name",
	"aws-region",
	"aws-account",
	"dataplane-account",
	"o11y-global-account",
	"byoc-tenant-id",
	"byoc-ecr",
}

type awsBYOCMapper struct{}

func (m *awsBYOCMapper) Validate(params map[string]string) error {
	if err := validateRequired(params, awsBYOCRequiredParams); err != nil {
		return err
	}
	// XOR: exactly one of availability-zones (zone ids) and availability-zone-names
	// (e.g. us-west-2a,us-west-2b) must be provided. The upstream workflow
	// resolves names to ids server-side; devops does not validate further.
	azIDs := params["availability-zones"]
	azNames := params["availability-zone-names"]
	if azIDs == "" && azNames == "" {
		return fmt.Errorf("either %q or %q must be provided", "availability-zones", "availability-zone-names")
	}
	if azIDs != "" && azNames != "" {
		return fmt.Errorf("only one of %q or %q may be provided, not both", "availability-zones", "availability-zone-names")
	}
	return nil
}

func (m *awsBYOCMapper) WorkflowFileName() string {
	return "deploy-aws-byoc-region.yml"
}

func (m *awsBYOCMapper) MapInputs(params map[string]string, preview bool) map[string]string {
	inputs := map[string]string{
		"environment":         params["environment"],
		"region-name":         params["region-name"],
		"aws-region":          params["aws-region"],
		"aws-account":         params["aws-account"],
		"dataplane-account":   params["dataplane-account"],
		"o11y-global-account": params["o11y-global-account"],
		"byoc-tenant-id":      params["byoc-tenant-id"],
		"byoc-ecr":            params["byoc-ecr"],
		"preview":             fmt.Sprintf("%t", preview),
		"destroy":             "false",
	}
	// Forward whichever AZ input is non-empty (XOR enforced by Validate).
	// The upstream workflow resolves names to ids, so only one of these keys
	// is ever set in the dispatched inputs.
	if v := params["availability-zone-names"]; v != "" {
		inputs["availability-zone-names"] = v
	} else {
		inputs["availability-zones"] = params["availability-zones"]
	}
	// Optional params
	for _, key := range []string{
		"seed-eks", "shoot-eks", "seed-vpc", "shoot-vpc",
		"cloudformation-region", "tidb-hosted-zone-id",
		"o11y-hosted-zone-id", "pca-arn", "target",
	} {
		if v, ok := params[key]; ok && v != "" {
			inputs[key] = v
		}
	}
	return inputs
}

// validateRequired checks that all required keys are present and non-empty.
func validateRequired(params map[string]string, required []string) error {
	for _, key := range required {
		if v, ok := params[key]; !ok || v == "" {
			return fmt.Errorf("required parameter %q is missing or empty", key)
		}
	}
	return nil
}
