// Package gcek3d owns the GCP Terraform adapter for the secondary Dedicated
// regional host substrate. Remote tool and k3d setup live in the shared
// remote/k3d module; this package does not start a Tilt controller or service
// graph.
package gcek3d

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/netip"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"time"

	cpaws "github.com/tidbcloud/controlplane9/internal/aws"
	"github.com/tidbcloud/controlplane9/internal/cluster"
	"github.com/tidbcloud/controlplane9/internal/config"
	"github.com/tidbcloud/controlplane9/internal/host"
	remotek3d "github.com/tidbcloud/controlplane9/internal/host/remote/k3d"
	cpterraform "github.com/tidbcloud/controlplane9/internal/terraform"
)

// Step applies the isolated GCP root and prepares the resulting VM as a k3d
// host. The primary AWS selected host is input only.
type Step struct {
	StateDir       string
	Session        *config.SessionConfig
	Primary        *host.Session
	Owner          string
	CreatedAt      time.Time
	Runner         cpterraform.Runner
	IdentityRunner cpterraform.Runner
	RunRemote      remotek3d.RemoteRunner
	Selected       *host.Session
}

func (s *Step) Name() cluster.StepName { return cluster.StepClusterCreate }

func (s *Step) Run(ctx context.Context) error {
	if s == nil || !config.DedicatedGCPEnabled(s.Session) {
		return nil
	}
	if s.Primary == nil || !s.Primary.IsRemoteEC2() || s.Primary.Remote == nil {
		return fmt.Errorf("GCP regional host requires the persisted primary AWS host")
	}
	primaryIP, err := netip.ParseAddr(strings.TrimSpace(s.Primary.Remote.PublicIP))
	if err != nil || !primaryIP.Is4() {
		return fmt.Errorf("GCP regional host requires the primary AWS host public IPv4 address")
	}
	if strings.TrimSpace(s.Owner) == "" || s.CreatedAt.IsZero() || s.Session.Host.TTL.Duration <= 0 {
		return fmt.Errorf("GCP regional host requires lifecycle owner, created_at, and positive TTL")
	}

	paths := cpterraform.GCPWorkspacePaths(s.StateDir)
	if err := cpterraform.MaterializeSource(
		s.Session.Terraform.Repo,
		cpterraform.DedicatedGCPModule(),
		paths.SourceDir,
	); err != nil {
		return fmt.Errorf("materialize GCP Terraform source: %w", err)
	}
	if err := writeJSONFile(paths.TFVarsPath, &gcpInputs{
		SessionName:          s.Session.Name,
		Owner:                s.Owner,
		TTLExpiresAt:         s.CreatedAt.UTC().Add(s.Session.Host.TTL.Duration).Format(time.RFC3339),
		SeedProjectID:        s.Session.Dedicated.GCP.SeedProjectID,
		DataplaneProjectID:   s.Session.Dedicated.GCP.DataplaneProjectID,
		Region:               s.Session.Dedicated.GCP.Region,
		Zone:                 s.Session.Dedicated.GCP.Zone,
		PrimaryAWSSourceCIDR: primaryIP.String() + "/32",
		RuntimeServiceAccountEmail: config.DedicatedGCPRuntimeServiceAccountEmail(
			s.Session.Dedicated.GCP.SeedProjectID,
		),
	}); err != nil {
		return fmt.Errorf("write GCP Terraform inputs: %w", err)
	}
	runner := s.Runner
	if runner.Run == nil {
		runner.Run = gcloudTerraformCommand
	}
	if _, err := runner.Plan(ctx, &paths); err != nil {
		return fmt.Errorf("plan GCP regional host: %w", err)
	}
	outputData, err := runner.Apply(ctx, &paths)
	if err != nil {
		return fmt.Errorf("apply GCP regional host: %w", err)
	}
	var outputs map[string]cpterraform.OutputValue
	if err := json.Unmarshal(outputData, &outputs); err != nil {
		return fmt.Errorf("parse GCP Terraform outputs: %w", err)
	}
	if err := cpterraform.WriteOutputs(paths.OutputsPath, outputs); err != nil {
		return fmt.Errorf("persist GCP Terraform outputs: %w", err)
	}
	runtime, err := parseRuntime(outputs)
	if err != nil {
		return err
	}
	selected, err := s.selectedFromRuntime(runtime)
	if err != nil {
		return err
	}
	if err := host.Save(selected.StateDir, selected); err != nil {
		return fmt.Errorf("persist GCP regional host: %w", err)
	}
	identity, tls, err := s.applyAWSIdentity(ctx, runtime)
	if err != nil {
		return err
	}
	if err := (&remotek3d.Runtime{
		Selected: selected,
		Options: remotek3d.Options{
			TTL:         s.Session.Host.TTL.Duration,
			PublicHTTP:  true,
			PublicHTTPS: true,
		},
		RunRemote: s.RunRemote,
	}).Prepare(ctx); err != nil {
		return fmt.Errorf("prepare GCP regional k3d host: %w", err)
	}
	if err := s.installAWSWebIdentity(ctx, selected, identity); err != nil {
		return err
	}
	if err := s.applyRegionalTLS(ctx, selected, tls); err != nil {
		return err
	}
	s.Selected = selected
	return nil
}

