#!/bin/sh
set -eu

usage() {
  echo "usage: $0 <kustomize-path> <namespace> <kubeconfig> <context> <session-context> <old=new>..." >&2
  exit 2
}

[ "$#" -ge 5 ] || usage

kustomize_path=$1
namespace=$2
kubeconfig=$3
context=$4
session_context=$5
shift 5

if [ "${CP9_AWS_RUNTIME_IDENTITY:-}" = "ec2-instance-profile" ]; then
  unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_SECURITY_TOKEN
  unset AWS_PROFILE AWS_DEFAULT_PROFILE AWS_SHARED_CREDENTIALS_FILE AWS_CONFIG_FILE
  unset AWS_EC2_METADATA_DISABLED AWS_WEB_IDENTITY_TOKEN_FILE AWS_ROLE_ARN
fi

patch_image_pull_secret_before_apply() {
  image_pull_secret=${CP9_IMAGE_PULL_SECRET:-}
  if [ -z "$image_pull_secret" ] || [ -z "$namespace" ]; then
    return 0
  fi

  workload_patch=$(
    python3 - "$image_pull_secret" <<'PY'
import json
import sys

print(json.dumps({
    "spec": {
        "template": {
            "spec": {
                "imagePullSecrets": [{"name": sys.argv[1]}],
            },
        },
    },
}))
PY
  )
  python3 "$(dirname "$0")/patch-local-manifests.py" "$target_namespace_deferred_rendered" \
    --kind Deployment --kind StatefulSet --kind DaemonSet \
    --patch "$workload_patch"
}

patch_runtime_config_rollout_stamp() {
  runtime_stamp=$(
    python3 - "$account_list_patch" "$root_config_patch" "$providerconfigs" <<'PY'
import hashlib
import sys

digest = hashlib.sha256()
for path in sys.argv[1:]:
    with open(path, "rb") as source:
        digest.update(source.read())
print(digest.hexdigest())
PY
  )
  annotation_patch=$(
    python3 - "$runtime_stamp" <<'PY'
import json
import sys

print(json.dumps({
    "spec": {
        "template": {
            "metadata": {
                "annotations": {
                    "cp9.tidbcloud.com/runtime-config-sha256": sys.argv[1],
                },
            },
        },
    },
}))
PY
  )
  python3 "$(dirname "$0")/patch-local-manifests.py" "$target_namespace_deferred_rendered" \
    --kind Deployment --kind StatefulSet --kind DaemonSet \
    --patch "$annotation_patch"
}

patch_image_rollout_stamp() {
  stamp=${CP9_IMAGE_ROLLOUT_STAMP:-}
  if [ -z "$stamp" ]; then
    return 0
  fi
  python3 - "$rendered" "$stamp" <<'PY'
import json
import re
import sys
from pathlib import Path

path = Path(sys.argv[1])
stamp = sys.argv[2]
annotation_key = "cp9.tidbcloud.com/source-image-stamp"
workload_kinds = {"Deployment", "StatefulSet", "DaemonSet"}


def indent_len(line: str) -> int:
    return len(line) - len(line.lstrip(" "))


def find_kind(lines):
    for line in lines:
        match = re.match(r"^kind:\s*(\S+)\s*$", line)
        if match:
            return match.group(1)
    return ""


def patch_document(document: str) -> str:
    lines = document.splitlines()
    if find_kind(lines) not in workload_kinds:
        return document

    template_index = -1
    template_indent = -1
    for index, line in enumerate(lines):
        if re.match(r"^\s*template:\s*$", line):
            template_index = index
            template_indent = indent_len(line)
            break
    if template_index < 0:
        return document

    child_indent = template_indent + 2
    metadata_index = -1
    scan = template_index + 1
    while scan < len(lines):
        if lines[scan].strip() and indent_len(lines[scan]) <= template_indent:
            break
        if indent_len(lines[scan]) == child_indent and re.match(r"^\s*metadata:\s*$", lines[scan]):
            metadata_index = scan
            break
        scan += 1
    if metadata_index < 0:
        lines[template_index + 1:template_index + 1] = [
            " " * child_indent + "metadata:",
            " " * (child_indent + 2) + "annotations:",
            " " * (child_indent + 4) + annotation_key + ": " + json.dumps(stamp),
        ]
        return "\n".join(lines)

    annotations_index = -1
    scan = metadata_index + 1
    while scan < len(lines):
        if lines[scan].strip() and indent_len(lines[scan]) <= child_indent:
            break
        if indent_len(lines[scan]) == child_indent + 2 and re.match(r"^\s*annotations:\s*$", lines[scan]):
            annotations_index = scan
            break
        scan += 1
    if annotations_index < 0:
        lines[metadata_index + 1:metadata_index + 1] = [
            " " * (child_indent + 2) + "annotations:",
            " " * (child_indent + 4) + annotation_key + ": " + json.dumps(stamp),
        ]
        return "\n".join(lines)

    key_indent = child_indent + 4
    scan = annotations_index + 1
    while scan < len(lines):
        if lines[scan].strip() and indent_len(lines[scan]) <= child_indent + 2:
            break
        if re.match(r"^\s*" + re.escape(annotation_key) + r":\s*", lines[scan]):
            lines[scan] = " " * key_indent + annotation_key + ": " + json.dumps(stamp)
            return "\n".join(lines)
        scan += 1
    lines[annotations_index + 1:annotations_index + 1] = [
        " " * key_indent + annotation_key + ": " + json.dumps(stamp),
    ]
    return "\n".join(lines)


text = path.read_text()
documents = re.split(r"\n---\n", text)
path.write_text("\n---\n".join(patch_document(document) for document in documents))
PY
}

