#!/usr/bin/env bash
# DNS Alert — Interactive deployment wizard
# Modes:
#   1) New deployment
#   2) Add/remove health check endpoints
#   3) Update enterprise SNS configuration
#   4) Enable/disable project Lark notifications

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
CDK_DIR="${PROJECT_ROOT}/cdk"
CONFIG_FILE="${PROJECT_ROOT}/config/health_checks.yaml"
CDK_JSON="${CDK_DIR}/cdk.json"
DEPLOYMENT_ID=""
DEPLOYMENT_ID_INITIALIZED="false"
STACK_NAME=""
DEFAULT_REGION="us-east-1"
REGION=""
AWS_PROFILE_INITIALIZED="false"
ACTIVE_AWS_PROFILE_LABEL=""
REGION_INITIALIZED="false"
LARK_WEBHOOK_SECRET_DEFAULT_NAME=""
CDK_CLI_PACKAGE="aws-cdk@2.1118.4"
CDK_TOOLKIT_STACK_BASE_NAME="CDKToolkitDnsAlert"
BOOTSTRAP_SUFFIX=""
CDK_BOOTSTRAP_QUALIFIER="dnsa1ert9"
CDK_TOOLKIT_STACK_NAME="${CDK_TOOLKIT_STACK_BASE_NAME}"
BOOTSTRAP_IDENTITY_INITIALIZED="false"
DEFAULT_DEPLOYMENT_ID_RAW="$(basename "${PROJECT_ROOT}")"
DEPLOY_PYTHON_VENV_DIR="${PROJECT_ROOT}/.deploy-venv"
export AWS_PAGER=""

RED=$'\033[0;31m'
GREEN=$'\033[0;32m'
YELLOW=$'\033[0;33m'
BLUE=$'\033[0;34m'
BOLD=$'\033[1m'
NC=$'\033[0m'

# ---------- Helpers ----------

print_banner() {
    echo ""
    echo "${BOLD}${BLUE}=== $1 ===${NC}"
}

print_warning() {
    echo "${YELLOW}⚠ $1${NC}"
}

print_error() {
    echo "${RED}✗ $1${NC}" >&2
}

print_success() {
    echo "${GREEN}✓ $1${NC}"
}

print_step() {
    echo "${BOLD}${BLUE}→ $1${NC}"
}

mask_value() {
    local value="$1"
    local prefix_length=18
    local suffix_length=8
    local total_length="${#value}"
    if (( total_length <= prefix_length + suffix_length )); then
        printf '%s' "${value}"
        return
    fi
    printf '%s***%s' "${value:0:prefix_length}" "${value:total_length-suffix_length}"
}

prompt_with_default() {
    local prompt="$1"
    local default="$2"
    local reply
    read -r -p "${prompt} [${default}]: " reply
    echo "${reply:-${default}}"
}

prompt_required() {
    local prompt="$1"
    local reply
    while true; do
        read -r -p "${prompt}: " reply
        if [[ -n "${reply}" ]]; then
            echo "${reply}"
            return
        fi
        print_error "This field is required."
    done
}

prompt_yes_no() {
    local prompt="$1"
    local default="${2:-n}"
    local hint="[y/N]"
    [[ "${default}" == "y" ]] && hint="[Y/n]"
    local reply
    read -r -p "${prompt} ${hint}: " reply
    reply="${reply:-${default}}"
    [[ "${reply}" =~ ^[Yy]$ ]]
}

prompt_exact_match() {
    local prompt="$1"
    local expected="$2"
    local reply
    read -r -p "${prompt}: " reply
    [[ "${reply}" == "${expected}" ]]
}