// Preflight validates the local gcloud identity and exact selected Projects
// without changing provider state.
func Preflight(ctx context.Context, session *config.SessionConfig) error {
	if !config.DedicatedGCPEnabled(session) {
		return nil
	}
	if err := validateCleanupProjectRegistration(session); err != nil {
		return err
	}
	account, err := gcloudValue(ctx, "auth", "list", "--filter=status:ACTIVE", "--format=value(account)")
	if err != nil {
		return fmt.Errorf("GCP preflight active account: %w", err)
	}
	if strings.TrimSpace(account) == "" {
		return fmt.Errorf("GCP preflight requires an active gcloud account")
	}
	projects := []string{
		session.Dedicated.GCP.SeedProjectID,
		session.Dedicated.GCP.DataplaneProjectID,
	}
	seen := map[string]bool{}
	for _, project := range projects {
		if seen[project] {
			continue
		}
		seen[project] = true
		actual, err := gcloudValue(ctx, "projects", "describe", project, "--format=value(projectId)")
		if err != nil {
			return fmt.Errorf("GCP preflight project %s: %w", project, err)
		}
		if strings.TrimSpace(actual) != project {
			return fmt.Errorf("GCP preflight project %s resolved as %q", project, strings.TrimSpace(actual))
		}
	}
	runtimeServiceAccount := config.DedicatedGCPRuntimeServiceAccountEmail(
		session.Dedicated.GCP.SeedProjectID,
	)
	actualServiceAccount, err := gcloudValue(
		ctx,
		"iam", "service-accounts", "describe", runtimeServiceAccount,
		"--project="+session.Dedicated.GCP.SeedProjectID,
		"--format=value(email)",
	)
	if err != nil {
		return fmt.Errorf(
			"GCP preflight shared runtime service account %s: %w; follow terraform/cp9-developer/SKILL.md first",
			runtimeServiceAccount,
			err,
		)
	}
	if strings.TrimSpace(actualServiceAccount) != runtimeServiceAccount {
		return fmt.Errorf(
			"GCP preflight shared runtime service account %s resolved as %q",
			runtimeServiceAccount,
			strings.TrimSpace(actualServiceAccount),
		)
	}
	return nil
}

type cleanupProjectRegistry struct {
	Include []cleanupProjectRegistryEntry `json:"include"`
}

type cleanupProjectRegistryEntry struct {
	ProjectID                string `json:"project_id"`
	ProjectNumber            string `json:"project_number"`
	Region                   string `json:"region"`
	ServiceAccountEmail      string `json:"service_account_email"`
	WorkloadIdentityProvider string `json:"workload_identity_provider"`
}