strip_remote_human_aws_credentials() {
  if [ "${CP9_AWS_RUNTIME_IDENTITY:-}" = "ec2-instance-profile" ]; then
    python3 "$(dirname "$0")/strip-human-aws-credentials.py" "$rendered"
  fi
}

patch_account_scoped_resources() {
  python3 - "$rendered" "$session_context" <<'PY'
import json
import re
import sys

manifest_path, ctx_path = sys.argv[1:3]
ctx = json.load(open(ctx_path))
target = ctx.get("target") or {}
account_id = str(target.get("account") or "").strip()
region = str(target.get("region") or "").strip()
terraform = ctx.get("terraform") or {}
pca = terraform.get("session_product_pca") or {}
pca_arn = str(pca.get("arn") or "").strip()
pca_region = str(pca.get("region") or "").strip()

if not re.fullmatch(r"\d{12}", account_id):
    raise SystemExit("CP9_SESSION_CONTEXT.target.account must be a 12-digit AWS account id")
if not region:
    raise SystemExit("CP9_SESSION_CONTEXT.target.region is required for dataplane-manager runtime")
if pca_region != region:
    raise SystemExit(
        "CP9_SESSION_CONTEXT.terraform.session_product_pca.region "
        f"must equal target.region {region!r}"
    )
if not re.fullmatch(
    rf"arn:aws(?:-[a-z0-9-]+)?:acm-pca:{re.escape(region)}:{account_id}:"
    r"certificate-authority/[0-9a-fA-F-]+",
    pca_arn,
):
    raise SystemExit(
        "CP9_SESSION_CONTEXT.terraform.session_product_pca.arn "
        "must be a target-account ACM PCA ARN"
    )

text = open(manifest_path).read()
account_placeholder = "000000000000"
account_count = text.count(account_placeholder)
if account_count == 0:
    raise SystemExit("dataplane-manager manifest is missing the account placeholder")
text = text.replace(account_placeholder, account_id)
text, pca_count = re.subn(
    r"arn:aws(?:-[a-z0-9-]+)?:acm-pca:[^:\s]+:\d{12}:"
    r"certificate-authority/[0-9a-fA-F-]+",
    pca_arn,
    text,
)
if pca_count != 2:
    raise SystemExit(
        "dataplane-manager manifest must contain exactly two ACM PCA ARNs; "
        f"found {pca_count}"
    )
open(manifest_path, "w").write(text)
PY
}

verify_provider_config() {
  provider_config=$1
  expected_role_arn=$2
  runtime_identity=$3

  KUBECONFIG="$kubeconfig" kubectl --context "$context" get providerconfig.aws.upbound.io "$provider_config" -o json |
    python3 -c '
import json
import sys

name = sys.argv[1]
expected_role_arn = sys.argv[2]
runtime_identity = sys.argv[3]
provider_config = json.load(sys.stdin)
spec = provider_config.get("spec") or {}
credentials = spec.get("credentials") or {}

expected_source = "PodIdentity" if runtime_identity == "ec2-instance-profile" else "Secret"
source = credentials.get("source")
if source != expected_source:
    raise SystemExit(f"providerconfig {name} must use credentials.source={expected_source}, got {source!r}")

secret_ref = credentials.get("secretRef")
if expected_source == "PodIdentity":
    if secret_ref:
        raise SystemExit(f"providerconfig {name} must not use secretRef with source PodIdentity")
else:
    secret_ref = secret_ref or {}
    expected_secret_ref = {
        "namespace": "crossplane-system",
        "name": "aws-creds",
        "key": "creds",
    }
    for key, expected in expected_secret_ref.items():
        actual = secret_ref.get(key)
        if actual != expected:
            raise SystemExit(f"providerconfig {name} must use secretRef.{key}={expected!r}, got {actual!r}")

assume_role_chain = spec.get("assumeRoleChain")
if not isinstance(assume_role_chain, list) or len(assume_role_chain) != 1 or not isinstance(assume_role_chain[0], dict):
    raise SystemExit(f"providerconfig {name} must use exactly one assumeRoleChain role, got {assume_role_chain!r}")
actual_role_arn = assume_role_chain[0].get("roleARN")
if actual_role_arn != expected_role_arn:
    raise SystemExit(f"providerconfig {name} must assume {expected_role_arn}, got {actual_role_arn!r}")
	' "$provider_config" "$expected_role_arn" "$runtime_identity"
}

print_live_yaml() {
  if [ "$print_applied_yaml" != "true" ]; then
    return 0
  fi
  KUBECONFIG="$kubeconfig" kubectl --context "$context" --namespace "$namespace" get -f "$rendered" -o yaml >&3
}