validate_health_check_entry() {
    local name="$1"
    local health_type="$2"
    local domain="$3"
    local port="$4"
    local path="$5"
    local interval="$6"
    local threshold="$7"

    [[ -n "${name}" ]] || { print_error "Endpoint name is required."; return 1; }
    [[ -n "${domain}" ]] || { print_error "Domain is required."; return 1; }

    if [[ "${health_type}" != "HTTP" && "${health_type}" != "HTTPS" && "${health_type}" != "TCP" ]]; then
        print_error "Type must be HTTP, HTTPS, or TCP."
        return 1
    fi

    if ! [[ "${port}" =~ ^[0-9]+$ ]] || (( port < 1 || port > 65535 )); then
        print_error "Port must be an integer between 1 and 65535."
        return 1
    fi

    if [[ "${interval}" != "10" && "${interval}" != "30" ]]; then
        print_error "Request interval must be 10 or 30 seconds."
        return 1
    fi

    if ! [[ "${threshold}" =~ ^[0-9]+$ ]] || (( threshold < 1 || threshold > 10 )); then
        print_error "Failure threshold must be an integer between 1 and 10."
        return 1
    fi

    if [[ "${health_type}" != "TCP" && -n "${path}" && "${path}" != /* ]]; then
        print_error "Path must start with '/'."
        return 1
    fi

    return 0
}

sanitize_deployment_id() {
    local raw="${1:-}"
    local normalized
    normalized="$(printf '%s' "${raw}" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//; s/-+/-/g')"
    normalized="${normalized:0:24}"
    normalized="${normalized%-}"
    normalized="${normalized#-}"
    if [[ -z "${normalized}" ]]; then
        normalized="dns-alert"
    fi
    printf '%s' "${normalized}"
}

stack_name_for_deployment() {
    printf 'DnsAlertStack-%s' "$1"
}

default_project_lark_secret_name() {
    printf 'dns-alert/%s/project-lark-webhook' "$1"
}

set_deployment_identity_vars() {
    DEPLOYMENT_ID="$1"
    STACK_NAME="$(stack_name_for_deployment "${DEPLOYMENT_ID}")"
    LARK_WEBHOOK_SECRET_DEFAULT_NAME="$(default_project_lark_secret_name "${DEPLOYMENT_ID}")"
}

check_command() {
    local cmd="$1"
    local hint="$2"
    if ! command -v "${cmd}" >/dev/null 2>&1; then
        print_error "Required command '${cmd}' not found. ${hint}"
        exit 1
    fi
}

activate_deploy_python_venv() {
    if [[ -x "${DEPLOY_PYTHON_VENV_DIR}/bin/python3" ]]; then
        export VIRTUAL_ENV="${DEPLOY_PYTHON_VENV_DIR}"
        case ":${PATH}:" in
            *":${DEPLOY_PYTHON_VENV_DIR}/bin:"*) ;;
            *)
                export PATH="${DEPLOY_PYTHON_VENV_DIR}/bin:${PATH}"
                hash -r
                ;;
        esac
    fi
}

check_python_pip() {
    activate_deploy_python_venv
    if ! python3 -m pip --version >/dev/null 2>&1; then
        print_error "Python package installer is unavailable for python3. Install pip for the active python3 interpreter."
        echo "  Try: python3 -m ensurepip --upgrade" >&2
        echo "  Or reinstall Python with pip support enabled." >&2
        exit 1
    fi
}

check_cdk_cli() {
    if ! npx --yes "${CDK_CLI_PACKAGE}" --version >/dev/null 2>&1; then
        print_error "AWS CDK CLI package '${CDK_CLI_PACKAGE}' is not available via npx. Ensure your Node.js/npm toolchain and network access are configured."
        exit 1
    fi
}

run_cdk() {
    npx --yes "${CDK_CLI_PACKAGE}" "$@"
}

ensure_deploy_python_deps() {
    activate_deploy_python_venv
    if python3 -c "import yaml" >/dev/null 2>&1; then
        return
    fi

    print_step "Installing missing deploy dependency: PyYAML"
    if ! python3 -m pip install --quiet --user "PyYAML>=6.0" >/dev/null 2>&1; then
        print_warning "System python is not accepting --user installs. Falling back to a repo-local virtualenv."
        if [[ ! -x "${DEPLOY_PYTHON_VENV_DIR}/bin/python3" ]]; then
            print_step "Creating local deploy virtualenv at ${DEPLOY_PYTHON_VENV_DIR}"
            if ! python3 -m venv "${DEPLOY_PYTHON_VENV_DIR}"; then
                print_error "Failed to create the local deploy virtualenv."
                echo "  Try: python3 -m venv '${DEPLOY_PYTHON_VENV_DIR}'" >&2
                exit 1
            fi
        fi

        activate_deploy_python_venv
        if ! python3 -m pip install --quiet "PyYAML>=6.0"; then
            print_error "Failed to install PyYAML into the local deploy virtualenv."
            echo "  Try: ${DEPLOY_PYTHON_VENV_DIR}/bin/python3 -m pip install 'PyYAML>=6.0'" >&2
            exit 1
        fi
    fi

    if ! python3 -c "import yaml" >/dev/null 2>&1; then
        print_error "PyYAML installation completed but the 'yaml' module is still unavailable to python3."
        exit 1
    fi

    print_success "Deploy Python dependency available: PyYAML"
}

profile_label() {
    local profile="${1:-}"
    if [[ -n "${profile}" ]]; then
        printf '%s' "${profile}"
    else
        printf 'default'
    fi
}

activate_aws_profile() {
    local profile="${1:-}"
    if [[ -n "${profile}" ]]; then
        export AWS_PROFILE="${profile}"
    else
        unset AWS_PROFILE || true
    fi
    ACTIVE_AWS_PROFILE_LABEL="$(profile_label "${profile}")"
    AWS_PROFILE_INITIALIZED="true"
}

configure_aws_profile() {
    [[ "${AWS_PROFILE_INITIALIZED}" == "true" ]] && return

    print_banner "AWS Profile"
    if [[ -n "${AWS_PROFILE:-}" ]]; then
        echo "  Current AWS_PROFILE: ${AWS_PROFILE}"
        if prompt_yes_no "Use this profile?" "y"; then
            AWS_PROFILE_INITIALIZED="true"
            return
        fi
    fi

    local profile
    read -r -p "AWS CLI profile name (press Enter for default profile): " profile
    if [[ -n "${profile}" ]]; then
        activate_aws_profile "${profile}"
        print_success "Using AWS profile: ${AWS_PROFILE}"
    else
        activate_aws_profile ""
        print_success "Using AWS default profile"
    fi
}

load_region_context() {
    local saved_region
    saved_region="$(read_context deploy_region)"
    if [[ -n "${saved_region}" ]]; then
        printf '%s' "${saved_region}"
    elif [[ -n "${AWS_REGION:-}" ]]; then
        printf '%s' "${AWS_REGION}"
    elif [[ -n "${AWS_DEFAULT_REGION:-}" ]]; then
        printf '%s' "${AWS_DEFAULT_REGION}"
    else
        printf '%s' "${DEFAULT_REGION}"
    fi
}

configure_region() {
    [[ "${REGION_INITIALIZED}" == "true" ]] && return

    print_banner "AWS Region"
    local suggested_region
    suggested_region="$(load_region_context)"

    if [[ -n "${suggested_region}" && "${suggested_region}" != "${DEFAULT_REGION}" ]]; then
        print_warning "Route53 health check metrics only publish to ${DEFAULT_REGION}; overriding saved region ${suggested_region}."
    fi

    REGION="${DEFAULT_REGION}"
    export AWS_REGION="${REGION}"
    export AWS_DEFAULT_REGION="${REGION}"
    REGION_INITIALIZED="true"
    print_success "Using AWS region: ${REGION}"
}

load_deployment_context() {
    local saved_deployment_id
    saved_deployment_id="$(read_context deployment_id)"
    if [[ -n "${saved_deployment_id}" ]]; then
        sanitize_deployment_id "${saved_deployment_id}"
    else
        sanitize_deployment_id "${DEFAULT_DEPLOYMENT_ID_RAW}"
    fi
}

configure_deployment_identity() {
    [[ "${DEPLOYMENT_ID_INITIALIZED}" == "true" ]] && return

    print_banner "Deployment Identity"
    local deployment_id
    deployment_id="$(load_deployment_context)"
    set_deployment_identity_vars "${deployment_id}"
    DEPLOYMENT_ID_INITIALIZED="true"
    print_success "Using deployment identifier: ${DEPLOYMENT_ID}"
    echo "  Stack name: ${STACK_NAME}"
    echo "  Managed secret default: ${LARK_WEBHOOK_SECRET_DEFAULT_NAME}"
}

derive_bootstrap_qualifier() {
    local suffix="${1:-}"
    BOOTSTRAP_SUFFIX_INPUT="${suffix}" python3 - <<'PY'
import hashlib
import os

suffix = os.environ.get("BOOTSTRAP_SUFFIX_INPUT", "").strip().lower()
if not suffix:
    print("dnsa1ert9")
else:
    digest = hashlib.sha1(f"dns-alert:{suffix}".encode("utf-8")).hexdigest()[:7]
    print(f"dna{digest}")
PY
}

refresh_bootstrap_identity() {
    CDK_TOOLKIT_STACK_NAME="${CDK_TOOLKIT_STACK_BASE_NAME}"
    if [[ -n "${BOOTSTRAP_SUFFIX}" ]]; then
        CDK_TOOLKIT_STACK_NAME="${CDK_TOOLKIT_STACK_NAME}-${BOOTSTRAP_SUFFIX}"
    fi
    CDK_BOOTSTRAP_QUALIFIER="$(derive_bootstrap_qualifier "${BOOTSTRAP_SUFFIX}")"
}

configure_bootstrap_identity() {
    [[ "${BOOTSTRAP_IDENTITY_INITIALIZED}" == "true" ]] && return

    print_banner "CDK Bootstrap"
    local suggested_suffix
    suggested_suffix="$(read_context bootstrap_suffix)"

    local suffix
    if [[ -n "${suggested_suffix}" ]]; then
        read -r -p "Bootstrap suffix for dedicated toolkit stack [${suggested_suffix}]: " suffix
        suffix="${suffix:-${suggested_suffix}}"
    else
        read -r -p "Bootstrap suffix for dedicated toolkit stack (optional, e.g. prod): " suffix
    fi

    if [[ -n "${suffix}" ]]; then
        if [[ ! "${suffix}" =~ ^[A-Za-z0-9-]+$ ]]; then
            print_error "Invalid bootstrap suffix '${suffix}'. Use only letters, numbers, and hyphens."
            exit 1
        fi
        if (( ${#CDK_TOOLKIT_STACK_BASE_NAME} + 1 + ${#suffix} > 128 )); then
            print_error "Bootstrap suffix is too long for CloudFormation stack name '${CDK_TOOLKIT_STACK_BASE_NAME}-${suffix}'."
            exit 1
        fi
    fi

    BOOTSTRAP_SUFFIX="${suffix}"
    refresh_bootstrap_identity
    BOOTSTRAP_IDENTITY_INITIALIZED="true"
    print_success "Using CDK toolkit stack: ${CDK_TOOLKIT_STACK_NAME}"
    print_success "Using CDK bootstrap qualifier: ${CDK_BOOTSTRAP_QUALIFIER}"
}

persist_runtime_context() {
    write_context "deployment_id" "${DEPLOYMENT_ID}"
    write_context "deploy_region" "${REGION}"
    write_context "bootstrap_suffix" "${BOOTSTRAP_SUFFIX}"
}

# Validates AWS SNS ARN; sets globals ARN_REGION, ARN_ACCOUNT, ARN_TOPIC on success.
validate_sns_arn() {
    local arn="$1"
    if [[ "${arn}" =~ ^arn:aws:sns:([a-z0-9-]+):([0-9]{12}):([a-zA-Z0-9_-]+)$ ]]; then
        ARN_REGION="${BASH_REMATCH[1]}"
        ARN_ACCOUNT="${BASH_REMATCH[2]}"
        ARN_TOPIC="${BASH_REMATCH[3]}"
        return 0
    fi
    return 1
}

# Read a string value from cdk.json context using python (portable).
read_context() {
    local key="$1"
    python3 -c "import json,sys; print(json.load(open('${CDK_JSON}'))['context'].get('${key}',''))"
}

# Write a string value into cdk.json context.
write_context() {
    local key="$1"
    local value="$2"
    python3 - <<PY
import json
with open("${CDK_JSON}") as f:
    data = json.load(f)
data.setdefault("context", {})["${key}"] = """${value}"""
with open("${CDK_JSON}", "w") as f:
    json.dump(data, f, indent=2)
    f.write("\n")
PY
}

notification_mode_has_enterprise() {
    local mode="${1:-}"
    [[ "${mode}" == "enterprise" || "${mode}" == "dual" ]]
}

notification_mode_has_project_lark() {
    local mode="${1:-}"
    [[ "${mode}" == "project_lark" || "${mode}" == "dual" ]]
}

notification_mode_has_enterprise_https() {
    local mode="${1:-}"
    [[ "${mode}" == "enterprise_https" ]]
}

infer_notification_mode_from_legacy() {
    local enterprise_arn legacy_enable_lark
    enterprise_arn="$(read_context enterprise_sns_topic_arn)"
    legacy_enable_lark="$(read_context enable_test_lark)"

    if [[ -n "${enterprise_arn}" && "${legacy_enable_lark}" == "true" ]]; then
        printf '%s' "dual"
    elif [[ -n "${enterprise_arn}" ]]; then
        printf '%s' "enterprise"
    elif [[ "${legacy_enable_lark}" == "true" ]]; then
        printf '%s' "project_lark"
    else
        printf '%s' ""
    fi
}

load_notification_context() {
    NOTIFICATION_MODE="$(read_context notification_mode)"
    ENTERPRISE_ARN="$(read_context enterprise_sns_topic_arn)"
    ENTERPRISE_HTTPS_ENDPOINT_URL="$(read_context enterprise_https_endpoint_url)"
    PROJECT_LARK_SECRET_ARN="$(read_context project_lark_secret_arn)"
    PROJECT_LARK_SECRET_MANAGED="$(read_context project_lark_secret_managed)"

    if [[ "${NOTIFICATION_MODE}" == "enterprise_webhook" ]]; then
        NOTIFICATION_MODE="enterprise_https"
    fi

    if [[ -z "${NOTIFICATION_MODE}" ]]; then
        NOTIFICATION_MODE="$(infer_notification_mode_from_legacy)"
    fi
}

persist_notification_context() {
    local enable_project_lark="false"
    notification_mode_has_project_lark "${NOTIFICATION_MODE}" && enable_project_lark="true"

    write_context "deploy_region" "${REGION}"
    write_context "bootstrap_suffix" "${BOOTSTRAP_SUFFIX}"
    write_context "notification_mode" "${NOTIFICATION_MODE}"
    write_context "enterprise_sns_topic_arn" "${ENTERPRISE_ARN:-}"
    write_context "enterprise_https_endpoint_url" "${ENTERPRISE_HTTPS_ENDPOINT_URL:-}"
    write_context "project_lark_secret_arn" "${PROJECT_LARK_SECRET_ARN:-}"
    write_context "project_lark_secret_managed" "${PROJECT_LARK_SECRET_MANAGED:-false}"
    write_context "enable_test_lark" "${enable_project_lark}"
    write_context "lark_webhook_url" ""
}

ensure_project_lark_secret() {
    local webhook_url="$1"
    local secret_id secret_arn

    secret_id="${PROJECT_LARK_SECRET_ARN:-${LARK_WEBHOOK_SECRET_DEFAULT_NAME}}"

    if aws secretsmanager describe-secret --secret-id "${secret_id}" --region "${REGION}" >/dev/null 2>&1; then
        secret_arn="$(aws secretsmanager describe-secret \
            --secret-id "${secret_id}" \
            --region "${REGION}" \
            --query 'ARN' \
            --output text)"
        print_step "Updating existing Secrets Manager secret ${secret_arn}"
        aws secretsmanager put-secret-value \
            --secret-id "${secret_id}" \
            --secret-string "${webhook_url}" \
            --region "${REGION}" >/dev/null
    else
        print_step "Creating Secrets Manager secret '${LARK_WEBHOOK_SECRET_DEFAULT_NAME}'"
        secret_arn="$(aws secretsmanager create-secret \
            --name "${LARK_WEBHOOK_SECRET_DEFAULT_NAME}" \
            --description "dns-alert project-managed Lark webhook URL" \
            --secret-string "${webhook_url}" \
            --region "${REGION}" \
            --query 'ARN' \
            --output text)"
    fi

    PROJECT_LARK_SECRET_ARN="${secret_arn}"
    PROJECT_LARK_SECRET_MANAGED="true"
    print_success "Using Lark webhook secret: ${PROJECT_LARK_SECRET_ARN}"
}

update_current_project_lark_secret_value() {
    local webhook_url="$1"

    if [[ -z "${PROJECT_LARK_SECRET_ARN:-}" ]]; then
        print_error "No current project Lark secret ARN is configured."
        exit 1
    fi

    print_banner "Project Lark Webhook Secret"
    print_step "Updating secret value for ${PROJECT_LARK_SECRET_ARN}"
    aws secretsmanager put-secret-value \
        --secret-id "${PROJECT_LARK_SECRET_ARN}" \
        --secret-string "${webhook_url}" \
        --region "${REGION}" >/dev/null
    print_success "Updated Lark webhook secret value"
}

use_existing_project_lark_secret() {
    local secret_id secret_arn

    while true; do
        secret_id="$(prompt_required "Existing Secrets Manager secret ARN or name")"
        if ! secret_arn="$(aws secretsmanager describe-secret \
                --secret-id "${secret_id}" \
                --region "${REGION}" \
                --query 'ARN' \
                --output text 2>/dev/null)"; then
            print_error "Could not find that secret in ${REGION} or you do not have access."
            continue
        fi
        break
    done

    PROJECT_LARK_SECRET_ARN="${secret_arn}"
    PROJECT_LARK_SECRET_MANAGED="false"
    print_success "Using existing Lark webhook secret: ${PROJECT_LARK_SECRET_ARN}"
}

configure_enterprise_https_endpoint() {
    local endpoint_url

    print_banner "Enterprise SNS HTTPS Endpoint"
    echo "Provide the enterprise-managed SNS HTTPS subscription endpoint."
    echo "Leave this blank to create the project SNS relay topic now and attach the enterprise endpoint later."

    while true; do
        read -r -p "Enterprise SNS HTTPS endpoint URL (optional): " endpoint_url
        if [[ -z "${endpoint_url}" ]]; then
            ENTERPRISE_HTTPS_ENDPOINT_URL=""
            print_warning "No HTTPS endpoint provided. The project SNS relay topic will be created without a downstream subscription."
            return
        fi

        if [[ "${endpoint_url}" == https://* ]]; then
            ENTERPRISE_HTTPS_ENDPOINT_URL="${endpoint_url}"
            print_success "Using enterprise SNS HTTPS endpoint: ${ENTERPRISE_HTTPS_ENDPOINT_URL}"
            return
        fi

        print_error "HTTPS endpoint must start with https://"
    done
}

configure_project_lark_secret() {
    local choice

    print_banner "Project Lark Webhook Secret"
    echo "Choose how to provide the webhook secret:"
    echo "  1) Let this script create/update a dedicated Secrets Manager secret"
    echo "  2) Reuse an existing Secrets Manager secret ARN/name (avoids creating another secret)"
    read -r -p "Choose [1/2] (default: 1): " choice
    choice="${choice:-1}"

    case "${choice}" in
        1)
            webhook_url="$(prompt_required "Project Lark webhook URL")"
            ensure_project_lark_secret "${webhook_url}"
            ;;
        2)
            use_existing_project_lark_secret
            ;;
        *)
            print_error "Invalid choice"
            exit 1
            ;;
    esac
}

# Reads health_checks.yaml names as a bash array into global HC_NAMES.
load_hc_names() {
    HC_NAMES=()
    [[ -f "${CONFIG_FILE}" ]] || return 0
    while IFS= read -r line; do
        HC_NAMES+=("${line}")
    done < <(python3 -c "
import yaml
with open('${CONFIG_FILE}') as f:
    d = yaml.safe_load(f) or {}
for hc in d.get('health_checks') or []:
    print(hc['name'])
")
}

health_check_tag_name_for_deployment() {
    local deployment_id="$1"
    local health_check_name="$2"
    printf 'r53-%s-%s' "$(sanitize_deployment_id "${deployment_id}")" "${health_check_name}"
}

load_stack_health_check_ids_for_stack() {
    local stack_name="$1"
    STACK_HEALTH_CHECK_IDS=()

    local ids_output
    ids_output="$(aws cloudformation list-stack-resources \
        --stack-name "${stack_name}" \
        --region "${REGION}" \
        --query 'StackResourceSummaries[?ResourceType==`AWS::Route53::HealthCheck`].PhysicalResourceId' \
        --output text 2>/dev/null || true)"

    [[ -n "${ids_output}" && "${ids_output}" != "None" ]] || return 0

    while IFS= read -r line; do
        [[ -n "${line}" ]] && STACK_HEALTH_CHECK_IDS+=("${line}")
    done < <(printf '%s\n' "${ids_output}" | tr '\t' '\n')
}

cloudformation_stack_exists_for_name() {
    local stack_name="$1"
    aws cloudformation describe-stacks --stack-name "${stack_name}" --region "${REGION}" >/dev/null 2>&1
}

find_orphaned_route53_health_checks() {
    local deployment_id="$1"
    local stack_name="$2"
    ORPHANED_ROUTE53_HEALTH_CHECKS=()

    load_stack_health_check_ids_for_stack "${stack_name}"

    local ids_output
    ids_output="$(aws route53 list-health-checks --query 'HealthChecks[].Id' --output text 2>/dev/null || true)"
    [[ -n "${ids_output}" && "${ids_output}" != "None" ]] || return 0

    local target_prefix
    target_prefix="r53-$(sanitize_deployment_id "${deployment_id}")-"

    local id
    for id in ${ids_output}; do
        local tags_output project_tag name_tag
        tags_output="$(aws route53 list-tags-for-resource \
            --resource-type healthcheck \
            --resource-id "${id}" \
            --query 'ResourceTagSet.Tags[].[Key,Value]' \
            --output text 2>/dev/null || true)"

        project_tag=""
        name_tag=""
        while IFS=$'\t' read -r key value; do
            [[ -z "${key}" ]] && continue
            [[ "${key}" == "Project" ]] && project_tag="${value}"
            [[ "${key}" == "Name" ]] && name_tag="${value}"
        done <<< "${tags_output}"

        [[ "${project_tag}" == "dns-alert" ]] || continue
        [[ -n "${name_tag}" && "${name_tag}" == "${target_prefix}"* ]] || continue
        if printf '%s\n' "${STACK_HEALTH_CHECK_IDS[@]:-}" | grep -Fxq "${id}"; then
            continue
        fi

        ORPHANED_ROUTE53_HEALTH_CHECKS+=("${id}|${name_tag}")
    done
}

cleanup_orphaned_route53_health_checks() {
    check_aws_auth

    print_banner "Cleanup Orphaned Route53 Health Checks"
    echo "This mode deletes Route53 health checks tagged for a dns-alert deployment that are"
    echo "no longer managed by that deployment's current CloudFormation stack."

    local cleanup_deployment_id cleanup_stack_name
    cleanup_deployment_id="$(prompt_with_default "Deployment identifier to clean" "${DEPLOYMENT_ID}")"
    cleanup_deployment_id="$(sanitize_deployment_id "${cleanup_deployment_id}")"
    cleanup_stack_name="$(stack_name_for_deployment "${cleanup_deployment_id}")"

    if ! cloudformation_stack_exists_for_name "${cleanup_stack_name}"; then
        print_error "CloudFormation stack ${cleanup_stack_name} was not found in ${REGION}."
        echo "  Cleanup stops here to avoid deleting Route53 health checks by tag-only matching."
        echo "  Select the correct deployment/region, or remove the health checks manually after review."
        return
    fi

    print_step "Scanning Route53 health checks for deployment '${cleanup_deployment_id}'"
    find_orphaned_route53_health_checks "${cleanup_deployment_id}" "${cleanup_stack_name}"

    if [[ "${#ORPHANED_ROUTE53_HEALTH_CHECKS[@]}" -eq 0 ]]; then
        print_success "No orphaned Route53 health checks found for deployment '${cleanup_deployment_id}'."
        return
    fi

    echo ""
    printf "  %-36s %s\n" "HEALTH CHECK ID" "TAGGED NAME"
    local orphan
    for orphan in "${ORPHANED_ROUTE53_HEALTH_CHECKS[@]}"; do
        IFS='|' read -r health_check_id tagged_name <<< "${orphan}"
        printf "  %-36s %s\n" "${health_check_id}" "${tagged_name}"
    done

    echo ""
    print_warning "These health checks are not in CloudFormation stack ${cleanup_stack_name} and will continue probing/costing until removed."
    prompt_yes_no "Delete these orphaned Route53 health checks?" "n" || return
    prompt_exact_match \
        "Type 'cleanup-${cleanup_deployment_id}' to confirm Route53 health check deletion" \
        "cleanup-${cleanup_deployment_id}" || {
            print_error "Confirmation text did not match. Aborted."
            return
        }

    for orphan in "${ORPHANED_ROUTE53_HEALTH_CHECKS[@]}"; do
        IFS='|' read -r health_check_id tagged_name <<< "${orphan}"
        print_step "Deleting Route53 health check ${health_check_id} (${tagged_name})"
        aws route53 delete-health-check --health-check-id "${health_check_id}" >/dev/null
    done

    print_success "Deleted ${#ORPHANED_ROUTE53_HEALTH_CHECKS[@]} orphaned Route53 health checks for deployment '${cleanup_deployment_id}'."
}

# ---------- Account / CDK prep ----------

check_aws_auth() {
    configure_aws_profile
    configure_region
    configure_deployment_identity
    configure_bootstrap_identity
    persist_runtime_context
    print_banner "Stage 1: Prerequisites"
    check_command aws "Install AWS CLI v2: https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html"
    check_command python3 "Install Python 3.9+."
    check_python_pip
    check_command npx "Install Node.js (for aws-cdk): https://nodejs.org/"
    check_cdk_cli
    ensure_deploy_python_deps

    if ! aws sts get-caller-identity >/dev/null 2>&1; then
        print_error "AWS credentials not configured or expired."
        echo "  Run: aws configure   # or: aws sso login --profile <profile>"
        exit 1
    fi

    print_step "Resolving AWS caller identity"
    CURRENT_ACCOUNT="$(aws sts get-caller-identity --query Account --output text)"
    CURRENT_ARN="$(aws sts get-caller-identity --query Arn --output text)"
    print_success "AWS authenticated: ${CURRENT_ARN}"
    echo "  Profile:    ${ACTIVE_AWS_PROFILE_LABEL:-$(profile_label "${AWS_PROFILE:-}")}"
    echo "  Account ID: ${CURRENT_ACCOUNT}"
    echo "  Region:     ${REGION}"
    echo "  Stack:      ${STACK_NAME}"
}

collect_e2e_profiles() {
    local current_default
    current_default="$(profile_label "${AWS_PROFILE:-}")"
    E2E_PROFILES=()

    print_banner "AWS Account Scope"
    if ! prompt_yes_no "Run this E2E workflow across multiple AWS profiles?" "n"; then
        local single_profile
        single_profile="$(prompt_with_default "AWS CLI profile name for this run" "${current_default}")"
        [[ "${single_profile}" == "default" ]] && single_profile=""
        E2E_PROFILES+=("${single_profile}")
        return
    fi

    while true; do
        local prompt_text="AWS CLI profile name"
        [[ "${#E2E_PROFILES[@]}" -eq 0 ]] && prompt_text="First AWS CLI profile name"
        local profile
        profile="$(prompt_with_default "${prompt_text}" "${current_default}")"
        [[ "${profile}" == "default" ]] && profile=""

        local duplicate="false"
        local existing
        for existing in "${E2E_PROFILES[@]:-}"; do
            if [[ "${existing}" == "${profile}" ]]; then
                duplicate="true"
                break
            fi
        done
        if [[ "${duplicate}" == "true" ]]; then
            print_warning "Profile '$(profile_label "${profile}")' is already in the run list."
        else
            E2E_PROFILES+=("${profile}")
            print_success "Queued profile: $(profile_label "${profile}")"
        fi

        if ! prompt_yes_no "Add another AWS profile?" "n"; then
            break
        fi
    done
}

install_cdk_deps() {
    print_banner "Installing CDK Python dependencies"
    activate_deploy_python_venv
    local pip_install_args=(-q -r requirements.txt)
    local pip_install_label="python3 -m pip install -r cdk/requirements.txt"
    if [[ -z "${VIRTUAL_ENV:-}" ]]; then
        pip_install_args=(--user "${pip_install_args[@]}")
        pip_install_label="python3 -m pip install --user -r cdk/requirements.txt"
    fi
    print_step "Running ${pip_install_label}"
    (cd "${CDK_DIR}" && python3 -m pip install "${pip_install_args[@]}")
    print_success "CDK dependencies installed"
}

cdk_bootstrap_if_needed() {
    local region="${REGION}"
    if ! aws cloudformation describe-stacks --stack-name "${CDK_TOOLKIT_STACK_NAME}" --region "${region}" >/dev/null 2>&1; then
        print_banner "Bootstrapping CDK in ${region}"
        print_step "Running cdk bootstrap for account ${CURRENT_ACCOUNT} with qualifier ${CDK_BOOTSTRAP_QUALIFIER}"
        (
            cd "${CDK_DIR}" && \
            run_cdk bootstrap \
                --qualifier "${CDK_BOOTSTRAP_QUALIFIER}" \
                --toolkit-stack-name "${CDK_TOOLKIT_STACK_NAME}" \
                "aws://${CURRENT_ACCOUNT}/${region}"
        )
    else
        print_success "CDK bootstrap stack ${CDK_TOOLKIT_STACK_NAME} already exists in ${region}"
    fi
}

cdk_deploy() {
    print_banner "Running cdk deploy"
    print_step "Deploying stack from ${CDK_DIR}"
    (cd "${CDK_DIR}" && run_cdk deploy --require-approval never --toolkit-stack-name "${CDK_TOOLKIT_STACK_NAME}")
}

cdk_deploy_with_context() {
    local notification_mode="$1"
    local enterprise_arn="$2"
    local project_lark_secret_arn="$3"
    local enterprise_https_endpoint_url="$4"

    print_banner "Running cdk deploy"
    print_step "Deploying ${STACK_NAME} with explicit E2E context"
    (
        cd "${CDK_DIR}" && \
        run_cdk deploy --require-approval never --toolkit-stack-name "${CDK_TOOLKIT_STACK_NAME}" \
            --context "notification_mode=${notification_mode}" \
            --context "enterprise_sns_topic_arn=${enterprise_arn}" \
            --context "enterprise_https_endpoint_url=${enterprise_https_endpoint_url}" \
            --context "project_lark_secret_arn=${project_lark_secret_arn}"
    )
}

stack_exists() {
    aws cloudformation describe-stacks --stack-name "${STACK_NAME}" --region "${REGION}" >/dev/null 2>&1
}

stack_output() {
    local key="$1"
    aws cloudformation describe-stacks \
        --stack-name "${STACK_NAME}" \
        --region "${REGION}" \
        --query "Stacks[0].Outputs[?OutputKey=='${key}'].OutputValue | [0]" \
        --output text 2>/dev/null || true
}

stack_status() {
    aws cloudformation describe-stacks \
        --stack-name "${STACK_NAME}" \
        --region "${REGION}" \
        --query 'Stacks[0].StackStatus' \
        --output text 2>/dev/null || true
}

print_stack_destroy_preview() {
    local stack_id creation_time update_time status
    stack_id="$(aws cloudformation describe-stacks \
        --stack-name "${STACK_NAME}" \
        --region "${REGION}" \
        --query 'Stacks[0].StackId' \
        --output text 2>/dev/null || true)"
    creation_time="$(aws cloudformation describe-stacks \
        --stack-name "${STACK_NAME}" \
        --region "${REGION}" \
        --query 'Stacks[0].CreationTime' \
        --output text 2>/dev/null || true)"
    update_time="$(aws cloudformation describe-stacks \
        --stack-name "${STACK_NAME}" \
        --region "${REGION}" \
        --query 'Stacks[0].LastUpdatedTime' \
        --output text 2>/dev/null || true)"
    status="$(stack_status)"

    echo "  Account ID:   ${CURRENT_ACCOUNT}"
    echo "  Region:       ${REGION}"
    echo "  Stack name:   ${STACK_NAME}"
    [[ -n "${stack_id}" && "${stack_id}" != "None" ]] && echo "  Stack ID:     ${stack_id}"
    [[ -n "${status}" && "${status}" != "None" ]] && echo "  Stack status: ${status}"
    [[ -n "${creation_time}" && "${creation_time}" != "None" ]] && echo "  Created at:   ${creation_time}"
    [[ -n "${update_time}" && "${update_time}" != "None" ]] && echo "  Updated at:   ${update_time}"

    echo ""
    echo "  CloudFormation-managed resources scheduled for deletion:"
    aws cloudformation list-stack-resources \
        --no-cli-pager \
        --stack-name "${STACK_NAME}" \
        --region "${REGION}" \
        --query 'StackResourceSummaries[].{LogicalId:LogicalResourceId,Type:ResourceType,PhysicalId:PhysicalResourceId,Status:ResourceStatus}' \
        --output table 2>/dev/null || print_warning "Could not list stack resources."
}


print_recent_stack_events() {
    aws cloudformation describe-stack-events \
        --no-cli-pager \
        --stack-name "${STACK_NAME}" \
        --region "${REGION}" \
        --query 'StackEvents[0:10].[Timestamp,LogicalResourceId,ResourceType,ResourceStatus,ResourceStatusReason]' \
        --output table 2>/dev/null || print_warning "Could not retrieve recent CloudFormation events."
}

wait_for_stack_deletion() {
    local last_status=""
    print_step "Waiting for CloudFormation to delete ${STACK_NAME}"
    while true; do
        local stack_status
        if ! stack_status="$(aws cloudformation describe-stacks \
                --no-cli-pager \
                --stack-name "${STACK_NAME}" \
                --region "${REGION}" \
                --query 'Stacks[0].StackStatus' \
                --output text 2>/dev/null)"; then
            print_success "${STACK_NAME} no longer exists in ${REGION}"
            return 0
        fi

        if [[ "${stack_status}" != "${last_status}" ]]; then
            print_step "Current stack status: ${stack_status}"
            last_status="${stack_status}"
        fi

        if [[ "${stack_status}" == "DELETE_FAILED" ]]; then
            print_error "CloudFormation reported DELETE_FAILED for ${STACK_NAME}."
            echo ""
            echo "Recent CloudFormation events:"
            print_recent_stack_events
            return 1
        fi

        sleep 10
    done
}

# ---------- Stage 2: enterprise SNS ----------

configure_enterprise_sns() {
    print_banner "Stage 2: Enterprise SNS Topic"
    local arn
    while true; do
        arn="$(prompt_required "Enterprise SNS Topic ARN (e.g. arn:aws:sns:ap-southeast-1:222222222222:enterprise-alerts)")"
        if validate_sns_arn "${arn}"; then
            ENTERPRISE_ARN="${arn}"
            break
        fi
        print_error "Invalid ARN format. Expected: arn:aws:sns:<region>:<12-digit-account>:<topic-name>"
    done

    local same_account="no" same_region="no"
    [[ "${ARN_ACCOUNT}" == "${CURRENT_ACCOUNT}" ]] && same_account="yes"
    [[ "${ARN_REGION}" == "${REGION}" ]] && same_region="yes"

    echo ""
    echo "  Topic region:  ${ARN_REGION}"
    echo "  Topic account: ${ARN_ACCOUNT}"
    if [[ "${same_account}" == "yes" ]]; then
        print_success "Same-account deployment detected"
    else
        print_warning "Cross-account deployment detected"
        print_cross_account_policy
    fi
    [[ "${same_region}" != "yes" ]] && print_warning "Cross-region alarm action (${REGION} → ${ARN_REGION}). CloudWatch supports this."
}

print_cross_account_policy() {
    cat <<EOF

${BOLD}The enterprise SNS Topic must allow this account to publish.${NC}
Send the following policy statement to the enterprise ops team and ask them
to merge it into the Topic's access policy:

{
  "Sid": "AllowDnsAlertPublish",
  "Effect": "Allow",
  "Principal": { "AWS": "arn:aws:iam::${CURRENT_ACCOUNT}:root" },
  "Action": "sns:Publish",
  "Resource": "${ENTERPRISE_ARN}"
}

EOF
    read -r -p "Press Enter after the policy has been applied (or to continue and apply it later)..." _
}

# ---------- Stage 3: health check endpoints ----------

collect_endpoints() {
    print_banner "Stage 3: Health Check Endpoints"
    local entries=()
    local names=()
    local add_more="y"
    local input_mode

    echo "Choose how to define health check endpoints:"
    echo "  1) Enter endpoints interactively"
    echo "  2) Import endpoints from YAML file"
    read -r -p "Choose [1/2] (default: 1): " input_mode
    input_mode="${input_mode:-1}"

    case "${input_mode}" in
        1) ;;
        2)
            local source_file
            source_file="$(prompt_with_default "YAML file path" "${CONFIG_FILE}")"
            if ! load_config_entries_from_file "${source_file}"; then
                exit 1
            fi
            if [[ "${#CONFIG_ENTRIES[@]}" -eq 0 ]]; then
                print_error "No health_checks entries were found in ${source_file}."
                exit 1
            fi
            entries=("${CONFIG_ENTRIES[@]}")
            print_success "Imported ${#entries[@]} endpoint definitions from ${source_file}"
            add_more="n"
            ;;
        *)
            print_error "Invalid choice"
            exit 1
            ;;
    esac

    while [[ "${add_more}" == "y" ]]; do
        echo ""
        local name
        while true; do
            name="$(prompt_required "  Endpoint name (e.g. api-prod)")"
            if printf '%s\n' "${names[@]:-}" | grep -Fxq "${name}"; then
                print_error "Name '${name}' already used. Choose a unique name."
                continue
            fi
            break
        done

        local type port path domain interval threshold string_match
        while true; do
            type="$(prompt_with_default "  Type (HTTP/HTTPS/TCP)" "HTTPS")"
            type="$(echo "${type}" | tr '[:lower:]' '[:upper:]')"
            domain="$(prompt_required "  Domain (e.g. api.example.com)")"

            local default_port=443
            [[ "${type}" == "HTTP" ]] && default_port=80
            [[ "${type}" == "TCP" ]] && default_port=80
            port="$(prompt_with_default "  Port" "${default_port}")"

            if [[ "${type}" != "TCP" ]]; then
                path="$(prompt_with_default "  Path" "/health")"
                string_match="$(prompt_with_default "  Expected string in response body (optional)" "")"
            else
                path=""
                string_match=""
            fi

            interval="$(prompt_with_default "  Request interval seconds (10 or 30)" "30")"
            threshold="$(prompt_with_default "  Failure threshold (consecutive failures)" "3")"

            if validate_health_check_entry "${name}" "${type}" "${domain}" "${port}" "${path}" "${interval}" "${threshold}"; then
                break
            fi
            print_warning "Please re-enter the endpoint details."
        done

        entries+=("${name}|${type}|${domain}|${port}|${path}|${interval}|${threshold}|${string_match}")
        names+=("${name}")

        if prompt_yes_no "  Add another endpoint?" "n"; then
            add_more="y"
        else
            add_more="n"
        fi
    done

    print_entries_summary "${entries[@]}"

    echo ""
    print_warning "Ensure your endpoints are reachable by AWS Route 53 health checkers."
    echo "  If firewalled, allow the checker IP ranges from:"
    echo "  https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/route-53-ip-addresses.html"

    write_yaml_from_entries "${entries[@]}"
}

print_entries_summary() {
    echo ""
    print_banner "Endpoints Summary"
    printf "%-20s %-8s %-35s %-6s %-15s %-8s %-9s\n" "NAME" "TYPE" "DOMAIN" "PORT" "PATH" "INTERVAL" "THRESHOLD"
    local entry n t d p pa i th sm
    for entry in "$@"; do
        IFS='|' read -r n t d p pa i th sm <<<"${entry}"
        printf "%-20s %-8s %-35s %-6s %-15s %-8s %-9s\n" "${n}" "${t}" "${d}" "${p}" "${pa:-/}" "${i}" "${th}"
    done
}

write_yaml_from_entries() {
    python3 - "$@" <<'PY'
import sys, yaml
entries = []
for raw in sys.argv[1:]:
    name, t, domain, port, path, interval, threshold, sm = raw.split("|")
    e = {
        "name": name,
        "type": t,
        "domain": domain,
        "port": int(port),
        "request_interval": int(interval),
        "failure_threshold": int(threshold),
    }
    if t != "TCP":
        e["path"] = path or "/"
        if sm:
            e["string_match"] = sm
    entries.append(e)
import os
out = os.environ.get("CONFIG_FILE") or "config/health_checks.yaml"
with open(out, "w") as f:
    yaml.safe_dump({"health_checks": entries}, f, sort_keys=False)
print(f"Wrote {out}")
PY
}

load_config_entries_from_file() {
    local source_file="$1"
    local raw_entries

    if [[ ! -f "${source_file}" ]]; then
        print_error "YAML file not found: ${source_file}"
        return 1
    fi

    if ! raw_entries="$(SOURCE_FILE="${source_file}" python3 - <<'PY'
import os
import sys
import yaml

source_file = os.environ["SOURCE_FILE"]
with open(source_file) as f:
    config = yaml.safe_load(f) or {}

health_checks = config.get("health_checks") or []
if not isinstance(health_checks, list):
    raise SystemExit(f"'health_checks' must be a list in {source_file}")

seen_names = set()
for index, health_check in enumerate(health_checks, start=1):
    if not isinstance(health_check, dict):
        raise SystemExit(f"health_checks[{index}] must be a mapping in {source_file}")

    name = str(health_check.get("name") or "").strip()
    domain = str(health_check.get("domain") or "").strip()
    if not name:
        raise SystemExit(f"health_checks[{index}] is missing required field 'name' in {source_file}")
    if not domain:
        raise SystemExit(f"health_checks[{index}] is missing required field 'domain' in {source_file}")
    if name in seen_names:
        raise SystemExit(f"Duplicate health check name '{name}' found in {source_file}")
    seen_names.add(name)

    health_type = str(health_check.get("type") or "HTTPS").upper()
    if health_type not in {"HTTP", "HTTPS", "TCP"}:
        raise SystemExit(
            f"health_checks[{index}] has invalid type '{health_type}' in {source_file}; "
            "expected HTTP, HTTPS, or TCP"
        )

    port = health_check.get("port", 443 if health_type == "HTTPS" else 80)
    interval = health_check.get("request_interval", 30)
    threshold = health_check.get("failure_threshold", 3)
    path = health_check.get("path", "/") if health_type != "TCP" else ""
    string_match = str(health_check.get("string_match", "") or "")

    if not isinstance(port, int) or not (1 <= port <= 65535):
        raise SystemExit(f"health_checks[{index}] has invalid port '{port}' in {source_file}; expected 1-65535")
    if interval not in {10, 30}:
        raise SystemExit(f"health_checks[{index}] has invalid request_interval '{interval}' in {source_file}; expected 10 or 30")
    if not isinstance(threshold, int) or not (1 <= threshold <= 10):
        raise SystemExit(
            f"health_checks[{index}] has invalid failure_threshold '{threshold}' in {source_file}; expected 1-10"
        )
    if health_type != "TCP" and path and not str(path).startswith("/"):
        raise SystemExit(f"health_checks[{index}] has invalid path '{path}' in {source_file}; expected to start with '/'")

    print("|".join([
        name,
        health_type,
        domain,
        str(port),
        str(path),
        str(interval),
        str(threshold),
        string_match,
    ]))
PY
)"; then
        return 1
    fi

    CONFIG_ENTRIES=()
    while IFS= read -r line; do
        [[ -n "${line}" ]] && CONFIG_ENTRIES+=("${line}")
    done <<< "${raw_entries}"
}

load_config_entries() {
    [[ -f "${CONFIG_FILE}" ]] || {
        CONFIG_ENTRIES=()
        return 0
    }
    load_config_entries_from_file "${CONFIG_FILE}"
}

import_endpoints_from_yaml() {
    local source_file
    source_file="$(prompt_with_default "YAML file path" "${CONFIG_FILE}")"
    if ! load_config_entries_from_file "${source_file}"; then
        return 1
    fi
    if [[ "${#CONFIG_ENTRIES[@]}" -eq 0 ]]; then
        print_error "No health_checks entries were found in ${source_file}."
        return 1
    fi

    print_success "Imported ${#CONFIG_ENTRIES[@]} endpoint definitions from ${source_file}"
    print_entries_summary "${CONFIG_ENTRIES[@]}"
    write_yaml_from_entries "${CONFIG_ENTRIES[@]}"
}

load_alarm_names_from_config() {
    EXPECTED_ALARM_NAMES=()
    [[ -f "${CONFIG_FILE}" ]] || return 0
    while IFS= read -r line; do
        [[ -n "${line}" ]] && EXPECTED_ALARM_NAMES+=("${line}")
    done < <(python3 - <<PY
import yaml
deployment_id = "${DEPLOYMENT_ID}"
with open("${CONFIG_FILE}") as f:
    config = yaml.safe_load(f) or {}
for health_check in config.get("health_checks") or []:
    print(f"r53-{deployment_id}-{health_check['name']}-health")
PY
)
}

# ---------- Notification targets ----------

collect_notification_targets() {
    print_banner "Notification Targets"
    echo "Choose how Route53 alarms should be delivered:"
    echo "  1) Enterprise SNS only"
    echo "  2) Project SNS -> Lark Lambda -> Lark only"
    echo "  3) Both enterprise SNS and project Lark"
    echo "  4) Project SNS relay -> enterprise SNS HTTPS endpoint (native SNS/CloudWatch payload)"

    local choice
    read -r -p "Choose [1/2/3/4] (default: 1): " choice
    choice="${choice:-1}"

    ENTERPRISE_ARN=""
    ENTERPRISE_HTTPS_ENDPOINT_URL=""
    PROJECT_LARK_SECRET_ARN="${PROJECT_LARK_SECRET_ARN:-}"
    PROJECT_LARK_SECRET_MANAGED="${PROJECT_LARK_SECRET_MANAGED:-false}"

    case "${choice}" in
        1)
            NOTIFICATION_MODE="enterprise"
            configure_enterprise_sns
            ;;
        2)
            NOTIFICATION_MODE="project_lark"
            configure_project_lark_secret
            ;;
        3)
            NOTIFICATION_MODE="dual"
            configure_enterprise_sns
            configure_project_lark_secret
            ;;
        4)
            NOTIFICATION_MODE="enterprise_https"
            configure_enterprise_https_endpoint
            ;;
        *)
            print_error "Invalid choice"
            exit 1
            ;;
    esac
}

# ---------- Post-deploy connectivity test ----------

connectivity_test() {
    if notification_mode_has_enterprise "${NOTIFICATION_MODE:-}"; then
        print_banner "Post-deploy: Connectivity Test"
        if ! validate_sns_arn "${ENTERPRISE_ARN}" || [[ "${ARN_TOPIC}" != "dns-alert-e2e-enterprise-placeholder-${DEPLOYMENT_ID}-"* ]]; then
            print_warning "Skipping synthetic SNS publish to the enterprise topic because downstream consumers may require a real CloudWatch alarm payload."
            print_warning "Validate the enterprise path using a real alarm transition or an enterprise-approved test method."
            return
        fi
        print_step "Publishing a test message to the enterprise topic target"
        echo "Publishing a test message to ${ENTERPRISE_ARN} ..."
        if aws sns publish --topic-arn "${ENTERPRISE_ARN}" --region "${ARN_REGION}" \
                --subject "dns-alert connectivity test" \
                --message "This is a connectivity test from the dns-alert deploy script. Ignore." >/dev/null 2>&1; then
            print_success "Successfully published to enterprise topic."
        else
            print_error "Failed to publish to enterprise topic."
            echo "  Possible causes:"
            echo "  - Cross-account policy not yet applied (ask enterprise ops)"
            echo "  - Wrong ARN"
            echo "  - IAM user/role missing sns:Publish permission"
        fi
        return
    fi

    if notification_mode_has_enterprise_https "${NOTIFICATION_MODE:-}"; then
        local relay_topic_arn
        relay_topic_arn="$(stack_output ProjectSnsTopicArn)"
        print_banner "Post-deploy: Connectivity Test"
        if [[ -z "${relay_topic_arn}" || "${relay_topic_arn}" == "None" ]]; then
            print_error "Could not find the project SNS relay topic output."
            return
        fi

        if [[ -n "${ENTERPRISE_HTTPS_ENDPOINT_URL:-}" ]]; then
            print_warning "Skipping synthetic SNS publish because it would forward a non-CloudWatch payload to the enterprise HTTPS endpoint."
            print_warning "Validate this path using a real alarm transition instead."
            return
        fi

        print_step "Publishing a test message to the project SNS relay topic"
        echo "Publishing a test message to ${relay_topic_arn} ..."
        if aws sns publish --topic-arn "${relay_topic_arn}" --region "${REGION}" \
                --subject "dns-alert relay connectivity test" \
                --message "This is a relay-topic connectivity test from the dns-alert deploy script. Ignore." >/dev/null 2>&1; then
            print_success "Successfully published to the project SNS relay topic."
            print_warning "No enterprise HTTPS endpoint is configured yet. This test only proves the relay SNS topic exists and accepts publishes."
        else
            print_error "Failed to publish to the project SNS relay topic."
        fi
        return
    fi

    if [[ -z "${ENTERPRISE_ARN:-}" ]]; then
        print_banner "Post-deploy: Connectivity Test"
        print_warning "Enterprise SNS delivery is not enabled for this deployment. Skipping enterprise publish check."
        return
    fi
}

summarize_enterprise_target() {
    local same_account="no" same_region="no"
    [[ "${ARN_ACCOUNT}" == "${CURRENT_ACCOUNT}" ]] && same_account="yes"
    [[ "${ARN_REGION}" == "${REGION}" ]] && same_region="yes"

    echo "  Topic region:  ${ARN_REGION}"
    echo "  Topic account: ${ARN_ACCOUNT}"
    if [[ "${same_account}" == "yes" ]]; then
        print_success "Same-account deployment detected"
    else
        print_warning "Cross-account deployment detected"
        print_cross_account_policy
    fi
    if [[ "${same_region}" != "yes" ]]; then
        print_warning "Cross-region alarm action (${REGION} → ${ARN_REGION}). CloudWatch supports this."
    fi
}

sanitize_name() {
    printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed 's/^-//; s/-$//'
}

name_exists() {
    local candidate="$1"
    printf '%s\n' "${GENERATED_NAMES[@]:-}" | grep -Fxq "${candidate}"
}

build_e2e_entry() {
    local domain="$1"
    local hc_type="$2"
    local path="$3"
    local interval="$4"
    local threshold="$5"
    local string_match="$6"
    local base slug type_lc name suffix port

    slug="$(sanitize_name "${domain}")"
    type_lc="$(printf '%s' "${hc_type}" | tr '[:upper:]' '[:lower:]')"
    base="${slug}-${type_lc}"
    name="${base}"
    suffix=2
    while name_exists "${name}"; do
        name="${base}-${suffix}"
        suffix=$((suffix + 1))
    done
    GENERATED_NAMES+=("${name}")

    port=443
    [[ "${hc_type}" == "HTTP" ]] && port=80
    [[ "${hc_type}" == "TCP" ]] && port=80

    if [[ "${hc_type}" == "TCP" ]]; then
        E2E_ENTRIES+=("${name}|${hc_type}|${domain}|${port}||${interval}|${threshold}|")
    else
        E2E_ENTRIES+=("${name}|${hc_type}|${domain}|${port}|${path}|${interval}|${threshold}|${string_match}")
    fi
}

normalize_domain_name() {
    printf '%s' "$1" | sed 's/\.$//'
}

route53_domain_selected() {
    local candidate
    candidate="$(normalize_domain_name "${1%%|*}")"
    local selected
    for selected in "${SELECTED_ROUTE53_DOMAINS[@]:-}"; do
        if [[ "$(normalize_domain_name "${selected%%|*}")" == "${candidate}" ]]; then
            return 0
        fi
    done
    return 1
}

e2e_domain_already_configured() {
    local candidate
    candidate="$(normalize_domain_name "$1")"
    local existing
    for existing in "${E2E_CONFIGURED_DOMAINS[@]:-}"; do
        if [[ "${existing}" == "${candidate}" ]]; then
            return 0
        fi
    done
    return 1
}

discover_route53_public_zones() {
    ROUTE53_PUBLIC_ZONES=()

    print_banner "Route53 Domain Discovery"
    print_step "Listing public Route53 hosted zones for profile ${ACTIVE_AWS_PROFILE_LABEL} in account ${CURRENT_ACCOUNT}"

    local zones_output
    if ! zones_output="$(aws route53 list-hosted-zones \
            --query 'HostedZones[?Config.PrivateZone==`false`].[Id,Name]' \
            --output text 2>/dev/null)"; then
        print_warning "Could not query Route53 hosted zones. You can still enter domains manually."
        return 1
    fi

    if [[ -z "${zones_output}" || "${zones_output}" == "None" ]]; then
        print_warning "No public Route53 hosted zones found for this account."
        return 1
    fi

    local zone_id zone_name
    while IFS=$'\t' read -r zone_id zone_name; do
        [[ -z "${zone_id}" ]] && continue
        zone_id="${zone_id#/hostedzone/}"
        zone_name="$(normalize_domain_name "${zone_name}")"
        ROUTE53_PUBLIC_ZONES+=("${zone_id}|${zone_name}")
    done <<< "${zones_output}"

    if [[ "${#ROUTE53_PUBLIC_ZONES[@]}" -eq 0 ]]; then
        print_warning "No public Route53 hosted zones found for this account."
        return 1
    fi

    print_success "Discovered ${#ROUTE53_PUBLIC_ZONES[@]} public hosted zones"
    return 0
}

select_route53_public_zones() {
    SELECTED_ROUTE53_ZONES=()

    [[ "${#ROUTE53_PUBLIC_ZONES[@]}" -gt 0 ]] || return 1

    print_banner "Route53 Public Hosted Zones"
    echo "When an account has many hosted zones, filter first instead of scanning the full list."

    local zone_pool=("${ROUTE53_PUBLIC_ZONES[@]}")
    if [[ "${#zone_pool[@]}" -gt 12 ]]; then
        local keyword
        keyword="$(prompt_with_default "Zone name filter (keyword, '*' for all, blank for all)" "")"
        keyword="$(printf '%s' "${keyword}" | tr '[:upper:]' '[:lower:]')"
        if [[ -n "${keyword}" && "${keyword}" != "*" ]]; then
            zone_pool=()
            local zone zone_id zone_name zone_name_lower
            for zone in "${ROUTE53_PUBLIC_ZONES[@]}"; do
                IFS='|' read -r zone_id zone_name <<< "${zone}"
                zone_name_lower="$(printf '%s' "${zone_name}" | tr '[:upper:]' '[:lower:]')"
                if [[ "${zone_name_lower}" == *"${keyword}"* ]]; then
                    zone_pool+=("${zone}")
                fi
            done
            if [[ "${#zone_pool[@]}" -eq 0 ]]; then
                print_warning "No hosted zones matched '${keyword}'. Falling back to manual domain entry."
                return 1
            fi
            print_success "Filtered to ${#zone_pool[@]} hosted zones"
        fi
    fi

    local index=1
    local zone zone_id zone_name
    printf "  %-4s %-36s %-32s\n" "NO." "HOSTED ZONE" "ZONE ID"
    for zone in "${zone_pool[@]}"; do
        IFS='|' read -r zone_id zone_name <<< "${zone}"
        printf "  %-4s %-36s %-32s\n" "${index})" "${zone_name}" "${zone_id}"
        index=$((index + 1))
    done

    echo ""
    echo "  Enter comma-separated numbers like 1,2 or 'all'."

    local reply raw_choice choice selected_zone
    while true; do
        read -r -p "Choose hosted zones to inspect (Enter to skip Route53 discovery): " reply
        if [[ -z "${reply}" ]]; then
            return 1
        fi

        if [[ "${reply}" =~ ^[Aa][Ll][Ll]$ ]]; then
            SELECTED_ROUTE53_ZONES=("${zone_pool[@]}")
            break
        fi

        IFS=',' read -r -a raw_choice <<< "${reply}"
        SELECTED_ROUTE53_ZONES=()

        local valid_selection="true"
        for choice in "${raw_choice[@]}"; do
            choice="${choice//[[:space:]]/}"
            if ! [[ "${choice}" =~ ^[0-9]+$ ]]; then
                valid_selection="false"
                break
            fi
            if (( choice < 1 || choice > ${#zone_pool[@]} )); then
                valid_selection="false"
                break
            fi

            selected_zone="${zone_pool[$((choice - 1))]}"
            if [[ ! " ${SELECTED_ROUTE53_ZONES[*]} " =~ " ${selected_zone} " ]]; then
                SELECTED_ROUTE53_ZONES+=("${selected_zone}")
            fi
        done

        if [[ "${valid_selection}" == "true" && "${#SELECTED_ROUTE53_ZONES[@]}" -gt 0 ]]; then
            break
        fi

        print_error "Invalid selection. Use comma-separated numbers from the list or 'all'."
    done

    print_success "Selected ${#SELECTED_ROUTE53_ZONES[@]} hosted zones"
    return 0
}

discover_route53_domain_candidates() {
    ROUTE53_CANDIDATES=()
    ROUTE53_RECOMMENDED_CANDIDATES=()

    [[ "${#SELECTED_ROUTE53_ZONES[@]}" -gt 0 ]] || return 1

    print_step "Listing candidate Route53 record names from the selected hosted zones"

    local zone zone_id zone_name records_output
    for zone in "${SELECTED_ROUTE53_ZONES[@]}"; do
        IFS='|' read -r zone_id zone_name <<< "${zone}"

        if ! records_output="$(aws route53 list-resource-record-sets \
                --hosted-zone-id "${zone_id}" \
                --query 'ResourceRecordSets[?Type==`A` || Type==`AAAA` || Type==`CNAME`].[Name,Type]' \
                --output text 2>/dev/null)"; then
            print_warning "Skipping hosted zone ${zone_name} because record discovery failed."
            continue
        fi

        if [[ -z "${records_output}" || "${records_output}" == "None" ]]; then
            print_warning "Hosted zone ${zone_name} has no public A/AAAA/CNAME candidates."
            continue
        fi

        local domain record_type normalized_domain candidate
        while IFS=$'\t' read -r domain record_type; do
            [[ -z "${domain}" || -z "${record_type}" ]] && continue
            normalized_domain="$(normalize_domain_name "${domain}")"
            candidate="${normalized_domain}|${record_type}|${zone_name}"
            ROUTE53_CANDIDATES+=("${candidate}")
            if [[ "${normalized_domain}" != \*.* && "${normalized_domain}" != _* ]]; then
                ROUTE53_RECOMMENDED_CANDIDATES+=("${candidate}")
            fi
        done < <(printf '%s\n' "${records_output}" | awk 'NF')
    done

    if [[ "${#ROUTE53_CANDIDATES[@]}" -eq 0 ]]; then
        print_warning "No candidate public Route53 record names were found."
        return 1
    fi

    local aggregated_candidates
    aggregated_candidates="$(printf '%s\n' "${ROUTE53_CANDIDATES[@]}" | python3 -c '
import sys
from collections import OrderedDict
candidates = OrderedDict()
for raw in sys.stdin:
    raw = raw.strip()
    if not raw:
        continue
    domain, record_type, zone_name = raw.split(\"|\", 2)
    entry = candidates.setdefault(domain, {\"types\": [], \"zones\": []})
    if record_type not in entry[\"types\"]:
        entry[\"types\"].append(record_type)
    if zone_name not in entry[\"zones\"]:
        entry[\"zones\"].append(zone_name)
for domain, info in candidates.items():
    print(f\"{domain}|{','.join(info[\\\"types\\\"])}|{','.join(info[\\\"zones\\\"])}\")
')"

    local aggregated_recommended
    aggregated_recommended="$(printf '%s\n' "${ROUTE53_RECOMMENDED_CANDIDATES[@]}" | python3 -c '
import sys
from collections import OrderedDict
candidates = OrderedDict()
for raw in sys.stdin:
    raw = raw.strip()
    if not raw:
        continue
    domain, record_type, zone_name = raw.split(\"|\", 2)
    entry = candidates.setdefault(domain, {\"types\": [], \"zones\": []})
    if record_type not in entry[\"types\"]:
        entry[\"types\"].append(record_type)
    if zone_name not in entry[\"zones\"]:
        entry[\"zones\"].append(zone_name)
for domain, info in candidates.items():
    print(f\"{domain}|{','.join(info[\\\"types\\\"])}|{','.join(info[\\\"zones\\\"])}\")
')"

    ROUTE53_CANDIDATES=()
    while IFS= read -r candidate; do
        [[ -n "${candidate}" ]] && ROUTE53_CANDIDATES+=("${candidate}")
    done < <(printf '%s\n' "${aggregated_candidates}" | awk 'NF && $1 !~ /^\*\./' | sort -u)

    ROUTE53_RECOMMENDED_CANDIDATES=()
    while IFS= read -r candidate; do
        [[ -n "${candidate}" ]] && ROUTE53_RECOMMENDED_CANDIDATES+=("${candidate}")
    done < <(printf '%s\n' "${aggregated_recommended}" | awk 'NF' | sort -u)

    if [[ "${#ROUTE53_CANDIDATES[@]}" -eq 0 ]]; then
        print_warning "Only wildcard Route53 records were found. Enter domains manually instead."
        return 1
    fi

    print_success "Discovered ${#ROUTE53_CANDIDATES[@]} candidate domains across ${#SELECTED_ROUTE53_ZONES[@]} selected hosted zones"
    return 0
}

select_route53_domain_candidates() {
    SELECTED_ROUTE53_DOMAINS=()

    [[ "${#ROUTE53_CANDIDATES[@]}" -gt 0 ]] || return 1

    print_banner "Route53 Candidate Domains"
    echo "Choose which Route53 domains to monitor for this profile/account."
    echo "Recommended practice: monitor customer-facing entrypoints and key APIs, not every DNS record."
    echo "System/helper records like _domainconnect are hidden from the default list."

    local candidate_pool=("${ROUTE53_RECOMMENDED_CANDIDATES[@]}")
    if [[ "${#candidate_pool[@]}" -eq 0 ]]; then
        candidate_pool=("${ROUTE53_CANDIDATES[@]}")
    elif prompt_yes_no "Show all discovered Route53 records instead of only recommended candidates?" "n"; then
        candidate_pool=("${ROUTE53_CANDIDATES[@]}")
    fi

    [[ "${#candidate_pool[@]}" -gt 0 ]] || return 1

    local index=1
    local candidate domain record_types zone_names
    printf "  %-4s %-40s %-14s %-28s\n" "NO." "DOMAIN" "RECORD TYPES" "HOSTED ZONE"
    for candidate in "${candidate_pool[@]}"; do
        IFS='|' read -r domain record_types zone_names <<< "${candidate}"
        printf "  %-4s %-40s %-14s %-28s\n" "${index})" "${domain}" "${record_types}" "${zone_names}"
        index=$((index + 1))
    done

    echo ""
    echo "  Enter comma-separated numbers like 1,3,5 or 'all'."

    local reply raw_choice choice selected_domain
    while true; do
        read -r -p "Choose domains to monitor (Enter to skip Route53 discovery): " reply
        if [[ -z "${reply}" ]]; then
            return 1
        fi

        if [[ "${reply}" =~ ^[Aa][Ll][Ll]$ ]]; then
            SELECTED_ROUTE53_DOMAINS=("${candidate_pool[@]}")
            break
        fi

        IFS=',' read -r -a raw_choice <<< "${reply}"
        SELECTED_ROUTE53_DOMAINS=()

        local valid_selection="true"
        for choice in "${raw_choice[@]}"; do
            choice="${choice//[[:space:]]/}"
            if ! [[ "${choice}" =~ ^[0-9]+$ ]]; then
                valid_selection="false"
                break
            fi
            if (( choice < 1 || choice > ${#candidate_pool[@]} )); then
                valid_selection="false"
                break
            fi

            selected_domain="${candidate_pool[$((choice - 1))]}"
            if ! route53_domain_selected "${selected_domain}"; then
                SELECTED_ROUTE53_DOMAINS+=("${selected_domain}")
            fi
        done

        if [[ "${valid_selection}" == "true" && "${#SELECTED_ROUTE53_DOMAINS[@]}" -gt 0 ]]; then
            break
        fi

        print_error "Invalid selection. Use comma-separated numbers from the list or 'all'."
    done

    print_success "Selected ${#SELECTED_ROUTE53_DOMAINS[@]} Route53 domains"
    return 0
}

configure_e2e_domain() {
    local requested_domain
    requested_domain="$(normalize_domain_name "$1")"

    local domain
    domain="$(normalize_domain_name "$(prompt_with_default "  Hostname to monitor" "${requested_domain}")")"

    if e2e_domain_already_configured "${domain}"; then
        print_warning "Domain '${domain}' is already configured in this run. Skipping duplicate."
        return
    fi

    E2E_CONFIGURED_DOMAINS+=("${domain}")

    echo ""
    print_step "Configuring health checks for ${domain}"

    local coverage path interval threshold string_match
    coverage="$(prompt_with_default "  Coverage [all/http/https/tcp/custom]" "all")"
    coverage="$(printf '%s' "${coverage}" | tr '[:upper:]' '[:lower:]')"
    path="$(prompt_with_default "  HTTP/HTTPS path" "/")"
    interval="$(prompt_with_default "  Request interval seconds (10 or 30)" "30")"
    threshold="$(prompt_with_default "  Failure threshold" "3")"
    string_match="$(prompt_with_default "  Expected response string for HTTP/HTTPS (optional)" "")"

    case "${coverage}" in
        all)
            build_e2e_entry "${domain}" "HTTP" "${path}" "${interval}" "${threshold}" "${string_match}"
            build_e2e_entry "${domain}" "HTTPS" "${path}" "${interval}" "${threshold}" "${string_match}"
            build_e2e_entry "${domain}" "TCP" "" "${interval}" "${threshold}" ""
            ;;
        http)
            build_e2e_entry "${domain}" "HTTP" "${path}" "${interval}" "${threshold}" "${string_match}"
            ;;
        https)
            build_e2e_entry "${domain}" "HTTPS" "${path}" "${interval}" "${threshold}" "${string_match}"
            ;;
        tcp)
            build_e2e_entry "${domain}" "TCP" "" "${interval}" "${threshold}" ""
            ;;
        custom)
            if prompt_yes_no "  Add HTTP health check?" "y"; then
                build_e2e_entry "${domain}" "HTTP" "${path}" "${interval}" "${threshold}" "${string_match}"
            fi
            if prompt_yes_no "  Add HTTPS health check?" "y"; then
                build_e2e_entry "${domain}" "HTTPS" "${path}" "${interval}" "${threshold}" "${string_match}"
            fi
            if prompt_yes_no "  Add TCP health check?" "y"; then
                build_e2e_entry "${domain}" "TCP" "" "${interval}" "${threshold}" ""
            fi
            ;;
        *)
            print_error "Unsupported coverage option '${coverage}'. Use all/http/https/tcp/custom."
            E2E_CONFIGURED_DOMAINS=("${E2E_CONFIGURED_DOMAINS[@]:0:${#E2E_CONFIGURED_DOMAINS[@]}-1}")
            return 1
            ;;
    esac

    return 0
}

collect_e2e_domains() {
    print_banner "E2E Health Check Coverage"
    echo "This wizard now treats ${CONFIG_FILE} as the primary source of endpoints to monitor."
    echo "Use manual entry only when you need to replace or extend the current config."

    E2E_ENTRIES=()
    GENERATED_NAMES=()
    E2E_CONFIGURED_DOMAINS=()

    load_config_entries

    if [[ "${#CONFIG_ENTRIES[@]}" -gt 0 ]]; then
        print_banner "Configured Endpoints"
        printf "%-28s %-8s %-35s %-6s %-16s\n" "NAME" "TYPE" "DOMAIN" "PORT" "PATH"
        local configured_entry configured_domain
        for configured_entry in "${CONFIG_ENTRIES[@]}"; do
            IFS='|' read -r n t d p pa i th sm <<< "${configured_entry}"
            printf "%-28s %-8s %-35s %-6s %-16s\n" "${n}" "${t}" "${d}" "${p}" "${pa:-/}"
        done
        echo ""
        if prompt_yes_no "Use these configured endpoints for this E2E deployment?" "y"; then
            E2E_ENTRIES=("${CONFIG_ENTRIES[@]}")
            for configured_entry in "${CONFIG_ENTRIES[@]}"; do
                IFS='|' read -r _n _t configured_domain _p _pa _i _th _sm <<< "${configured_entry}"
                if ! e2e_domain_already_configured "${configured_domain}"; then
                    E2E_CONFIGURED_DOMAINS+=("$(normalize_domain_name "${configured_domain}")")
                fi
            done
        fi
    else
        print_warning "No existing endpoints found in ${CONFIG_FILE}."
    fi

    if [[ "${#E2E_ENTRIES[@]}" -eq 0 ]] && ! prompt_yes_no "Build ${CONFIG_FILE} manually now?" "y"; then
        print_error "At least one health check must be configured before deployment."
        exit 1
    fi

    if [[ "${#E2E_ENTRIES[@]}" -eq 0 ]] || prompt_yes_no "Replace or extend endpoints manually?" "n"; then
        if [[ "${#E2E_ENTRIES[@]}" -gt 0 ]] && prompt_yes_no "Reset current configured endpoints before manual entry?" "n"; then
            E2E_ENTRIES=()
            E2E_CONFIGURED_DOMAINS=()
            GENERATED_NAMES=()
        fi
        while true; do
            echo ""
            local domain
            domain="$(prompt_required "  Domain to test (e.g. api.example.com)")"
            while ! configure_e2e_domain "${domain}"; do
                print_warning "Please choose a valid coverage option for ${domain}."
            done

            if ! prompt_yes_no "  Add another domain?" "n"; then
                break
            fi
        done
    fi

    if [[ "${#E2E_ENTRIES[@]}" -eq 0 ]]; then
        print_error "At least one health check must be generated."
        exit 1
    fi

    print_banner "Generated Health Checks"
    printf "%-28s %-8s %-35s %-6s %-16s\n" "NAME" "TYPE" "DOMAIN" "PORT" "PATH"
    local entry
    for entry in "${E2E_ENTRIES[@]}"; do
        IFS='|' read -r n t d p pa i th sm <<<"${entry}"
        printf "%-28s %-8s %-35s %-6s %-16s\n" "${n}" "${t}" "${d}" "${p}" "${pa:-/}"
    done

    print_step "Writing selected health checks to ${CONFIG_FILE}"
    write_yaml_from_entries "${E2E_ENTRIES[@]}"
    print_success "Generated ${#E2E_ENTRIES[@]} health check definitions"
}

create_placeholder_enterprise_topic() {
    local topic_name
    topic_name="dns-alert-e2e-enterprise-placeholder-${DEPLOYMENT_ID}-${CURRENT_ACCOUNT}"
    print_banner "Creating Placeholder Enterprise SNS Topic"
    print_step "Creating or reusing placeholder SNS topic '${topic_name}' in ${REGION}"
    ENTERPRISE_ARN="$(aws sns create-topic --name "${topic_name}" --region "${REGION}" --query 'TopicArn' --output text)"
    ARN_REGION="${REGION}"
    ARN_ACCOUNT="${CURRENT_ACCOUNT}"
    ARN_TOPIC="${topic_name}"
    E2E_PLACEHOLDER_ENTERPRISE="true"
    print_success "Using placeholder topic: ${ENTERPRISE_ARN}"
}

collect_e2e_targets() {
    print_banner "E2E Delivery Targets"
    echo "Choose the path you want to validate:"
    echo "  1) Project SNS -> Lark Lambda -> Lark"
    echo "  2) Enterprise SNS only"
    echo "  3) Both project Lark path and enterprise SNS"
    echo "  4) Project SNS relay -> enterprise SNS HTTPS endpoint (native SNS/CloudWatch payload)"

    local choice
    read -r -p "Choose [1/2/3/4] (default: 1): " choice
    choice="${choice:-1}"

    E2E_ENABLE_LARK="false"
    E2E_PLACEHOLDER_ENTERPRISE="false"
    E2E_NOTIFICATION_MODE=""
    ENTERPRISE_ARN="${ENTERPRISE_ARN:-}"
    ENTERPRISE_HTTPS_ENDPOINT_URL="${ENTERPRISE_HTTPS_ENDPOINT_URL:-}"
    PROJECT_LARK_SECRET_ARN="${PROJECT_LARK_SECRET_ARN:-}"

    case "${choice}" in
        1)
            E2E_ENABLE_LARK="true"
            E2E_NOTIFICATION_MODE="project_lark"
            configure_project_lark_secret
            ;;
        2)
            E2E_NOTIFICATION_MODE="enterprise"
            while true; do
                ENTERPRISE_ARN="$(prompt_required "Enterprise SNS Topic ARN")"
                if validate_sns_arn "${ENTERPRISE_ARN}"; then
                    break
                fi
                print_error "Invalid ARN format."
            done
            ;;
        3)
            E2E_ENABLE_LARK="true"
            E2E_NOTIFICATION_MODE="dual"
            configure_project_lark_secret
            while true; do
                ENTERPRISE_ARN="$(prompt_required "Enterprise SNS Topic ARN")"
                if validate_sns_arn "${ENTERPRISE_ARN}"; then
                    break
                fi
                print_error "Invalid ARN format."
            done
            ;;
        4)
            E2E_NOTIFICATION_MODE="enterprise_https"
            configure_enterprise_https_endpoint
            ;;
        *)
            print_error "Invalid choice"
            exit 1
            ;;
    esac

    if [[ -n "${ENTERPRISE_ARN:-}" ]] && [[ -z "${ARN_REGION:-}" ]]; then
        validate_sns_arn "${ENTERPRISE_ARN}" || true
    fi

    if notification_mode_has_enterprise "${E2E_NOTIFICATION_MODE}"; then
        print_banner "Enterprise Target Summary"
        summarize_enterprise_target
    fi
}

run_optional_project_e2e() {
    if [[ "${E2E_ENABLE_LARK}" != "true" ]]; then
        return
    fi

    print_banner "Post-deploy Validation"
    echo "Choose how to validate the project SNS -> Lark path:"
    echo "  1) Observe real Route53/CloudWatch alarm transitions"
    echo "  2) Run synthetic SNS -> Lark E2E"
    echo "  3) Skip post-deploy validation"

    local choice
    read -r -p "Choose [1/2/3] (default: 1): " choice
    choice="${choice:-1}"

    case "${choice}" in
        1) observe_real_project_e2e ;;
        2)
            print_step "Launching scripts/test_e2e.sh"
            "${PROJECT_ROOT}/scripts/test_e2e.sh"
            ;;
        3)
            print_warning "Skipping post-deploy validation. You can rerun ./scripts/test_e2e.sh later for synthetic checks."
            ;;
        *)
            print_error "Invalid choice"
            exit 1
            ;;
    esac
}

run_optional_enterprise_https_e2e() {
    if ! notification_mode_has_enterprise_https "${E2E_NOTIFICATION_MODE:-}"; then
        return
    fi

    print_banner "Post-deploy Validation"
    echo "Choose how to validate the project SNS -> enterprise SNS HTTPS path:"
    echo "  1) Observe real Route53/CloudWatch alarm transitions"
    echo "  2) Skip post-deploy validation"

    local choice
    read -r -p "Choose [1/2] (default: 1): " choice
    choice="${choice:-1}"

    case "${choice}" in
        1) observe_real_enterprise_https_e2e ;;
        2) print_warning "Skipping post-deploy validation." ;;
        *)
            print_error "Invalid choice"
            exit 1
            ;;
    esac
}

observe_real_project_e2e() {
    load_alarm_names_from_config
    if [[ "${#EXPECTED_ALARM_NAMES[@]}" -eq 0 ]]; then
        print_warning "No alarm names could be derived from ${CONFIG_FILE}. Skipping real alarm observation."
        return
    fi

    print_banner "Real Alarm Observation"
    echo "This mode does not publish synthetic SNS messages."
    echo "It watches the real CloudWatch alarm states produced by Route53 health checks."

    local timeout_seconds
    timeout_seconds="$(prompt_with_default "Observation timeout seconds" "600")"
    local poll_seconds
    poll_seconds="$(prompt_with_default "Polling interval seconds" "30")"

    local start_epoch now_epoch state_rows
    start_epoch="$(date +%s)"

    while true; do
        state_rows="$(aws cloudwatch describe-alarms \
            --region "${REGION}" \
            --alarm-names "${EXPECTED_ALARM_NAMES[@]}" \
            --query 'MetricAlarms[].[AlarmName,StateValue,StateUpdatedTimestamp]' \
            --output text)"

        if [[ -z "${state_rows}" || "${state_rows}" == "None" ]]; then
            print_warning "No matching CloudWatch alarms were returned."
            return
        fi

        local observed_count=0
        echo ""
        printf "  %-38s %-18s %-28s\n" "ALARM NAME" "STATE" "UPDATED"
        local all_resolved="true"
        local alarm_name state_value updated_at
        while IFS=$'\t' read -r alarm_name state_value updated_at; do
            [[ -z "${alarm_name}" ]] && continue
            observed_count=$((observed_count + 1))
            printf "  %-38s %-18s %-28s\n" "${alarm_name}" "${state_value}" "${updated_at}"
            if [[ "${state_value}" == "INSUFFICIENT_DATA" ]]; then
                all_resolved="false"
            fi
        done <<< "${state_rows}"

        if (( observed_count != ${#EXPECTED_ALARM_NAMES[@]} )); then
            all_resolved="false"
            print_warning "Expected ${#EXPECTED_ALARM_NAMES[@]} alarms but observed ${observed_count}. Waiting for the remaining alarms to appear."
        fi

        if [[ "${all_resolved}" == "true" ]]; then
            break
        fi

        now_epoch="$(date +%s)"
        if (( now_epoch - start_epoch >= timeout_seconds )); then
            print_warning "Timed out waiting for real alarms to move beyond INSUFFICIENT_DATA."
            return
        fi

        print_step "At least one alarm is still INSUFFICIENT_DATA. Waiting ${poll_seconds}s before checking again."
        sleep "${poll_seconds}"
    done

    echo ""
    print_banner "Recent Real Alarm Transitions"
    local alarm
    for alarm in "${EXPECTED_ALARM_NAMES[@]}"; do
        echo "  ${alarm}"
        aws cloudwatch describe-alarm-history \
            --region "${REGION}" \
            --alarm-name "${alarm}" \
            --history-item-type StateUpdate \
            --max-items 3 \
            --query 'AlarmHistoryItems[].[Timestamp,HistorySummary]' \
            --output text || true
        echo ""
    done

    print_warning "Confirm in Lark that these were real AWS-generated alarm notifications, not synthetic test payloads."
}

observe_real_enterprise_https_e2e() {
    load_alarm_names_from_config
    if [[ "${#EXPECTED_ALARM_NAMES[@]}" -eq 0 ]]; then
        print_warning "No alarm names could be derived from ${CONFIG_FILE}. Skipping real alarm observation."
        return
    fi

    print_banner "Real Alarm Observation"
    echo "This mode does not publish synthetic SNS messages."
    echo "It watches the real CloudWatch alarm states produced by Route53 health checks."

    local timeout_seconds
    timeout_seconds="$(prompt_with_default "Observation timeout seconds" "600")"
    local poll_seconds
    poll_seconds="$(prompt_with_default "Polling interval seconds" "30")"

    local start_epoch now_epoch state_rows
    start_epoch="$(date +%s)"

    while true; do
        state_rows="$(aws cloudwatch describe-alarms \
            --region "${REGION}" \
            --alarm-names "${EXPECTED_ALARM_NAMES[@]}" \
            --query 'MetricAlarms[].[AlarmName,StateValue,StateUpdatedTimestamp]' \
            --output text)"

        if [[ -z "${state_rows}" || "${state_rows}" == "None" ]]; then
            print_warning "No matching CloudWatch alarms were returned."
            return
        fi

        local observed_count=0
        echo ""
        printf "  %-38s %-18s %-28s\n" "ALARM NAME" "STATE" "UPDATED"
        local all_resolved="true"
        local alarm_name state_value updated_at
        while IFS=$'\t' read -r alarm_name state_value updated_at; do
            [[ -z "${alarm_name}" ]] && continue
            observed_count=$((observed_count + 1))
            printf "  %-38s %-18s %-28s\n" "${alarm_name}" "${state_value}" "${updated_at}"
            if [[ "${state_value}" == "INSUFFICIENT_DATA" ]]; then
                all_resolved="false"
            fi
        done <<< "${state_rows}"

        if (( observed_count != ${#EXPECTED_ALARM_NAMES[@]} )); then
            all_resolved="false"
            print_warning "Expected ${#EXPECTED_ALARM_NAMES[@]} alarms but observed ${observed_count}. Waiting for the remaining alarms to appear."
        fi

        if [[ "${all_resolved}" == "true" ]]; then
            break
        fi

        now_epoch="$(date +%s)"
        if (( now_epoch - start_epoch >= timeout_seconds )); then
            print_warning "Timed out waiting for real alarms to move beyond INSUFFICIENT_DATA."
            return
        fi

        print_step "At least one alarm is still INSUFFICIENT_DATA. Waiting ${poll_seconds}s before checking again."
        sleep "${poll_seconds}"
    done

    echo ""
    print_banner "Recent Real Alarm Transitions"
    local alarm
    for alarm in "${EXPECTED_ALARM_NAMES[@]}"; do
        echo "  ${alarm}"
        aws cloudwatch describe-alarm-history \
            --region "${REGION}" \
            --alarm-name "${alarm}" \
            --history-item-type StateUpdate \
            --max-items 3 \
            --query 'AlarmHistoryItems[].[Timestamp,HistorySummary]' \
            --output text || true
        echo ""
    done

    if [[ -n "${ENTERPRISE_HTTPS_ENDPOINT_URL:-}" ]]; then
        print_warning "Confirm with the enterprise endpoint owner that the raw SNS/CloudWatch message was accepted."
    else
        print_warning "No enterprise HTTPS endpoint is configured. This proves only the Route53/CloudWatch -> project SNS topic portion of the path."
    fi
}

# ---------- Modes ----------

mode_new_deployment() {
    check_aws_auth
    load_notification_context
    collect_notification_targets
    collect_endpoints

    print_banner "Stage 5: Review & Deploy"
    echo "  Notification mode:  ${NOTIFICATION_MODE}"
    notification_mode_has_enterprise "${NOTIFICATION_MODE}" && echo "  Enterprise SNS ARN: ${ENTERPRISE_ARN}"
    notification_mode_has_enterprise_https "${NOTIFICATION_MODE}" && echo "  Enterprise HTTPS:   ${ENTERPRISE_HTTPS_ENDPOINT_URL:-"(not configured yet)"}"
    notification_mode_has_project_lark "${NOTIFICATION_MODE}" && echo "  Lark secret ARN:    ${PROJECT_LARK_SECRET_ARN}"
    echo "  Config file:        ${CONFIG_FILE}"
    echo ""
    prompt_yes_no "Proceed with deployment?" "y" || { echo "Aborted."; exit 0; }

    persist_notification_context
    write_context "enterprise_sns_topic_is_placeholder" "false"

    install_cdk_deps
    cdk_bootstrap_if_needed
    cdk_deploy
    connectivity_test

    echo ""
    print_success "Deployment complete."
    echo ""
    echo "Next steps:"
    echo "  - Verify alarms in ${REGION}: https://console.aws.amazon.com/cloudwatch/home?region=${REGION}#alarmsV2:"
    if notification_mode_has_project_lark "${NOTIFICATION_MODE}"; then
        echo "  - Run project Lark validation: ./scripts/test_e2e.sh"
    fi
    echo "  - Manage endpoints later: ./scripts/deploy.sh (Mode 2)"
}

mode_manage_endpoints() {
    check_aws_auth
    if [[ ! -f "${CONFIG_FILE}" ]]; then
        print_error "No existing deployment found (${CONFIG_FILE} missing). Run Mode 1 first."
        exit 1
    fi

    local config_changed="false"
    while true; do
        echo ""
        print_banner "Current Endpoints"
        python3 -c "
import yaml
with open('${CONFIG_FILE}') as f:
    d = yaml.safe_load(f) or {}
hcs = d.get('health_checks') or []
if not hcs:
    print('  (none)')
for i, hc in enumerate(hcs, 1):
    suffix = hc.get('path', '') if hc.get('type', 'HTTPS').upper() != 'TCP' else ''
    print(f\"  {i}) {hc['name']:<20} {hc.get('type', 'HTTPS'):<6} {hc['domain']}:{hc.get('port', '')}{suffix}\")
" || true
        echo ""
        echo "  a) Add endpoint"
        echo "  i) Import endpoints from YAML file"
        echo "  c) Cleanup orphaned Route53 health checks"
        echo "  d) Delete endpoint"
        echo "  q) Done — deploy changes"
        local choice
        read -r -p "Choose [a/i/c/d/q]: " choice
        case "${choice}" in
            a) add_single_endpoint; config_changed="true" ;;
            i) import_endpoints_from_yaml && config_changed="true" ;;
            c) cleanup_orphaned_route53_health_checks ;;
            d) delete_single_endpoint; config_changed="true" ;;
            q) break ;;
            *) print_error "Invalid choice" ;;
        esac
    done

    if [[ "${config_changed}" == "true" ]]; then
        install_cdk_deps
        cdk_deploy
        print_success "Endpoint changes deployed."
    else
        print_success "No endpoint configuration changes to deploy."
    fi
}

add_single_endpoint() {
    load_hc_names
    local name type port path domain interval threshold string_match
    while true; do
        name="$(prompt_required "  Endpoint name")"
        if printf '%s\n' "${HC_NAMES[@]:-}" | grep -Fxq "${name}"; then
            print_error "Name '${name}' already exists."
            continue
        fi
        break
    done

    while true; do
        type="$(prompt_with_default "  Type (HTTP/HTTPS/TCP)" "HTTPS")"
        type="$(echo "${type}" | tr '[:lower:]' '[:upper:]')"
        domain="$(prompt_required "  Domain")"
        local default_port=443
        [[ "${type}" == "HTTP" ]] && default_port=80
        [[ "${type}" == "TCP" ]] && default_port=80
        port="$(prompt_with_default "  Port" "${default_port}")"
        if [[ "${type}" != "TCP" ]]; then
            path="$(prompt_with_default "  Path" "/health")"
            string_match="$(prompt_with_default "  String match (optional)" "")"
        else
            path=""
            string_match=""
        fi
        interval="$(prompt_with_default "  Interval (10/30)" "30")"
        threshold="$(prompt_with_default "  Failure threshold" "3")"

        if validate_health_check_entry "${name}" "${type}" "${domain}" "${port}" "${path}" "${interval}" "${threshold}"; then
            break
        fi
        print_warning "Please re-enter the endpoint details."
    done

    NEW_ENTRY="${name}|${type}|${domain}|${port}|${path}|${interval}|${threshold}|${string_match}" \
        python3 - <<'PY'
import os, yaml
entry_raw = os.environ["NEW_ENTRY"]
name, t, domain, port, path, interval, threshold, sm = entry_raw.split("|")
entry = {
    "name": name,
    "type": t,
    "domain": domain,
    "port": int(port),
    "request_interval": int(interval),
    "failure_threshold": int(threshold),
}
if t != "TCP":
    entry["path"] = path or "/"
    if sm:
        entry["string_match"] = sm
cf = os.environ.get("CONFIG_FILE")
with open(cf) as f:
    d = yaml.safe_load(f) or {"health_checks": []}
d.setdefault("health_checks", []).append(entry)
with open(cf, "w") as f:
    yaml.safe_dump(d, f, sort_keys=False)
PY
    print_success "Added '${name}'."
}

delete_single_endpoint() {
    load_hc_names
    if [[ "${#HC_NAMES[@]}" -eq 0 ]]; then
        print_error "No endpoints to delete."
        return
    fi
    local idx
    read -r -p "  Number to delete: " idx
    if ! [[ "${idx}" =~ ^[0-9]+$ ]] || (( idx < 1 || idx > ${#HC_NAMES[@]} )); then
        print_error "Invalid number."
        return
    fi
    local target="${HC_NAMES[$((idx-1))]}"
    local alarm_name="r53-${DEPLOYMENT_ID}-${target}-health"

    local state
    state="$(aws cloudwatch describe-alarms --alarm-names "${alarm_name}" --region "${REGION}" \
                --query 'MetricAlarms[0].StateValue' --output text 2>/dev/null || echo "")"
    if [[ "${state}" == "ALARM" ]]; then
        print_warning "Endpoint '${target}' is currently in ALARM state."
        if ! prompt_yes_no "Deleting will remove this active alert. Continue?" "n"; then
            return
        fi
    fi
    if (( ${#HC_NAMES[@]} == 1 )); then
        if ! prompt_yes_no "This will remove the last endpoint. Continue?" "n"; then
            return
        fi
    fi

    TARGET="${target}" python3 - <<'PY'
import os, yaml
cf = os.environ["CONFIG_FILE"]
target = os.environ["TARGET"]
with open(cf) as f:
    d = yaml.safe_load(f) or {}
d["health_checks"] = [hc for hc in d.get("health_checks", []) if hc["name"] != target]
with open(cf, "w") as f:
    yaml.safe_dump(d, f, sort_keys=False)
PY
    print_success "Removed '${target}'."
}

mode_update_sns() {
    check_aws_auth
    load_notification_context
    print_banner "Current Enterprise Delivery Configuration"
    if notification_mode_has_enterprise "${NOTIFICATION_MODE}" && [[ -n "${ENTERPRISE_ARN}" ]]; then
        echo "  Topic ARN: ${ENTERPRISE_ARN}"
        if validate_sns_arn "${ENTERPRISE_ARN}"; then
            local same="cross-account"
            [[ "${ARN_ACCOUNT}" == "${CURRENT_ACCOUNT}" ]] && same="same-account"
            local reg="cross-region"
            [[ "${ARN_REGION}" == "${REGION}" ]] && reg="same-region"
            echo "  Mode:      ${same}, ${reg}"
        fi
    elif notification_mode_has_enterprise_https "${NOTIFICATION_MODE}"; then
        echo "  Delivery mode: Project SNS relay -> enterprise SNS HTTPS endpoint"
        echo "  HTTPS endpoint: ${ENTERPRISE_HTTPS_ENDPOINT_URL:-"(not configured yet)"}"
    else
        echo "  Status: Disabled"
    fi

    echo ""
    if notification_mode_has_enterprise "${NOTIFICATION_MODE}"; then
        echo "  1) Update enterprise SNS ARN"
        if notification_mode_has_project_lark "${NOTIFICATION_MODE}"; then
            echo "  2) Disable enterprise SNS path"
        fi
        local choice
        if notification_mode_has_project_lark "${NOTIFICATION_MODE}"; then
            read -r -p "Choose [1/2] (default: 1): " choice
        else
            read -r -p "Choose [1] (default: 1): " choice
        fi
        choice="${choice:-1}"
        case "${choice}" in
            1)
                configure_enterprise_sns
                ENTERPRISE_HTTPS_ENDPOINT_URL=""
                notification_mode_has_project_lark "${NOTIFICATION_MODE}" && NOTIFICATION_MODE="dual" || NOTIFICATION_MODE="enterprise"
                ;;
            2)
                if ! notification_mode_has_project_lark "${NOTIFICATION_MODE}"; then
                    print_error "Cannot disable enterprise SNS because no project Lark path is enabled."
                    exit 1
                fi
                ENTERPRISE_ARN=""
                ENTERPRISE_HTTPS_ENDPOINT_URL=""
                NOTIFICATION_MODE="project_lark"
                ;;
            *)
                print_error "Invalid choice."
                exit 1
                ;;
        esac
    elif notification_mode_has_enterprise_https "${NOTIFICATION_MODE}"; then
        echo "  1) Update enterprise SNS HTTPS endpoint"
        echo "  2) Clear enterprise SNS HTTPS endpoint"
        local choice
        read -r -p "Choose [1/2] (default: 1): " choice
        choice="${choice:-1}"
        case "${choice}" in
            1)
                configure_enterprise_https_endpoint
                ENTERPRISE_ARN=""
                NOTIFICATION_MODE="enterprise_https"
                ;;
            2)
                ENTERPRISE_HTTPS_ENDPOINT_URL=""
                ENTERPRISE_ARN=""
                NOTIFICATION_MODE="enterprise_https"
                ;;
            *)
                print_error "Invalid choice."
                exit 1
                ;;
        esac
    else
        echo "  1) Enable enterprise SNS topic delivery"
        echo "  2) Enable project SNS relay -> enterprise SNS HTTPS endpoint"
        local choice
        read -r -p "Choose [1/2] (default: 1): " choice
        choice="${choice:-1}"
        case "${choice}" in
            1)
                configure_enterprise_sns
                ENTERPRISE_HTTPS_ENDPOINT_URL=""
                notification_mode_has_project_lark "${NOTIFICATION_MODE}" && NOTIFICATION_MODE="dual" || NOTIFICATION_MODE="enterprise"
                ;;
            2)
                if notification_mode_has_project_lark "${NOTIFICATION_MODE}"; then
                    print_error "Combining project Lark and enterprise SNS HTTPS relay is not supported yet."
                    exit 1
                fi
                configure_enterprise_https_endpoint
                ENTERPRISE_ARN=""
                NOTIFICATION_MODE="enterprise_https"
                ;;
            *)
                print_error "Invalid choice."
                exit 1
                ;;
        esac
    fi

    persist_notification_context
    write_context "enterprise_sns_topic_is_placeholder" "false"

    install_cdk_deps
    cdk_deploy
    connectivity_test
}

mode_toggle_lark() {
    check_aws_auth
    load_notification_context

    print_banner "Project Lark Notifications"
    if notification_mode_has_project_lark "${NOTIFICATION_MODE}"; then
        echo "  Status: Enabled"
        echo "  Secret ARN: ${PROJECT_LARK_SECRET_ARN}"
        echo "  Script-managed: ${PROJECT_LARK_SECRET_MANAGED:-false}"
        echo ""
        echo "  1) Disable project Lark notifications"
        echo "  2) Update current secret value"
        echo "  3) Change secret source"
        local choice
        read -r -p "Choose [1/2/3]: " choice
        case "${choice}" in
            1)
                if ! notification_mode_has_enterprise "${NOTIFICATION_MODE}"; then
                    print_error "Cannot disable project Lark notifications because enterprise SNS delivery is not enabled."
                    exit 1
                fi
                NOTIFICATION_MODE="enterprise"
                ;;
            2)
                local new_url
                new_url="$(prompt_required "  New Lark webhook URL")"
                update_current_project_lark_secret_value "${new_url}"
                ;;
            3)
                configure_project_lark_secret
                ;;
            *) print_error "Invalid choice"; exit 1 ;;
        esac
    else
        echo "  Status: Disabled"
        echo ""
        if ! prompt_yes_no "Enable project SNS -> Lark notifications?" "y"; then
            return
        fi
        if notification_mode_has_enterprise_https "${NOTIFICATION_MODE}"; then
            print_error "Combining project Lark with the enterprise SNS HTTPS relay mode is not supported yet."
            exit 1
        fi
        configure_project_lark_secret
        notification_mode_has_enterprise "${NOTIFICATION_MODE}" && NOTIFICATION_MODE="dual" || NOTIFICATION_MODE="project_lark"
    fi

    persist_notification_context
    write_context "enterprise_sns_topic_is_placeholder" "false"
    install_cdk_deps
    cdk_deploy
    print_success "Project Lark notification configuration updated."
}

mode_destroy_deployment() {
    check_aws_auth

    local enterprise_arn placeholder_enterprise
    load_notification_context
    enterprise_arn="$(read_context enterprise_sns_topic_arn)"
    placeholder_enterprise="$(read_context enterprise_sns_topic_is_placeholder)"

    print_banner "Destroy Deployment"
    echo "This will remove the current ${STACK_NAME} stack in ${REGION}."
    [[ -n "${enterprise_arn}" ]] && echo "  Context enterprise SNS ARN: ${enterprise_arn}"
    echo "  Placeholder enterprise topic: ${placeholder_enterprise:-false}"

    if stack_exists; then
        print_stack_destroy_preview
        echo ""
        print_warning "Deleting this stack removes only the resources managed by this CloudFormation stack in the account and region shown above."
        prompt_yes_no "Delete ${STACK_NAME} and all CloudFormation-managed resources?" "n" || { echo "Aborted."; exit 0; }
        prompt_exact_match \
            "Type '${STACK_NAME}@${CURRENT_ACCOUNT}/${REGION}' to confirm destroy" \
            "${STACK_NAME}@${CURRENT_ACCOUNT}/${REGION}" || {
                print_error "Confirmation text did not match. Aborted."
                exit 1
            }
        print_step "Deleting CloudFormation stack ${STACK_NAME}"
        aws cloudformation delete-stack --stack-name "${STACK_NAME}" --region "${REGION}"
        wait_for_stack_deletion
    else
        print_warning "${STACK_NAME} does not currently exist in ${REGION}."
    fi

    if [[ "${placeholder_enterprise}" == "true" && -n "${enterprise_arn}" ]]; then
        if validate_sns_arn "${enterprise_arn}" && [[ "${ARN_TOPIC}" == "dns-alert-e2e-enterprise-placeholder-${DEPLOYMENT_ID}-"* ]]; then
            print_step "Deleting placeholder enterprise SNS topic ${enterprise_arn}"
            aws sns delete-topic --topic-arn "${enterprise_arn}" --region "${ARN_REGION}"
            print_success "Deleted placeholder SNS topic"
        else
            print_warning "Context enterprise SNS ARN is not a recognized placeholder topic. Leaving it unchanged."
        fi
    fi

    print_step "Clearing local deploy context"
    write_context "deploy_region" ""
    write_context "bootstrap_suffix" ""
    write_context "notification_mode" ""
    write_context "enterprise_sns_topic_arn" ""
    write_context "enterprise_https_endpoint_url" ""
    write_context "project_lark_secret_arn" ""
    write_context "project_lark_secret_managed" "false"
    write_context "enable_test_lark" "false"
    write_context "lark_webhook_url" ""
    write_context "enterprise_sns_topic_is_placeholder" "false"

    if [[ -n "${PROJECT_LARK_SECRET_ARN:-}" && "${PROJECT_LARK_SECRET_MANAGED:-false}" == "true" ]]; then
        if prompt_yes_no "Schedule deletion for the managed project Lark webhook secret?" "n"; then
            print_step "Scheduling deletion for ${PROJECT_LARK_SECRET_ARN}"
            aws secretsmanager delete-secret \
                --secret-id "${PROJECT_LARK_SECRET_ARN}" \
                --region "${REGION}" \
                --recovery-window-in-days 7 >/dev/null
            print_success "Managed webhook secret scheduled for deletion"
        fi
    fi

    print_success "Destroy workflow complete."
}

mode_e2e_wizard() {
    collect_e2e_profiles

    if (( ${#E2E_PROFILES[@]} > 1 )); then
        print_warning "Mode 5 uses the shared repo config file ${CONFIG_FILE} for every selected AWS profile."
        print_warning "If different accounts need different endpoints, run them separately or rebuild the config between runs."
    fi

    local profile
    for profile in "${E2E_PROFILES[@]}"; do
        activate_aws_profile "${profile}"
        print_banner "Guided E2E Run"
        echo "  AWS profile: ${ACTIVE_AWS_PROFILE_LABEL}"

        check_aws_auth
        collect_e2e_targets
        collect_e2e_domains

        print_banner "E2E Deployment Summary"
        echo "  AWS Profile:         ${ACTIVE_AWS_PROFILE_LABEL}"
        echo "  AWS Account:         ${CURRENT_ACCOUNT}"
        echo "  Region:              ${REGION}"
        echo "  Notification mode:   ${E2E_NOTIFICATION_MODE}"
        notification_mode_has_enterprise "${E2E_NOTIFICATION_MODE}" && echo "  Enterprise SNS ARN:  ${ENTERPRISE_ARN}"
        notification_mode_has_enterprise_https "${E2E_NOTIFICATION_MODE}" && echo "  Enterprise HTTPS:    ${ENTERPRISE_HTTPS_ENDPOINT_URL:-"(not configured yet)"}"
        notification_mode_has_project_lark "${E2E_NOTIFICATION_MODE}" && echo "  Lark secret ARN:     ${PROJECT_LARK_SECRET_ARN}"
        echo "  Health checks:       ${#E2E_ENTRIES[@]}"
        echo ""
        prompt_yes_no "Proceed with E2E deployment for profile ${ACTIVE_AWS_PROFILE_LABEL}?" "y" || { echo "Aborted."; exit 0; }

        print_step "Saving E2E deploy context for follow-on modes"
        NOTIFICATION_MODE="${E2E_NOTIFICATION_MODE}"
        persist_notification_context
        write_context "enterprise_sns_topic_is_placeholder" "${E2E_PLACEHOLDER_ENTERPRISE}"

        install_cdk_deps
        cdk_bootstrap_if_needed
        cdk_deploy_with_context \
            "${E2E_NOTIFICATION_MODE}" \
            "${ENTERPRISE_ARN:-}" \
            "${PROJECT_LARK_SECRET_ARN:-}" \
            "${ENTERPRISE_HTTPS_ENDPOINT_URL:-}"
        connectivity_test
        run_optional_project_e2e
        run_optional_enterprise_https_e2e

        echo ""
        print_success "E2E deployment workflow complete for profile ${ACTIVE_AWS_PROFILE_LABEL}."
        echo "Next steps:"
        echo "  - Watch Route53 alarms in ${REGION} for account ${CURRENT_ACCOUNT}"
        if notification_mode_has_project_lark "${E2E_NOTIFICATION_MODE}"; then
            echo "  - If you deployed project Lark path, you can rerun ./scripts/test_e2e.sh anytime with AWS_PROFILE=${ACTIVE_AWS_PROFILE_LABEL}"
        fi
        if notification_mode_has_enterprise_https "${E2E_NOTIFICATION_MODE}"; then
            echo "  - If the enterprise HTTPS endpoint is configured, confirm with the enterprise team that the raw SNS/CloudWatch message was received"
        fi
    done
}

# ---------- Entry ----------

export CONFIG_FILE

main() {
    echo "${BOLD}=== DNS Alert Management ===${NC}"
    echo "What would you like to do?"
    echo "  1) New deployment (first time setup)"
    echo "  2) Add/remove health check endpoints"
    echo "  3) Update enterprise delivery configuration"
    echo "  4) Enable/disable project Lark notifications"
    echo "  5) Guided E2E deployment wizard"
    echo "  6) Destroy current deployment"
    local mode
    read -r -p "Choose [1/2/3/4/5/6] (default: 1): " mode
    mode="${mode:-1}"
    case "${mode}" in
        1) mode_new_deployment ;;
        2) mode_manage_endpoints ;;
        3) mode_update_sns ;;
        4) mode_toggle_lark ;;
        5) mode_e2e_wizard ;;
        6) mode_destroy_deployment ;;
        *) print_error "Invalid mode"; exit 1 ;;
    esac
}

main "$@"