func validateCleanupProjectRegistration(session *config.SessionConfig) error {
	const registryPath = ".github/cp9-cleanup-gcp-projects.json"
	registryFile := filepath.Join(session.Terraform.Repo, registryPath)
	data, err := os.ReadFile(registryFile)
	if err != nil {
		return fmt.Errorf("read GCP cleanup project registry %s: %w", registryFile, err)
	}
	var registry cleanupProjectRegistry
	decoder := json.NewDecoder(bytes.NewReader(data))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&registry); err != nil {
		return fmt.Errorf("parse GCP cleanup project registry %s: %w", registryFile, err)
	}
	projectID := session.Dedicated.GCP.SeedProjectID
	matches := 0
	for _, entry := range registry.Include {
		if entry.ProjectID != projectID {
			continue
		}
		matches++
		expectedServiceAccount := config.DedicatedGCPRuntimeServiceAccountEmail(projectID)
		expectedProvider := "projects/" + entry.ProjectNumber +
			"/locations/global/workloadIdentityPools/cp9-github/providers/controlplane9"
		if entry.ProjectNumber == "" || strings.Trim(entry.ProjectNumber, "0123456789") != "" ||
			entry.Region != session.Dedicated.GCP.Region || entry.ServiceAccountEmail != expectedServiceAccount ||
			entry.WorkloadIdentityProvider != expectedProvider {
			return fmt.Errorf("GCP cleanup project %s registry contract is incomplete", projectID)
		}
	}
	if matches != 1 {
		return fmt.Errorf("GCP cleanup project %s must appear exactly once in %s", projectID, registryPath)
	}
	return nil
}

type gcpInputs struct {
	SessionName                string `json:"session_name"`
	Owner                      string `json:"owner"`
	TTLExpiresAt               string `json:"ttl_expires_at"`
	SeedProjectID              string `json:"seed_project_id"`
	DataplaneProjectID         string `json:"dataplane_project_id"`
	Region                     string `json:"region"`
	Zone                       string `json:"zone"`
	PrimaryAWSSourceCIDR       string `json:"primary_aws_source_cidr"`
	RuntimeServiceAccountEmail string `json:"runtime_service_account_email"`
}

type runtimeOutput struct {
	ProjectID              string `json:"project_id"`
	DataplaneProjectID     string `json:"dataplane_project_id"`
	Region                 string `json:"region"`
	Zone                   string `json:"zone"`
	InstanceID             string `json:"instance_id"`
	InstanceName           string `json:"instance_name"`
	MachineType            string `json:"machine_type"`
	PublicIP               string `json:"public_ip"`
	ServiceAccountEmail    string `json:"service_account_email"`
	ServiceAccountUniqueID string `json:"service_account_unique_id"`
	Network                string `json:"network"`
	Subnet                 string `json:"subnet"`
	O11YServiceAttachment  string `json:"o11y_service_attachment"`
	O11YBucket             string `json:"o11y_bucket"`
}

func writeJSONFile(filename string, value any) error {
	data, err := json.MarshalIndent(value, "", "  ")
	if err != nil {
		return err
	}
	if err := os.MkdirAll(filepath.Dir(filename), 0o755); err != nil {
		return err
	}
	return os.WriteFile(filename, append(data, '\n'), 0o600)
}

func parseRuntime(outputs map[string]cpterraform.OutputValue) (*runtimeOutput, error) {
	output, ok := outputs["runtime"]
	if !ok {
		return nil, fmt.Errorf("GCP Terraform output runtime is required")
	}
	data, err := json.Marshal(output.Value)
	if err != nil {
		return nil, fmt.Errorf("marshal GCP runtime output: %w", err)
	}
	var runtime runtimeOutput
	if err := json.Unmarshal(data, &runtime); err != nil {
		return nil, fmt.Errorf("parse GCP runtime output: %w", err)
	}
	for name, value := range map[string]string{
		"project_id":                runtime.ProjectID,
		"dataplane_project_id":      runtime.DataplaneProjectID,
		"region":                    runtime.Region,
		"zone":                      runtime.Zone,
		"instance_id":               runtime.InstanceID,
		"instance_name":             runtime.InstanceName,
		"machine_type":              runtime.MachineType,
		"public_ip":                 runtime.PublicIP,
		"service_account_email":     runtime.ServiceAccountEmail,
		"service_account_unique_id": runtime.ServiceAccountUniqueID,
		"network":                   runtime.Network,
		"subnet":                    runtime.Subnet,
		"o11y_service_attachment":   runtime.O11YServiceAttachment,
		"o11y_bucket":               runtime.O11YBucket,
	} {
		if strings.TrimSpace(value) == "" {
			return nil, fmt.Errorf("GCP Terraform output runtime.%s is required", name)
		}
	}
	return &runtime, nil
}