rendered=$(mktemp)
target_namespace_rendered=$(mktemp)
target_namespace_deferred_rendered=$(mktemp)
cross_namespace_rendered=$(mktemp)
account_list_live=$(mktemp)
account_list_patch=$(mktemp)
root_config_live=$(mktemp)
root_config_patch=$(mktemp)
providerconfigs=$(mktemp)
providerconfig_account_patch=$(mktemp)
providerconfig_dns_patch=$(mktemp)
eks_service_role_manifest=$(mktemp)
trap 'rm -f "$rendered" "$target_namespace_rendered" "$target_namespace_deferred_rendered" "$cross_namespace_rendered" "$account_list_live" "$account_list_patch" "$root_config_live" "$root_config_patch" "$providerconfigs" "$providerconfig_account_patch" "$providerconfig_dns_patch" "$eks_service_role_manifest"' EXIT
print_applied_yaml=${CP9_PRINT_APPLIED_YAML:-false}
if [ "$print_applied_yaml" = "true" ]; then
  exec 3>&1
  exec 1>&2
fi

kubectl kustomize "$kustomize_path" >"$rendered"
python3 - "$rendered" "$session_context" <<'PY'
import json
import pathlib
import sys

manifest_path = pathlib.Path(sys.argv[1])
ctx = json.loads(pathlib.Path(sys.argv[2]).read_text())
text = manifest_path.read_text()
if "__CP9_SESSION_NAME__" in text:
    session_name = str(ctx.get("session_name") or "").strip()
    if not session_name:
        raise SystemExit("CP9_SESSION_CONTEXT.session_name is required for dataplane-manager O11Y")
    text = text.replace("__CP9_SESSION_NAME__", session_name)
manifest_path.write_text(text)
PY

