//nolint:gocritic // Proof rendering deliberately receives immutable activation contracts by value.
package byoc

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
)

const roleProofAWSCLIImage = "public.ecr.aws/aws-cli/aws-cli:2.31.22"

func (d *AWSDriver) applyRoleChainProof(ctx context.Context, input Input) (*StageResult, error) {
	jobName := roleProofJobName(input)
	if _, err := d.kubernetesRunner.Run(ctx, Command{
		Name: "kubectl",
		Args: kubeArgs(input, "-n", byocRuntimeNamespace, "delete", "job", jobName, "--ignore-not-found=true", "--wait=true"),
	}); err != nil {
		return nil, fmt.Errorf("reset BYOC role-chain proof job: %w", err)
	}
	manifest, err := renderRoleProofJob(input)
	if err != nil {
		return nil, err
	}
	if _, err := d.kubernetesRunner.Run(ctx, Command{
		Name: "kubectl", Args: kubeArgs(input, "apply", "-f", "-"), Stdin: manifest,
	}); err != nil {
		return nil, fmt.Errorf("apply BYOC role-chain proof job: %w", err)
	}
	if _, err := d.kubernetesRunner.Run(ctx, Command{
		Name: "kubectl",
		Args: kubeArgs(input, "-n", byocRuntimeNamespace, "wait", "--for=condition=complete", "job/"+jobName, "--timeout=5m"),
	}); err != nil {
		return nil, fmt.Errorf("wait for BYOC role-chain proof job: %w", err)
	}
	output, err := d.kubernetesRunner.Run(ctx, Command{
		Name: "kubectl", Args: kubeArgs(input, "-n", byocRuntimeNamespace, "logs", "job/"+jobName),
	})
	if err != nil {
		return nil, fmt.Errorf("read BYOC role-chain proof result: %w", err)
	}
	observed, err := parseRoleProofOutput(input, output)
	if err != nil {
		return nil, err
	}
	artifacts := roleProofArtifacts(input, observed)
	evidence, err := renderRoleProofEvidence(input, artifacts)
	if err != nil {
		return nil, err
	}
	if _, err := d.kubernetesRunner.Run(ctx, Command{
		Name: "kubectl", Args: kubeArgs(input, "apply", "-f", "-"), Stdin: evidence,
	}); err != nil {
		return nil, fmt.Errorf("persist sanitized BYOC role-chain evidence: %w", err)
	}
	return &StageResult{Artifacts: artifacts}, nil
}

func (d *AWSDriver) revalidateRoleChainProof(ctx context.Context, input Input, record *StageRecord) (*StageResult, error) {
	if record == nil || record.Artifacts["evidence_config_map"] != roleProofEvidenceName(input) {
		return nil, ErrNeedsReconcile
	}
	customerRole, err := d.readIAMRoleWithProfile(ctx, input, input.CustomerProfile, customerDataplaneManagerRoleName)
	if err != nil {
		if isAWSNotFound(err) {
			return nil, ErrNeedsReconcile
		}
		return nil, fmt.Errorf("read live customer BYOC DPM role: %w", err)
	}
	trustIsValid := customerDPMTrustExactlyAllowsControlPlane(
		customerRole.AssumeRolePolicyDocument,
		input.ControlPlaneAccountID,
	)
	if input.ReuseAccountSetup {
		trustIsValid = customerDPMTrustAllowsControlPlane(
			customerRole.AssumeRolePolicyDocument,
			input.ControlPlaneAccountID,
		)
	}
	if customerRole.RoleName != customerDataplaneManagerRoleName ||
		customerRole.ARN != customerDataplaneManagerRoleARN(input.CustomerAccountID) ||
		!trustIsValid {
		return nil, ErrNeedsReconcile
	}
	output, err := d.kubernetesRunner.Run(ctx, Command{
		Name: "kubectl",
		Args: kubeArgs(input, "-n", byocRuntimeNamespace, "get", "configmap", roleProofEvidenceName(input), "-o", "json"),
	})
	if err != nil {
		return nil, ErrNeedsReconcile
	}
	var response struct {
		Data map[string]string `json:"data"`
	}
	if err := json.Unmarshal(output, &response); err != nil {
		return nil, fmt.Errorf("parse sanitized BYOC role-chain evidence: %w", err)
	}
	if _, err := parseRoleProofValues(input, response.Data); err != nil {
		return nil, ErrNeedsReconcile
	}
	for key, expected := range record.Artifacts {
		if response.Data[key] != expected {
			return nil, ErrNeedsReconcile
		}
	}
	return &StageResult{Artifacts: cloneArtifacts(record.Artifacts)}, nil
}