func (s *Step) selectedFromRuntime(runtime *runtimeOutput) (*host.Session, error) {
	if runtime.ProjectID != s.Session.Dedicated.GCP.SeedProjectID ||
		runtime.DataplaneProjectID != s.Session.Dedicated.GCP.DataplaneProjectID ||
		runtime.Region != s.Session.Dedicated.GCP.Region || runtime.Zone != s.Session.Dedicated.GCP.Zone ||
		runtime.MachineType != config.DedicatedGCPRuntimeMachineType ||
		runtime.ServiceAccountEmail != config.DedicatedGCPRuntimeServiceAccountEmail(
			s.Session.Dedicated.GCP.SeedProjectID,
		) {
		return nil, fmt.Errorf("GCP Terraform runtime output does not match the resolved session contract")
	}
	publicIP, err := netip.ParseAddr(runtime.PublicIP)
	if err != nil || !publicIP.Is4() {
		return nil, fmt.Errorf("GCP Terraform runtime output public_ip is not an IPv4 address")
	}
	selected := s.Selected
	if selected == nil {
		selected = host.ResolveGCPRegionalSession(s.Session, s.StateDir)
		if persisted, err := host.Load(selected.StateDir); err == nil {
			selected = persisted
		} else if !os.IsNotExist(err) {
			return nil, fmt.Errorf("load GCP regional host: %w", err)
		}
	}
	if selected.Mode != config.HostModeRemoteGCEK3d || selected.Remote == nil || selected.GCP == nil {
		return nil, fmt.Errorf("persisted GCP regional host contract is invalid")
	}
	if persisted := strings.TrimSpace(selected.Remote.InstanceID); persisted != "" && persisted != runtime.InstanceID {
		return nil, fmt.Errorf("persisted GCP instance %q does not match Terraform instance %q", persisted, runtime.InstanceID)
	}
	selected.Region = runtime.Region
	selected.Remote.Region = runtime.Region
	selected.Remote.InstanceID = runtime.InstanceID
	selected.Remote.InstanceName = runtime.InstanceName
	selected.Remote.PublicIP = runtime.PublicIP
	selected.Remote.SSHTransport = host.SSHTransportGCloudIAP
	selected.GCP.ProjectID = runtime.ProjectID
	selected.GCP.DataplaneProjectID = runtime.DataplaneProjectID
	selected.GCP.Zone = runtime.Zone
	selected.GCP.ServiceAccountEmail = runtime.ServiceAccountEmail
	selected.GCP.ServiceAccountUniqueID = runtime.ServiceAccountUniqueID
	selected.GCP.Network = runtime.Network
	selected.GCP.Subnet = runtime.Subnet
	selected.GCP.O11YServiceAttachment = runtime.O11YServiceAttachment
	selected.GCP.O11YBucket = runtime.O11YBucket
	return selected, nil
}

type identityInputs struct {
	SessionName             string `json:"session_name"`
	Owner                   string `json:"owner"`
	TTLExpiresAt            string `json:"ttl_expires_at"`
	AWSProfile              string `json:"aws_profile"`
	AWSRegion               string `json:"aws_region"`
	TargetAccount           string `json:"target_account"`
	PermissionsBoundaryARN  string `json:"permissions_boundary_arn"`
	DeveloperPowerPolicyARN string `json:"developer_power_policy_arn"`
	ServiceAccountUniqueID  string `json:"service_account_unique_id"`
	Audience                string `json:"audience"`
	Route53ZoneID           string `json:"route53_zone_id"`
	Route53ZoneName         string `json:"route53_zone_name"`
	RegionalEndpointHost    string `json:"regional_endpoint_host"`
	GCPPublicIP             string `json:"gcp_public_ip"`
	PCAARN                  string `json:"pca_arn"`
}

type identityOutput struct {
	Audience               string `json:"audience"`
	RegionalEndpointHost   string `json:"regional_endpoint_host"`
	RegionalEndpointURL    string `json:"regional_endpoint_url"`
	RoleARN                string `json:"role_arn"`
	ServiceAccountUniqueID string `json:"service_account_unique_id"`
}

type regionalTLSOutput struct {
	Certificate      string `json:"certificate"`
	CertificateChain string `json:"certificate_chain"`
	PrivateKey       string `json:"private_key"`
}