for replacement in "$@"; do
  old=${replacement%%=*}
  new=${replacement#*=}
  python3 - "$rendered" "$old" "$new" <<'PY'
import pathlib
import sys

path = pathlib.Path(sys.argv[1])
old = sys.argv[2]
new = sys.argv[3]
text = path.read_text()
count = text.count(old)
if count == 0:
    print(f"expected image replacement not found: {old}", file=sys.stderr)
    sys.exit(1)
path.write_text(text.replace(old, new))
PY
done

python3 - "$rendered" <<'PY'
import pathlib
import sys

path = pathlib.Path(sys.argv[1])
path.write_text(path.read_text().replace("imagePullPolicy: Always", "imagePullPolicy: IfNotPresent"))
PY

patch_image_rollout_stamp
strip_remote_human_aws_credentials
patch_account_scoped_resources

python3 - "$rendered" "$target_namespace_rendered" "$target_namespace_deferred_rendered" "$cross_namespace_rendered" "$namespace" <<'PY'
import pathlib
import re
import sys

source = pathlib.Path(sys.argv[1])
target_path = pathlib.Path(sys.argv[2])
target_deferred_path = pathlib.Path(sys.argv[3])
cross_path = pathlib.Path(sys.argv[4])
target_namespace = sys.argv[5]


def metadata_namespace(document: str) -> str:
    lines = document.splitlines()
    for index, line in enumerate(lines):
        if line == "metadata:":
            for child in lines[index + 1:]:
                if child and not child.startswith(" "):
                    break
                match = re.match(r"^  namespace:\s*(.*?)\s*$", child)
                if match:
                    value = match.group(1).strip()
                    if len(value) >= 2 and value[0] in ("'", '"') and value[-1] == value[0]:
                        value = value[1:-1]
                    return value
    return ""


def top_level_value(document: str, key: str) -> str:
    match = re.search(r"^" + re.escape(key) + r":\s*(.*?)\s*$", document, flags=re.MULTILINE)
    if not match:
        return ""
    value = match.group(1).strip()
    if len(value) >= 2 and value[0] in ("'", '"') and value[-1] == value[0]:
        value = value[1:-1]
    return value


def metadata_name(document: str) -> str:
    lines = document.splitlines()
    for index, line in enumerate(lines):
        if line == "metadata:":
            for child in lines[index + 1:]:
                if child and not child.startswith(" "):
                    break
                match = re.match(r"^  name:\s*(.*?)\s*$", child)
                if match:
                    value = match.group(1).strip()
                    if len(value) >= 2 and value[0] in ("'", '"') and value[-1] == value[0]:
                        value = value[1:-1]
                    return value
    return ""


def defer_target_document(document: str) -> bool:
    return (
        top_level_value(document, "kind") == "Deployment"
        and metadata_name(document) in {"dataplane-controller", "dataplane-server"}
    )


def write_documents(path: pathlib.Path, documents: list[str]) -> None:
    documents = [document.strip() for document in documents if document.strip()]
    if documents:
        path.write_text("\n---\n".join(documents) + "\n")
    else:
        path.write_text("")


target_docs = []
target_deferred_docs = []
cross_docs = []
for raw_document in re.split(r"^---\s*$", source.read_text(), flags=re.MULTILINE):
    document = raw_document.strip()
    if not document:
        continue
    namespace = metadata_namespace(document)
    if namespace and target_namespace and namespace != target_namespace:
        cross_docs.append(document)
    elif defer_target_document(document):
        target_deferred_docs.append(document)
    else:
        target_docs.append(document)

write_documents(target_path, target_docs)
write_documents(target_deferred_path, target_deferred_docs)
write_documents(cross_path, cross_docs)
PY

if [ "${CP9_RENDER_ONLY:-false}" = "true" ]; then
  cat "$rendered"
  exit 0
fi

KUBECONFIG="$kubeconfig" kubectl --context "$context" --namespace "$namespace" apply -f "$target_namespace_rendered"

runtime_json=$(
  python3 - "$session_context" <<'PY'
import ipaddress
import json
import os
import pathlib
import re
import sys
from datetime import datetime

ctx_path = pathlib.Path(sys.argv[1])
ctx = json.loads(ctx_path.read_text())
terraform = ctx.get("terraform") or {}
iam = terraform.get("iam") or {}
contract_path = terraform.get("contract_path") or ""
if not iam and contract_path:
    try:
        contract = json.loads(pathlib.Path(contract_path).read_text())
        iam = contract.get("iam") or {}
    except FileNotFoundError:
        pass

target = ctx.get("target") or {}
account = str(target.get("account") or "").strip()
region = str(target.get("region") or "").strip()
session_name = str(ctx.get("session_name") or (ctx.get("host") or {}).get("cluster_name") or "").strip()
owner = str(ctx.get("owner") or "").strip()
session_tags = ctx.get("session_tags") or {}
if not isinstance(session_tags, dict):
    raise SystemExit("CP9_SESSION_CONTEXT.session_tags must be an object")
cp9_owner_tag = str(session_tags.get("cp9-owner") or "").strip()
cp9_session_tag = str(session_tags.get("cp9-session") or "").strip()
ttl_expires_at = str(ctx.get("ttl_expires_at") or "").strip()
dataplane_role_name = str(iam.get("dataplane_role_name") or "").strip()
dataplane_role_arn = str(iam.get("dataplane_role_arn") or "").strip()
developer_role_arn = str(iam.get("role_arn") or "").strip()
public_dns = terraform.get("dedicated_public_dns") or {}
public_dns_zone_id = str(public_dns.get("zone_id") or "").strip()
public_dns_domain = str(public_dns.get("domain") or "").strip().rstrip(".")

if not account:
    raise SystemExit("CP9_SESSION_CONTEXT.target.account is required for dataplane-manager runtime")
if not region:
    raise SystemExit("CP9_SESSION_CONTEXT.target.region is required for dataplane-manager runtime")
if not session_name:
    raise SystemExit("CP9_SESSION_CONTEXT.session_name is required for dataplane-manager runtime")
if not cp9_owner_tag or cp9_owner_tag == "required":
    raise SystemExit("CP9_SESSION_CONTEXT.session_tags.cp9-owner is required for dataplane-manager cloud tags")
if not cp9_session_tag or cp9_session_tag == "required":
    raise SystemExit("CP9_SESSION_CONTEXT.session_tags.cp9-session is required for dataplane-manager cloud tags")
if not ttl_expires_at:
    raise SystemExit("CP9_SESSION_CONTEXT.ttl_expires_at is required for dataplane-manager cloud tags")
try:
    ttl_dt = datetime.fromisoformat(ttl_expires_at.replace("Z", "+00:00"))
except ValueError as exc:
    raise SystemExit(f"CP9_SESSION_CONTEXT.ttl_expires_at must be RFC3339: {exc}") from exc
if ttl_dt.tzinfo is None:
    raise SystemExit("CP9_SESSION_CONTEXT.ttl_expires_at must include timezone")
if not dataplane_role_arn:
    raise SystemExit("CP9_SESSION_CONTEXT.terraform.iam.dataplane_role_arn is required for dataplane-manager runtime")
if not dataplane_role_name:
    dataplane_role_name = dataplane_role_arn.rsplit("/", 1)[-1]
if not developer_role_arn:
    raise SystemExit("CP9_SESSION_CONTEXT.terraform.iam.role_arn is required for dataplane-manager DNS runtime")
expected_public_dns_domain = f"{session_name}.{account}.cp9.dev.aws.tidbcloud.com"
if not re.fullmatch(r"Z[A-Z0-9]+", public_dns_zone_id):
    raise SystemExit("CP9_SESSION_CONTEXT.terraform.dedicated_public_dns.zone_id must be a hosted zone ID")
if public_dns_domain != expected_public_dns_domain:
    raise SystemExit(
        "CP9_SESSION_CONTEXT.terraform.dedicated_public_dns.domain must be "
        + expected_public_dns_domain
    )

def normalize_local_eks_public_access_cidr(raw):
    raw = str(raw or "").strip()
    if not raw:
        raise SystemExit("local EKS public access CIDR is required")
    try:
        addr = ipaddress.ip_address(raw)
    except ValueError:
        try:
            network = ipaddress.ip_network(raw, strict=False)
        except ValueError as exc:
            raise SystemExit(f"invalid local EKS public access CIDR {raw!r}: {exc}") from exc
        if network.version != 4:
            raise SystemExit(f"local EKS public access CIDR {raw!r} must be IPv4")
        if network.network_address.is_unspecified and network.prefixlen == 0:
            return str(network)
        if network.prefixlen != 32 or network.network_address.is_unspecified:
            raise SystemExit(f"local EKS public access CIDR {raw!r} must be a non-broad /32 or 0.0.0.0/0")
        return str(network)
    if addr.version != 4:
        raise SystemExit(f"local EKS public access address {raw!r} must be IPv4")
    if addr.is_unspecified:
        raise SystemExit(f"local EKS public access address {raw!r} must not be unspecified")
    return f"{addr}/32"

public_access_cidr = os.environ.get("CP9_LOCAL_EKS_PUBLIC_ACCESS_CIDR", "0.0.0.0/0").strip()
public_access_cidr = normalize_local_eks_public_access_cidr(public_access_cidr)

role_segment = []
last_hyphen = False
for ch in session_name.strip():
    ok = ch.isalnum()
    if ok:
        role_segment.append(ch)
        last_hyphen = False
    elif not last_hyphen:
        role_segment.append("-")
        last_hyphen = True
role_segment = "".join(role_segment).strip("-")
eks_service_role_name = (role_segment + "-eks-service").strip("-")[:64].strip("-")
if not eks_service_role_name:
    raise SystemExit("unable to derive dataplane-manager EKS service role name")

def aws_tag_label_value(value):
    value = str(value or "").strip()
    if not value:
        return "cp9"
    value = re.sub(r"[^A-Za-z0-9-]+", "-", value).strip("-")
    if not value:
        return "cp9"
    if len(value) > 35:
        value = value[:35].strip("-")
        if not value:
            return "cp9"
    return value


print(json.dumps({
    "account": account,
    "region": region,
    "session_name": session_name,
    "owner": owner,
    "cp9_owner_tag": cp9_owner_tag,
    "cp9_session_tag": cp9_session_tag,
    "cp9_ttl_expires_at": ttl_expires_at,
    "cp9_ttl_label": aws_tag_label_value(ttl_expires_at),
    "eks_service_role_name": eks_service_role_name,
    "dataplane_role_name": dataplane_role_name,
    "dataplane_role_arn": dataplane_role_arn,
    "developer_role_arn": developer_role_arn,
    "public_dns_zone_id": public_dns_zone_id,
    "public_dns_domain": public_dns_domain,
    "eks_public_access_cidr": public_access_cidr,
    "aws_runtime_identity": os.environ.get("CP9_AWS_RUNTIME_IDENTITY", ""),
}))
PY
)

target_account=$(
  python3 - "$runtime_json" <<'PY'
import json
import sys

print(json.loads(sys.argv[1])["account"])
PY
)

target_account_role_arn=$(
  python3 - "$runtime_json" <<'PY'
import json
import sys

print(json.loads(sys.argv[1])["dataplane_role_arn"])
PY
)

KUBECONFIG="$kubeconfig" kubectl --context "$context" get crd providerconfigs.aws.upbound.io >/dev/null

python3 - "$runtime_json" >"$eks_service_role_manifest" <<'PY'
import json
import sys

runtime = json.loads(sys.argv[1])
role_name = runtime["eks_service_role_name"]
attachment_name = (role_name + "-cluster-policy")[:253].strip("-")
tags = {
    "Name": role_name,
    "cp9-account": runtime["account"],
    "cp9-managed": "true",
    "cp9-owner": runtime["cp9_owner_tag"],
    "cp9-persona": "premium",
    "cp9-region": runtime["region"],
    "cp9-session": runtime["cp9_session_tag"],
    "cp9-ttl": runtime["cp9_ttl_expires_at"],
}
tag_lines = "".join(f"      {json.dumps(k)}: {json.dumps(v)}\n" for k, v in sorted(tags.items()))

sys.stdout.write(f"""apiVersion: iam.aws.upbound.io/v1beta1
kind: Role
metadata:
  name: {json.dumps(role_name)}
spec:
  deletionPolicy: Delete
  providerConfigRef:
    name: {json.dumps(runtime["account"])}
  managementPolicies:
    - Create
    - Update
    - Observe
    - LateInitialize
  forProvider:
    assumeRolePolicy: '{{"Version":"2012-10-17","Statement":[{{"Effect":"Allow","Principal":{{"Service":"eks.amazonaws.com"}},"Action":"sts:AssumeRole"}}]}}'
    description: "cp9 local Premium EKS cluster service role"
    tags:
{tag_lines}---
apiVersion: iam.aws.upbound.io/v1beta1
kind: RolePolicyAttachment
metadata:
  name: {json.dumps(attachment_name)}
spec:
  deletionPolicy: Delete
  providerConfigRef:
    name: {json.dumps(runtime["account"])}
  managementPolicies:
    - Create
    - Update
    - Observe
    - LateInitialize
  forProvider:
    policyArn: arn:aws:iam::aws:policy/AmazonEKSClusterPolicy
    roleRef:
      name: {json.dumps(role_name)}
""")
PY

KUBECONFIG="$kubeconfig" kubectl --context "$context" apply -f "$eks_service_role_manifest"

KUBECONFIG="$kubeconfig" kubectl --context "$context" --namespace "$namespace" get configmap account-list -o json >"$account_list_live"
python3 - "$runtime_json" "$account_list_live" >"$account_list_patch" <<'PY'
import json
import pathlib
import sys

runtime = json.loads(sys.argv[1])
cm = json.loads(pathlib.Path(sys.argv[2]).read_text())
cm.pop("status", None)
metadata = cm.setdefault("metadata", {})
for key in ("creationTimestamp", "resourceVersion", "uid", "managedFields", "generation"):
    metadata.pop(key, None)
cm["data"] = {
    "account-list.yaml": (
        "accounts:\n"
        f"  - accountID: {json.dumps(runtime['account'])}\n"
        f"    roleName: {runtime['dataplane_role_name']}\n"
        "    externalID: \"\"\n"
    )
}
json.dump(cm, sys.stdout)
PY

KUBECONFIG="$kubeconfig" kubectl --context "$context" --namespace "$namespace" apply -f "$account_list_patch"

KUBECONFIG="$kubeconfig" kubectl --context "$context" --namespace "$namespace" get configmap regional-server-root-config -o json >"$root_config_live"

python3 - "$runtime_json" "$root_config_live" >"$root_config_patch" <<'PY'
import json
import pathlib
import re
import sys
from datetime import datetime

runtime = json.loads(sys.argv[1])
cm = json.loads(pathlib.Path(sys.argv[2]).read_text())
cm.pop("status", None)
metadata = cm.setdefault("metadata", {})
for key in ("creationTimestamp", "resourceVersion", "uid", "managedFields", "generation"):
    metadata.pop(key, None)

data = cm.setdefault("data", {})
root = data.get("root-config.yaml", "")
if not root:
    raise SystemExit("regional-server-root-config missing root-config.yaml")

def replace_line(text, pattern, replacement, label):
    next_text, count = re.subn(pattern, replacement, text, count=1, flags=re.MULTILINE)
    if count != 1:
        raise SystemExit(f"root-config.yaml missing {label}")
    return next_text

def replace_sequence(text, key_indent, key, item_indent, value, label):
    lines = text.splitlines()
    key_pattern = re.compile(r"^" + re.escape(key_indent) + re.escape(key) + r":(?:\s*\[\])?\s*$")
    key_index = -1
    for index, line in enumerate(lines):
        if key_pattern.match(line):
            key_index = index
            break
    if key_index < 0:
        raise SystemExit(f"root-config.yaml missing {label}")

    end_index = key_index + 1
    while end_index < len(lines):
        child = lines[end_index]
        if not child.strip():
            end_index += 1
            continue
        if re.match(r"^\s*-\s+", child):
            end_index += 1
            continue
        break

    replacement = [
        f"{key_indent}{key}:",
        f"{item_indent}- {json.dumps(value)}",
    ]
    return "\n".join(lines[:key_index] + replacement + lines[end_index:]) + "\n"

def unquote_simple_yaml_scalar(raw):
    raw = str(raw or "").strip()
    if len(raw) >= 2 and raw[0] in ("'", '"') and raw[-1] == raw[0]:
        return raw[1:-1]
    return raw

def replace_top_level_mapping(text, key, updates, required_keys, label):
    pattern = r"^" + re.escape(key) + r":\n((?:  [^\n]*:.*\n)*)"
    match = re.search(pattern, text, flags=re.MULTILINE)
    existing = {}
    order = []
    if match:
        body = match.group(1)
        for line in body.splitlines():
            item = re.match(r"^  ([A-Za-z0-9_.:/-]+):\s*(.*)$", line)
            if not item:
                raise SystemExit(f"{label} has unsupported entry {line!r}")
            name = item.group(1)
            if name not in existing:
                order.append(name)
            existing[name] = item.group(2).strip()
    rendered_values = dict(existing)
    for name, value in updates.items():
        rendered_values[name] = json.dumps(str(value))
        if name not in order:
            order.append(name)
    for name in required_keys:
        raw_value = rendered_values.get(name, "")
        value = unquote_simple_yaml_scalar(raw_value)
        if not value or value == "required":
            raise SystemExit(f"required dataplane cloud tag {name} has invalid value {value!r}")
    rendered = key + ":\n" + "".join(f"  {name}: {rendered_values[name]}\n" for name in order)
    if match:
        return text[:match.start()] + rendered + text[match.end():]
    next_text, count = re.subn(r"^(dnsProviderConfig:\n)", rendered + r"\1", text, count=1, flags=re.MULTILINE)
    if count != 1:
        raise SystemExit(f"root-config.yaml missing insertion point for {label}")
    return next_text

root = replace_top_level_mapping(
    root,
    "resourceLabels",
    {
        "tags.tidbcloud.com/cp9-managed": "true",
        "tags.tidbcloud.com/cp9-owner": runtime["cp9_owner_tag"],
        "tags.tidbcloud.com/cp9-session": runtime["cp9_session_tag"],
        "tags.tidbcloud.com/cp9-ttl": runtime["cp9_ttl_label"],
        "tags.tidbcloud.com/cp9-account": runtime["account"],
        "tags.tidbcloud.com/cp9-region": runtime["region"],
        "tags.tidbcloud.com/cp9-persona": "premium",
    },
    [
        "tags.tidbcloud.com/cp9-managed",
        "tags.tidbcloud.com/cp9-owner",
        "tags.tidbcloud.com/cp9-session",
        "tags.tidbcloud.com/cp9-ttl",
        "tags.tidbcloud.com/cp9-account",
        "tags.tidbcloud.com/cp9-region",
        "tags.tidbcloud.com/cp9-persona",
    ],
    "resourceLabels",
)
root = replace_top_level_mapping(
    root,
    "additionalCloudTags",
    {
        "environment": "cp9",
        "cp9-managed": "true",
        "cp9-owner": runtime["cp9_owner_tag"],
        "cp9-session": runtime["cp9_session_tag"],
        "cp9-ttl": runtime["cp9_ttl_label"],
        "cp9-account": runtime["account"],
        "cp9-region": runtime["region"],
    },
    [
        "environment",
        "cp9-managed",
        "cp9-owner",
        "cp9-session",
        "cp9-ttl",
        "cp9-account",
        "cp9-region",
        "cp9-persona",
    ],
    "additionalCloudTags",
)
root = replace_line(
    root,
    r'^    accountID:\s*.*$',
    f"    accountID: {json.dumps(runtime['account'])}",
    "dnsProviderConfig.awsRoute53.accountID",
)
root = replace_line(
    root,
    r'^    zoneId:\s*.*$',
    f"    zoneId: {runtime['public_dns_zone_id']}",
    "dnsProviderConfig.awsRoute53.zoneId",
)
root = replace_line(
    root,
    r'^    domain:\s*.*$',
    f"    domain: {json.dumps(runtime['public_dns_domain'])}",
    "dnsProviderConfig.awsRoute53.domain",
)
root = replace_line(
    root,
    r'^accountListPath:\s*.*$',
    'accountListPath: ""',
    "accountListPath",
)
root = replace_line(
    root,
    r'^dataPlaneRoleName:\s*.*$',
    f"dataPlaneRoleName: {runtime['dataplane_role_name']}",
    "dataPlaneRoleName",
)
root = replace_line(
    root,
    r'^resourceNamePrefix:\s*.*$',
    f"resourceNamePrefix: {runtime['session_name']}",
    "resourceNamePrefix",
)
root = replace_line(
    root,
    r'^  serviceRoleName:\s*.*$',
    f"  serviceRoleName: {runtime['eks_service_role_name']}",
    "appBoxConfig.serviceRoleName",
)
root = replace_line(
    root,
    r'^  enableK8sPublicAccess:\s*.*$',
    "  enableK8sPublicAccess: true",
    "appBoxConfig.enableK8sPublicAccess",
)
root = replace_sequence(
    root,
    "  ",
    "apiserverPublicAccessCidrs",
    "    ",
    runtime["eks_public_access_cidr"],
    "appBoxConfig.apiserverPublicAccessCidrs",
)
root = replace_line(
    root,
    r'^    enablePublicAccess:\s*.*$',
    "    enablePublicAccess: true",
    "appBoxConfig.apiGateway.enablePublicAccess",
)
root = replace_sequence(
    root,
    "    ",
    "publicAccessCidrs",
    "      ",
    runtime["eks_public_access_cidr"],
    "appBoxConfig.apiGateway.publicAccessCidrs",
)
if re.search(r'^    roleArn:\s*.*$', root, flags=re.MULTILINE):
    root = re.sub(
        r'^    roleArn:\s*.*$',
        f"    roleArn: {runtime['developer_role_arn']}",
        root,
        count=1,
        flags=re.MULTILINE,
    )
else:
    root = replace_line(
        root,
        r'^(    accountID:\s*.*)$',
        "\\1\n" + f"    roleArn: {runtime['developer_role_arn']}",
        "dnsProviderConfig.awsRoute53.accountID",
    )

data["root-config.yaml"] = root
json.dump(cm, sys.stdout)
PY

KUBECONFIG="$kubeconfig" kubectl --context "$context" --namespace "$namespace" apply -f "$root_config_patch"

KUBECONFIG="$kubeconfig" kubectl --context "$context" --namespace "$namespace" get configmap regional-server-root-config -o json >"$root_config_live"

dns_runtime_json=$(
  python3 - "$runtime_json" "$root_config_live" <<'PY'
import json
import pathlib
import re
import sys

runtime = json.loads(sys.argv[1])
cm = json.loads(pathlib.Path(sys.argv[2]).read_text())
root = (cm.get("data") or {}).get("root-config.yaml", "")
if not root:
    raise SystemExit("regional-server-root-config missing root-config.yaml")

def section(text, key, indent):
    lines = text.splitlines()
    prefix = " " * indent
    key_line = prefix + key + ":"
    for index, line in enumerate(lines):
        if line.rstrip() != key_line:
            continue
        body = []
        for child in lines[index + 1:]:
            if child and not child.startswith(prefix + " "):
                break
            body.append(child)
        return "\n".join(body)
    raise SystemExit(f"root-config.yaml missing {key}")

def scalar(text, key, indent):
    pattern = r"^" + (" " * indent) + re.escape(key) + r":\s*(.*?)\s*$"
    match = re.search(pattern, text, flags=re.MULTILINE)
    if not match:
        raise SystemExit(f"root-config.yaml missing dnsProviderConfig.awsRoute53.{key}")
    value = match.group(1).strip()
    if len(value) >= 2 and value[0] in ("'", '"') and value[-1] == value[0]:
        value = value[1:-1]
    return value

dns_provider = section(root, "dnsProviderConfig", 0)
aws_route53 = section(dns_provider, "awsRoute53", 2)
dns_account = scalar(aws_route53, "accountID", 4)
dns_zone_id = scalar(aws_route53, "zoneId", 4)
dns_domain = scalar(aws_route53, "domain", 4).rstrip(".")
dns_role_arn = scalar(aws_route53, "roleArn", 4)

if not re.fullmatch(r"[0-9]{12}", dns_account):
    raise SystemExit(f"dnsProviderConfig.awsRoute53.accountID must be a 12-digit account ID: {dns_account!r}")
if not re.fullmatch(r"Z[A-Z0-9]+", dns_zone_id):
    raise SystemExit(f"dnsProviderConfig.awsRoute53.zoneId must be a hosted zone ID: {dns_zone_id!r}")
if not re.fullmatch(r"[A-Za-z0-9.-]+", dns_domain):
    raise SystemExit(f"dnsProviderConfig.awsRoute53.domain must be a DNS domain: {dns_domain!r}")

if dns_account != runtime["account"]:
    raise SystemExit("dnsProviderConfig.awsRoute53.accountID must equal the CP9 runtime account")
if dns_zone_id != runtime["public_dns_zone_id"]:
    raise SystemExit("dnsProviderConfig.awsRoute53.zoneId must equal the CP9 account child zone")
if dns_domain != runtime["public_dns_domain"]:
    raise SystemExit("dnsProviderConfig.awsRoute53.domain must equal the CP9 session child domain")
if dns_role_arn != runtime["developer_role_arn"]:
    raise SystemExit("dnsProviderConfig.awsRoute53.roleArn must use the runtime account cp9-developer role")

print(json.dumps({
    "account": dns_account,
    "zone_id": dns_zone_id,
    "domain": dns_domain,
    "provider_config_name": "dns-" + dns_account,
    "role_arn": dns_role_arn,
}))
PY
)

dns_provider_config_name=$(
  python3 - "$dns_runtime_json" <<'PY'
import json
import sys

print(json.loads(sys.argv[1])["provider_config_name"])
PY
)

dns_provider_role_arn=$(
  python3 - "$dns_runtime_json" <<'PY'
import json
import sys

print(json.loads(sys.argv[1])["role_arn"])
PY
)

python3 - "$runtime_json" "$dns_runtime_json" >"$providerconfigs" <<'PY'
import json
import sys

runtime = json.loads(sys.argv[1])
dns_runtime = json.loads(sys.argv[2])

def provider_config(name, role_arn):
    source = "PodIdentity" if runtime["aws_runtime_identity"] == "ec2-instance-profile" else "Secret"
    secret_ref = "" if source == "PodIdentity" else """\n    secretRef:
      namespace: crossplane-system
      name: "aws-creds"
      key: creds"""
    return f"""apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: {json.dumps(name)}
spec:
  assumeRoleChain:
    - roleARN: {role_arn}
  credentials:
    source: {source}{secret_ref}
"""

sys.stdout.write(provider_config(runtime["account"], runtime["dataplane_role_arn"]))
sys.stdout.write("---\n")
sys.stdout.write(provider_config(dns_runtime["provider_config_name"], dns_runtime["role_arn"]))
PY

KUBECONFIG="$kubeconfig" kubectl --context "$context" apply -f "$providerconfigs"

python3 - "$runtime_json" >"$providerconfig_account_patch" <<'PY'
import json
import sys

runtime = json.loads(sys.argv[1])
source = "PodIdentity" if runtime["aws_runtime_identity"] == "ec2-instance-profile" else "Secret"
credentials = {"source": source}
if source == "Secret":
    credentials["secretRef"] = {
        "namespace": "crossplane-system",
        "name": "aws-creds",
        "key": "creds",
    }
print(json.dumps({
    "spec": {
        "assumeRoleChain": [{"roleARN": runtime["dataplane_role_arn"]}],
        "credentials": credentials,
    },
}))
PY

python3 - "$runtime_json" "$dns_runtime_json" >"$providerconfig_dns_patch" <<'PY'
import json
import sys

runtime = json.loads(sys.argv[1])
dns_runtime = json.loads(sys.argv[2])
source = "PodIdentity" if runtime["aws_runtime_identity"] == "ec2-instance-profile" else "Secret"
credentials = {"source": source}
if source == "Secret":
    credentials["secretRef"] = {
        "namespace": "crossplane-system",
        "name": "aws-creds",
        "key": "creds",
    }
print(json.dumps({
    "spec": {
        "assumeRoleChain": [{"roleARN": dns_runtime["role_arn"]}],
        "credentials": credentials,
    },
}))
PY

KUBECONFIG="$kubeconfig" kubectl --context "$context" patch providerconfig.aws.upbound.io "$target_account" --type=merge --patch-file "$providerconfig_account_patch"
KUBECONFIG="$kubeconfig" kubectl --context "$context" patch providerconfig.aws.upbound.io "$dns_provider_config_name" --type=merge --patch-file "$providerconfig_dns_patch"
verify_provider_config "$target_account" "$target_account_role_arn" "${CP9_AWS_RUNTIME_IDENTITY:-}"
verify_provider_config "$dns_provider_config_name" "$dns_provider_role_arn" "${CP9_AWS_RUNTIME_IDENTITY:-}"

if [ -s "$target_namespace_deferred_rendered" ]; then
  patch_image_pull_secret_before_apply
  patch_runtime_config_rollout_stamp
  KUBECONFIG="$kubeconfig" kubectl --context "$context" --namespace "$namespace" apply -f "$target_namespace_deferred_rendered"
fi

if [ -s "$cross_namespace_rendered" ]; then
  echo "dataplane-manager runtime rendered unexpected cross-namespace resources" >&2
  exit 1
fi

KUBECONFIG="$kubeconfig" kubectl --context "$context" --namespace "$namespace" rollout status deployment/dataplane-controller --timeout=180s
KUBECONFIG="$kubeconfig" kubectl --context "$context" --namespace "$namespace" rollout status deployment/dataplane-server --timeout=180s

print_live_yaml