func renderRoleProofJob(input Input) ([]byte, error) {
	jobName := roleProofJobName(input)
	script := fmt.Sprintf(`set +x
base_arn="$(env -u AWS_PROFILE -u AWS_DEFAULT_PROFILE AWS_CONFIG_FILE=/dev/null aws sts get-caller-identity --region %s --query Arn --output text)"
if env -u AWS_PROFILE -u AWS_DEFAULT_PROFILE AWS_CONFIG_FILE=/dev/null aws sts assume-role --region %s --role-arn %s --role-session-name cp9-byoc-direct-denial >/tmp/direct.out 2>/tmp/direct.err; then
  echo "direct customer assume unexpectedly succeeded" >&2
  exit 41
fi
if ! grep -qi 'AccessDenied' /tmp/direct.err; then
  echo "direct customer assume failed without an AccessDenied decision" >&2
  exit 42
fi
control_plane_arn="$(aws sts get-caller-identity --region %s --query Arn --output text)"
credentials="$(aws sts assume-role --region %s --role-arn %s --role-session-name cp9-byoc-customer-proof --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text)"
IFS="$(printf '\t')" read -r access_key secret_key session_token <<EOF
$credentials
EOF
customer_arn="$(env -u AWS_PROFILE -u AWS_DEFAULT_PROFILE AWS_CONFIG_FILE=/dev/null AWS_ACCESS_KEY_ID="$access_key" AWS_SECRET_ACCESS_KEY="$secret_key" AWS_SESSION_TOKEN="$session_token" aws sts get-caller-identity --region %s --query Arn --output text)"
unset credentials access_key secret_key session_token
printf 'base_arn=%%s\ndirect_customer_assume=denied\ncontrol_plane_arn=%%s\ncustomer_arn=%%s\n' "$base_arn" "$control_plane_arn" "$customer_arn"
`, input.Region, input.Region, customerDataplaneManagerRoleARN(input.CustomerAccountID), input.Region,
		input.Region, customerDataplaneManagerRoleARN(input.CustomerAccountID), input.Region)
	document := map[string]any{
		"apiVersion": "batch/v1",
		"kind":       "Job",
		"metadata": map[string]any{
			"name": jobName, "namespace": byocRuntimeNamespace,
			"labels": roleProofLabels(input),
		},
		"spec": map[string]any{
			"backoffLimit": 0,
			"template": map[string]any{
				"metadata": map[string]any{"labels": roleProofLabels(input)},
				"spec": map[string]any{
					"restartPolicy":      "Never",
					"hostNetwork":        true,
					"dnsPolicy":          "ClusterFirstWithHostNet",
					"serviceAccountName": "byoc-dataplane-manager",
					"containers": []any{map[string]any{
						"name": "proof", "image": roleProofAWSCLIImage,
						"command": []any{"/bin/sh", "-ceu"}, "args": []any{script},
						"env": []any{
							map[string]any{"name": "AWS_CONFIG_FILE", "value": "/etc/cp9-byoc-aws/config"},
							map[string]any{"name": "AWS_EC2_METADATA_DISABLED", "value": "false"},
							map[string]any{"name": "EXPECTED_CONTROL_PLANE_ROLE_ARN", "value": byocControlPlaneRoleARN(input)},
						},
						"volumeMounts": []any{map[string]any{
							"name": "aws-config", "mountPath": "/etc/cp9-byoc-aws/config", "subPath": "config", "readOnly": true,
						}},
					}},
					"volumes": []any{map[string]any{
						"name": "aws-config", "configMap": map[string]any{"name": "byoc-aws-runtime-config"},
					}},
				},
			},
		},
	}
	return encodeYAMLDocuments([]map[string]any{document})
}

func renderRoleProofEvidence(input Input, artifacts map[string]string) ([]byte, error) {
	document := map[string]any{
		"apiVersion": "v1",
		"kind":       "ConfigMap",
		"metadata": map[string]any{
			"name": roleProofEvidenceName(input), "namespace": byocRuntimeNamespace,
			"labels": roleProofLabels(input),
		},
		"data": artifacts,
	}
	return encodeYAMLDocuments([]map[string]any{document})
}