func (s *Step) applyAWSIdentity(ctx context.Context, runtime *runtimeOutput) (*identityOutput, *regionalTLSOutput, error) {
	contract, err := cpterraform.LoadOutputContract(cpterraform.WorkspacePaths(s.StateDir).ContractPath)
	if err != nil {
		return nil, nil, fmt.Errorf("load primary Terraform contract for GCP identity: %w", err)
	}
	awsState, err := cpaws.LoadSessionState(s.StateDir)
	if err != nil {
		return nil, nil, fmt.Errorf("load primary AWS state for GCP identity: %w", err)
	}
	if contract.RemoteEC2Runtime == nil || strings.TrimSpace(contract.RemoteEC2Runtime.BoundaryPolicyARN) == "" ||
		strings.TrimSpace(contract.RemoteEC2Runtime.PowerPolicyARN) == "" {
		return nil, nil, fmt.Errorf("GCP AWS identity requires the primary runtime power policy and permissions boundary")
	}
	if contract.SessionProductPCA == nil || strings.TrimSpace(contract.SessionProductPCA.ARN) == "" {
		return nil, nil, fmt.Errorf("GCP AWS identity requires the session Product PCA")
	}
	if contract.DedicatedPublicDNS == nil || strings.TrimSpace(contract.DedicatedPublicDNS.ZoneID) == "" ||
		strings.TrimSpace(contract.DedicatedPublicDNS.Domain) == "" {
		return nil, nil, fmt.Errorf("GCP AWS identity requires the Dedicated public DNS contract")
	}
	identityPaths := cpterraform.GCPAWSIdentityWorkspacePaths(s.StateDir)
	if err := cpterraform.MaterializeSource(
		s.Session.Terraform.Repo,
		cpterraform.DedicatedGCPAWSIdentityModule(),
		identityPaths.SourceDir,
	); err != nil {
		return nil, nil, fmt.Errorf("materialize GCP AWS identity Terraform source: %w", err)
	}
	audience := "cp9-" + s.Session.Name + "-gcp-regional"
	endpointHost := "gcp-regional." + strings.Trim(strings.TrimSpace(contract.DedicatedPublicDNS.Domain), ".")
	if err := writeJSONFile(identityPaths.TFVarsPath, &identityInputs{
		SessionName:             s.Session.Name,
		Owner:                   s.Owner,
		TTLExpiresAt:            s.CreatedAt.UTC().Add(s.Session.Host.TTL.Duration).Format(time.RFC3339),
		AWSProfile:              awsState.Profile,
		AWSRegion:               awsState.Region,
		TargetAccount:           awsState.Account,
		PermissionsBoundaryARN:  contract.RemoteEC2Runtime.BoundaryPolicyARN,
		DeveloperPowerPolicyARN: contract.RemoteEC2Runtime.PowerPolicyARN,
		ServiceAccountUniqueID:  runtime.ServiceAccountUniqueID,
		Audience:                audience,
		Route53ZoneID:           contract.DedicatedPublicDNS.ZoneID,
		Route53ZoneName:         strings.TrimSuffix(strings.TrimSpace(s.Session.Host.Exposure.ZoneName), "."),
		RegionalEndpointHost:    endpointHost,
		GCPPublicIP:             runtime.PublicIP,
		PCAARN:                  contract.SessionProductPCA.ARN,
	}); err != nil {
		return nil, nil, fmt.Errorf("write GCP AWS identity Terraform inputs: %w", err)
	}
	runner := s.IdentityRunner
	if runner.Run == nil {
		runner.Run = awsTerraformCommand(awsState.Profile, awsState.Region)
	}
	if _, err := runner.Plan(ctx, &identityPaths); err != nil {
		return nil, nil, fmt.Errorf("plan GCP AWS identity: %w", err)
	}
	outputData, err := runner.Apply(ctx, &identityPaths)
	if err != nil {
		return nil, nil, fmt.Errorf("apply GCP AWS identity: %w", err)
	}
	var outputs map[string]cpterraform.OutputValue
	if err := json.Unmarshal(outputData, &outputs); err != nil {
		return nil, nil, fmt.Errorf("parse GCP AWS identity outputs: %w", err)
	}
	if err := cpterraform.WriteOutputs(identityPaths.OutputsPath, outputs); err != nil {
		return nil, nil, fmt.Errorf("persist GCP AWS identity outputs: %w", err)
	}
	identity, err := parseOutput[identityOutput](outputs, "identity")
	if err != nil {
		return nil, nil, err
	}
	tls, err := parseOutput[regionalTLSOutput](outputs, "regional_tls")
	if err != nil {
		return nil, nil, err
	}
	for name, value := range map[string]string{
		"audience":                  identity.Audience,
		"regional_endpoint_host":    identity.RegionalEndpointHost,
		"regional_endpoint_url":     identity.RegionalEndpointURL,
		"role_arn":                  identity.RoleARN,
		"service_account_unique_id": identity.ServiceAccountUniqueID,
	} {
		if strings.TrimSpace(value) == "" {
			return nil, nil, fmt.Errorf("GCP AWS identity output identity.%s is required", name)
		}
	}
	if identity.Audience != audience || identity.ServiceAccountUniqueID != runtime.ServiceAccountUniqueID {
		return nil, nil, fmt.Errorf("GCP AWS identity output does not match the exact Google identity contract")
	}
	if strings.TrimSpace(tls.Certificate) == "" || strings.TrimSpace(tls.CertificateChain) == "" ||
		strings.TrimSpace(tls.PrivateKey) == "" {
		return nil, nil, fmt.Errorf("GCP AWS identity output regional_tls is incomplete")
	}
	expectedRoleARN := "arn:aws:iam::" + awsState.Account + ":role/cp9-" + s.Session.Name + "-gcp-regional"
	if identity.RoleARN != expectedRoleARN ||
		strings.TrimSuffix(identity.RegionalEndpointHost, ".") != endpointHost ||
		identity.RegionalEndpointURL != "https://"+endpointHost {
		return nil, nil, fmt.Errorf("GCP AWS identity output does not match the exact AWS endpoint contract")
	}
	return identity, tls, nil
}

func parseOutput[T any](outputs map[string]cpterraform.OutputValue, name string) (*T, error) {
	output, ok := outputs[name]
	if !ok {
		return nil, fmt.Errorf("GCP AWS identity Terraform output %s is required", name)
	}
	data, err := json.Marshal(output.Value)
	if err != nil {
		return nil, fmt.Errorf("marshal GCP AWS identity output %s: %w", name, err)
	}
	var value T
	if err := json.Unmarshal(data, &value); err != nil {
		return nil, fmt.Errorf("parse GCP AWS identity output %s: %w", name, err)
	}
	return &value, nil
}

func (s *Step) installAWSWebIdentity(ctx context.Context, selected *host.Session, identity *identityOutput) error {
	if selected == nil || selected.Remote == nil || selected.GCP == nil || identity == nil {
		return fmt.Errorf("install GCP AWS web identity requires the selected runtime and identity output")
	}
	identityDir := path.Join(selected.Remote.RemoteStateDir, "identity")
	tokenPath := path.Join(identityDir, path.Base(host.GCPAWSWebIdentityTokenNodePath))
	scriptPath := path.Join(identityDir, "refresh-aws-web-identity.sh")
	unitSourcePath := path.Join(identityDir, "cp9-gcp-aws-token.service")
	unitName := "cp9-" + s.Session.Name + "-gcp-aws-token.service"
	metadataURL := "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=" + url.QueryEscape(
		identity.Audience,
	) + "&format=full&licenses=FALSE"
	script := fmt.Sprintf(`#!/bin/sh
set -eu
token=%s
metadata_url=%s
while :; do
  temporary=$(mktemp "${token}.XXXXXX")
  trap 'rm -f -- "$temporary"' EXIT
  curl --connect-timeout 5 --max-time 20 --fail --silent --show-error --header 'Metadata-Flavor: Google' "$metadata_url" >"$temporary"
  test -s "$temporary"
  chmod 0444 "$temporary"
  mv -f "$temporary" "$token"
  trap - EXIT
  sleep 2700
done
`, host.ShellEscape(tokenPath), host.ShellEscape(metadataURL))
	unit := fmt.Sprintf(`[Unit]
Description=CP9 GCP runtime Google-to-AWS identity token
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=%s
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
`, scriptPath)
	if err := s.writeRemoteFile(ctx, selected, scriptPath, []byte(script), 0o700); err != nil {
		return fmt.Errorf("write GCP AWS identity refresher: %w", err)
	}
	if err := s.writeRemoteFile(ctx, selected, unitSourcePath, []byte(unit), 0o600); err != nil {
		return fmt.Errorf("write GCP AWS identity unit: %w", err)
	}
	install := "set -eu; sudo install -m 0644 " + host.ShellEscape(unitSourcePath) + " " + host.ShellEscape("/etc/systemd/system/"+unitName) +
		"; sudo systemctl daemon-reload; sudo systemctl enable --now " + host.ShellEscape(unitName) +
		"; i=0; while [ ! -s " + host.ShellEscape(tokenPath) + " ]; do sudo systemctl is-active --quiet " + host.ShellEscape(unitName) +
		"; i=$((i+1)); [ \"$i\" -lt 60 ] || exit 1; sleep 1; done"
	if _, err := s.runRemote(ctx, selected, nil, "sh", "-lc", install); err != nil {
		return fmt.Errorf("start GCP AWS identity refresher: %w", err)
	}
	probe := "set -eu; export AWS_ROLE_ARN=" + host.ShellEscape(identity.RoleARN) +
		" AWS_WEB_IDENTITY_TOKEN_FILE=" + host.ShellEscape(tokenPath) +
		" AWS_ROLE_SESSION_NAME=cp9-gcp-regional AWS_REGION=" + host.ShellEscape(s.Session.Target.Region) +
		" AWS_DEFAULT_REGION=" + host.ShellEscape(s.Session.Target.Region) +
		" AWS_EC2_METADATA_DISABLED=true; aws sts get-caller-identity --query Arn --output text"
	output, err := s.runRemote(ctx, selected, nil, "sh", "-lc", probe)
	if err != nil {
		return fmt.Errorf("verify GCP AWS web identity: %w", err)
	}
	roleName := identity.RoleARN[strings.LastIndex(identity.RoleARN, "/")+1:]
	if !strings.Contains(strings.TrimSpace(string(output)), ":assumed-role/"+roleName+"/") {
		return fmt.Errorf("verify GCP AWS web identity returned unexpected caller")
	}
	selected.GCP.AWSRoleARN = identity.RoleARN
	selected.GCP.RegionalEndpointHost = strings.TrimSuffix(identity.RegionalEndpointHost, ".")
	selected.GCP.RegionalEndpointURL = identity.RegionalEndpointURL
	return host.Save(selected.StateDir, selected)
}