func parseRoleProofOutput(input Input, output []byte) (map[string]string, error) {
	values := make(map[string]string)
	for line := range strings.SplitSeq(strings.TrimSpace(string(output)), "\n") {
		key, value, ok := strings.Cut(strings.TrimSpace(line), "=")
		if !ok || strings.TrimSpace(key) == "" {
			return nil, fmt.Errorf("BYOC role-chain proof returned malformed evidence")
		}
		values[strings.TrimSpace(key)] = strings.TrimSpace(value)
	}
	return parseRoleProofValues(input, values)
}

func parseRoleProofValues(input Input, values map[string]string) (map[string]string, error) {
	required := []string{"base_arn", "direct_customer_assume", "control_plane_arn", "customer_arn"}
	for _, key := range required {
		if strings.TrimSpace(values[key]) == "" {
			return nil, fmt.Errorf("BYOC role-chain proof is missing %s", key)
		}
	}
	if values["direct_customer_assume"] != "denied" {
		return nil, fmt.Errorf("remote runtime direct customer assume was not denied")
	}
	if !isAssumedRoleARN(values["base_arn"], input.ControlPlaneAccountID, roleNameFromARN(input.RemoteRuntimeRoleARN)) {
		return nil, fmt.Errorf("BYOC role-chain base identity is not the remote runtime role in the control-plane account")
	}
	if !isAssumedRoleARN(values["control_plane_arn"], input.ControlPlaneAccountID, byocControlPlaneRoleName(input.Session)) {
		return nil, fmt.Errorf("BYOC role-chain control-plane identity is not the session BYOC DPM role in the control-plane account")
	}
	if !isAssumedRoleARN(values["customer_arn"], input.CustomerAccountID, customerDataplaneManagerRoleName) {
		return nil, fmt.Errorf("BYOC role-chain customer identity is not the official customer DPM role in the customer account")
	}
	return values, nil
}

func isAssumedRoleARN(value, accountID, roleName string) bool {
	parts := strings.SplitN(strings.TrimSpace(value), ":", 6)
	return len(parts) == 6 && parts[0] == "arn" && parts[2] == "sts" && parts[4] == accountID &&
		strings.HasPrefix(parts[5], "assumed-role/"+roleName+"/")
}

func roleProofArtifacts(input Input, values map[string]string) map[string]string {
	result := map[string]string{
		"job":                 roleProofJobName(input),
		"evidence_config_map": roleProofEvidenceName(input),
	}
	for _, key := range []string{"base_arn", "direct_customer_assume", "control_plane_arn", "customer_arn"} {
		result[key] = values[key]
	}
	return result
}

func roleProofJobName(input Input) string {
	return boundedKubernetesName(input.Session + "-byoc-role-proof")
}
func roleProofEvidenceName(input Input) string {
	return boundedKubernetesName(input.Session + "-byoc-role-proof-evidence")
}

func boundedKubernetesName(value string) string {
	value = strings.ToLower(strings.TrimSpace(value))
	var cleaned strings.Builder
	for _, r := range value {
		if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' {
			_, _ = cleaned.WriteRune(r)
		} else {
			_ = cleaned.WriteByte('-')
		}
	}
	value = strings.Trim(cleaned.String(), "-")
	if len(value) <= 63 {
		return value
	}
	suffix := fmt.Sprintf("-%08x", uint32(stableCatalogID(value)))
	return strings.Trim(value[:63-len(suffix)], "-") + suffix
}

func boundedKubernetesLabelValue(value string) string {
	original := strings.TrimSpace(value)
	var cleaned strings.Builder
	for _, r := range original {
		if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') ||
			(r >= '0' && r <= '9') || r == '-' || r == '_' || r == '.' {
			_, _ = cleaned.WriteRune(r)
		} else {
			_ = cleaned.WriteByte('-')
		}
	}
	value = strings.Trim(cleaned.String(), "-_.")
	if value == "" {
		value = "cp9"
	}
	if len(value) <= 63 {
		return value
	}
	suffix := fmt.Sprintf("-%08x", uint32(stableCatalogID(original)))
	return strings.Trim(value[:63-len(suffix)], "-_.") + suffix
}

func roleProofLabels(input Input) map[string]any {
	return map[string]any{
		"app.kubernetes.io/managed-by": "cp9-byoc",
		"cp9.tidbcloud.com/owner":      boundedKubernetesLabelValue(input.Owner),
		"cp9.tidbcloud.com/session":    boundedKubernetesLabelValue(input.Session),
	}
}