func (s *Step) applyRegionalTLS(ctx context.Context, selected *host.Session, tls *regionalTLSOutput) error {
	chain := strings.TrimSpace(tls.CertificateChain)
	primaryKubectl := "KUBECONFIG=" + host.ShellEscape(
		s.Primary.Remote.RemoteKubeconfigPath,
	) + " kubectl --context " + host.ShellEscape(
		s.Primary.Context,
	)
	applyPrimary := "set -eu; ca_file=$(mktemp); trap 'rm -f -- \"$ca_file\"' EXIT; cat >\"$ca_file\"; " +
		primaryKubectl + " create namespace cp9-global --dry-run=client -o yaml | " + primaryKubectl + " apply -f -; " +
		primaryKubectl + " create namespace kong-system --dry-run=client -o yaml | " + primaryKubectl + " apply -f -; " +
		primaryKubectl + " create namespace observability --dry-run=client -o yaml | " + primaryKubectl + " apply -f -; " +
		primaryKubectl + " --namespace cp9-global create configmap gcp-regional-ca --from-file=ca.crt=\"$ca_file\" --dry-run=client -o yaml | " + primaryKubectl + " apply -f -; " +
		primaryKubectl + " --namespace kong-system create configmap gcp-regional-ca --from-file=ca.crt=\"$ca_file\" --dry-run=client -o yaml | " + primaryKubectl + " apply -f -; " +
		primaryKubectl + " --namespace observability create configmap gcp-regional-ca --from-file=ca.crt=\"$ca_file\" --dry-run=client -o yaml | " + primaryKubectl + " apply -f -"
	if _, err := s.runRemote(ctx, s.Primary, []byte(chain), "sh", "-lc", applyPrimary); err != nil {
		return fmt.Errorf("apply GCP Regional CA trust: %w", err)
	}

	certificate := strings.TrimSpace(tls.Certificate)
	certificate += "\n" + chain
	secret := func(namespace string) map[string]any {
		return map[string]any{
			"apiVersion": "v1",
			"kind":       "Secret",
			"metadata": map[string]string{
				"name":      "gcp-regional-kong-tls",
				"namespace": namespace,
			},
			"type": "kubernetes.io/tls",
			"data": map[string]string{
				"tls.crt": base64.StdEncoding.EncodeToString([]byte(certificate)),
				"tls.key": base64.StdEncoding.EncodeToString([]byte(tls.PrivateKey)),
			},
		}
	}
	caConfigMap := map[string]any{
		"apiVersion": "v1",
		"kind":       "ConfigMap",
		"metadata": map[string]string{
			"name":      "gcp-regional-ca",
			"namespace": "cp9-dedicated",
		},
		"data": map[string]string{"ca.crt": chain},
	}
	manifest, err := json.Marshal(map[string]any{
		"apiVersion": "v1",
		"kind":       "List",
		"items":      []map[string]any{caConfigMap, secret("kong-system"), secret("cp9-dedicated")},
	})
	if err != nil {
		return fmt.Errorf("marshal GCP Regional TLS secret: %w", err)
	}
	kubectl := "KUBECONFIG=" + host.ShellEscape(selected.Remote.RemoteKubeconfigPath) + " kubectl --context " + host.ShellEscape(selected.Context)
	apply := "set -eu; for namespace in kong-system cp9-dedicated; do " + kubectl + " create namespace \"$namespace\" --dry-run=client -o yaml | " + kubectl + " apply -f -; done; " + kubectl + " apply -f -"
	if _, err := s.runRemote(ctx, selected, manifest, "sh", "-lc", apply); err != nil {
		return fmt.Errorf("apply GCP Regional TLS secret: %w", err)
	}
	return nil
}

func (s *Step) writeRemoteFile(ctx context.Context, selected *host.Session, remotePath string, data []byte, mode os.FileMode) error {
	script := fmt.Sprintf(
		"set -eu; mkdir -p %s; temporary=$(mktemp %s); trap 'rm -f -- \"$temporary\"' EXIT; cat > \"$temporary\"; chmod %04o \"$temporary\"; mv -f \"$temporary\" %s; trap - EXIT",
		host.ShellEscape(path.Dir(remotePath)),
		host.ShellEscape(path.Join(path.Dir(remotePath), "."+path.Base(remotePath)+".XXXXXX")),
		mode.Perm(),
		host.ShellEscape(remotePath),
	)
	_, err := s.runRemote(ctx, selected, data, "sh", "-lc", script)
	return err
}

func (s *Step) runRemote(ctx context.Context, selected *host.Session, stdin []byte, argv ...string) ([]byte, error) {
	if s.RunRemote != nil {
		return s.RunRemote(ctx, selected, stdin, argv...)
	}
	return host.RunSSH(ctx, selected, stdin, argv...)
}

func awsTerraformCommand(profile, region string) cpterraform.CommandRunner {
	return func(ctx context.Context, dir, name string, args ...string) (stdout, stderr []byte, err error) {
		cmd := exec.CommandContext(ctx, name, args...)
		cmd.Dir = dir
		cmd.Env = append(os.Environ(), "AWS_PROFILE="+profile, "AWS_REGION="+region, "AWS_DEFAULT_REGION="+region)
		var stdoutBuffer, stderrBuffer bytes.Buffer
		cmd.Stdout = &stdoutBuffer
		cmd.Stderr = &stderrBuffer
		err = cmd.Run()
		return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), err
	}
}

func gcloudValue(ctx context.Context, args ...string) (string, error) {
	cmd := exec.CommandContext(ctx, "gcloud", args...)
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	output, err := cmd.Output()
	if err != nil {
		message := strings.TrimSpace(stderr.String())
		if message == "" {
			message = err.Error()
		}
		return "", fmt.Errorf("gcloud %s: %s", strings.Join(args, " "), message)
	}
	return strings.TrimSpace(string(output)), nil
}

func gcloudTerraformCommand(
	ctx context.Context,
	dir string,
	name string,
	args ...string,
) (stdout, stderr []byte, err error) {
	token, err := gcloudValue(ctx, "auth", "print-access-token")
	if err != nil {
		return nil, []byte("gcloud access token unavailable"), err
	}
	if token == "" {
		return nil, []byte("gcloud access token unavailable"), fmt.Errorf("gcloud returned an empty access token")
	}
	cmd := exec.CommandContext(ctx, name, args...)
	cmd.Dir = dir
	cmd.Env = append(os.Environ(), "GOOGLE_OAUTH_ACCESS_TOKEN="+token)
	var stdoutBuffer, stderrBuffer bytes.Buffer
	cmd.Stdout = &stdoutBuffer
	cmd.Stderr = &stderrBuffer
	err = cmd.Run()
	return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), err
}
