#!/usr/bin/env bash

set -Eeuo pipefail

usage() {
    cat <<'USAGE'
Usage:
  q100-140-clone-handoff-audit.sh \
    --role ROLE --node NODE_KEY --expected-ip IPV4 --expected-hostname HOSTNAME \
    [--bundle-root /opt/zhct-deploy] [--run-id ID] [--apply-post-clone]

Default mode is read-only handoff audit. --apply-post-clone regenerates clone
identity and renders the selected node configuration, but keeps every role unit
disabled/inactive and requires all three VIPs to remain absent.
USAGE
}

die() {
    printf 'ERROR: %s\n' "$*" >&2
    exit 1
}

ROLE=
NODE_KEY=
EXPECTED_IP=
EXPECTED_HOSTNAME=
BUNDLE_ROOT=/opt/zhct-deploy
RUN_ID=
APPLY_POST_CLONE=0

while (($#)); do
    case "$1" in
        --role) ROLE=${2:-}; shift 2 ;;
        --node) NODE_KEY=${2:-}; shift 2 ;;
        --expected-ip) EXPECTED_IP=${2:-}; shift 2 ;;
        --expected-hostname) EXPECTED_HOSTNAME=${2:-}; shift 2 ;;
        --bundle-root) BUNDLE_ROOT=${2:-}; shift 2 ;;
        --run-id) RUN_ID=${2:-}; shift 2 ;;
        --apply-post-clone) APPLY_POST_CLONE=1; shift ;;
        -h|--help) usage; exit 0 ;;
        *) die "unknown argument: $1" ;;
    esac
done

[[ $EUID -eq 0 ]] || die 'must run as root'
case "$ROLE" in nginx|app|mysql|redis|emqx) ;; *) die "unsupported role: $ROLE" ;; esac
[[ -n "$NODE_KEY" ]] || die 'missing --node'
[[ -n "$EXPECTED_IP" ]] || die 'missing --expected-ip'
[[ -n "$EXPECTED_HOSTNAME" ]] || die 'missing --expected-hostname'
[[ "$EXPECTED_IP" =~ ^10\.2\.(203|208)\.[0-9]{1,3}$ ]] || die "unexpected IP: $EXPECTED_IP"
[[ -d "$BUNDLE_ROOT/roles/$ROLE" ]] || die "role bundle absent: $BUNDLE_ROOT/roles/$ROLE"
[[ -x "$BUNDLE_ROOT/common/scripts/post-clone.sh" ]] || die 'post-clone entrypoint absent'

RUN_ID=${RUN_ID:-qclone-$(date +%Y%m%dT%H%M%S%z)}
EVIDENCE_ROOT=/var/backups/zhct-clone-handoff
EVIDENCE_DIR="$EVIDENCE_ROOT/$RUN_ID/$NODE_KEY"
mkdir -p "$EVIDENCE_DIR"
chmod 0700 "$EVIDENCE_ROOT" "$EVIDENCE_ROOT/$RUN_ID" "$EVIDENCE_DIR"
exec > >(tee -a "$EVIDENCE_DIR/console.log") 2>&1

printf 'RUN_ID=%s\nROLE=%s\nNODE_KEY=%s\nEXPECTED_IP=%s\nEXPECTED_HOSTNAME=%s\n' \
    "$RUN_ID" "$ROLE" "$NODE_KEY" "$EXPECTED_IP" "$EXPECTED_HOSTNAME"

actual_hostname=$(hostname -s)
actual_hostname_lc=$(printf '%s' "$actual_hostname" | tr '[:upper:]' '[:lower:]')
expected_hostname_lc=$(printf '%s' "$EXPECTED_HOSTNAME" | tr '[:upper:]' '[:lower:]')
[[ "$actual_hostname_lc" == "$expected_hostname_lc" ]] ||
    die "hostname mismatch: actual=$actual_hostname expected=$EXPECTED_HOSTNAME"

node_interface=$(ip -o -4 addr show scope global |
    awk -v expected="$EXPECTED_IP" '{split($4,a,"/"); if (a[1] == expected) {print $2; exit}}')
[[ -n "$node_interface" ]] || die "expected IP is not configured: $EXPECTED_IP"

{
    printf 'captured_at=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
    printf 'hostname=%s\n' "$actual_hostname"
    printf 'expected_ip=%s\n' "$EXPECTED_IP"
    printf 'interface=%s\n' "$node_interface"
    printf 'machine_id_bytes=%s\n' "$(wc -c </etc/machine-id 2>/dev/null || printf 0)"
    printf 'ssh_private_key_count=%s\n' \
        "$(find /etc/ssh -maxdepth 1 -type f -name 'ssh_host_*_key' | wc -l)"
    printf 'product_uuid=%s\n' "$(cat /sys/class/dmi/id/product_uuid 2>/dev/null || printf UNKNOWN)"
} >"$EVIDENCE_DIR/identity-before.env"

ip -o -4 addr show >"$EVIDENCE_DIR/ip-before.txt"
ip route show >"$EVIDENCE_DIR/routes-before.txt"
findmnt >"$EVIDENCE_DIR/findmnt-before.txt"
df -hT >"$EVIDENCE_DIR/df-before.txt"
systemctl --failed --no-pager >"$EVIDENCE_DIR/failed-units-before.txt" || true
ss -lntup >"$EVIDENCE_DIR/listeners-before.txt"
for pub in /etc/ssh/ssh_host_*_key.pub; do
    [[ -f "$pub" ]] && ssh-keygen -lf "$pub"
done >"$EVIDENCE_DIR/ssh-host-fingerprints-before.txt"

nodes_manifest="$BUNDLE_ROOT/roles/$ROLE/manifests/nodes.tsv"
manifest_line=$(awk -F'\t' -v node="$NODE_KEY" 'NR > 1 && $1 == node {print; exit}' "$nodes_manifest")
[[ -n "$manifest_line" ]] || die "node absent from manifest: $NODE_KEY"
manifest_ip=$(awk -F'\t' '{print $2}' <<<"$manifest_line")
manifest_hostname=$(awk -F'\t' '{print $3}' <<<"$manifest_line")
[[ "$manifest_ip" == "$EXPECTED_IP" ]] || die "manifest IP mismatch: $manifest_ip"
[[ "$(printf '%s' "$manifest_hostname" | tr '[:upper:]' '[:lower:]')" == "$expected_hostname_lc" ]] ||
    die "manifest hostname mismatch: $manifest_hostname"

if [[ "$APPLY_POST_CLONE" == "1" ]]; then
    env RUN_ID="$RUN_ID" BACKUP_ROOT="$EVIDENCE_ROOT" \
        "$BUNDLE_ROOT/common/scripts/post-clone.sh" \
        --role "$ROLE" \
        --bundle-root "$BUNDLE_ROOT" \
        --node "$NODE_KEY" \
        --expected-ip "$EXPECTED_IP"
    post_clone_execution=PERFORMED
else
    env RUN_ID="$RUN_ID" BACKUP_ROOT="$EVIDENCE_ROOT" \
        "$BUNDLE_ROOT/common/scripts/post-clone.sh" \
        --role "$ROLE" \
        --bundle-root "$BUNDLE_ROOT" \
        --node "$NODE_KEY" \
        --expected-ip "$EXPECTED_IP" \
        --dry-run
    post_clone_execution=DRY_RUN_ONLY
fi

if [[ "$APPLY_POST_CLONE" == "1" ]]; then
    [[ -s /etc/machine-id ]] || die 'machine-id was not regenerated'
    [[ -s /etc/ssh/ssh_host_ed25519_key ]] || die 'SSH ED25519 host key was not regenerated'
    [[ -r /etc/zhct/node.env ]] || die '/etc/zhct/node.env missing'
    selected_role=$ROLE
    selected_node_key=$NODE_KEY
    selected_ip=$EXPECTED_IP
    # shellcheck disable=SC1091
    source /etc/zhct/node.env
    [[ "${ROLE:-}" == "$selected_role" ]] || die 'node.env role mismatch'
    [[ "${NODE_KEY:-}" == "$selected_node_key" ]] || die 'node.env node key mismatch'
    [[ "${NODE_IP:-}" == "$selected_ip" ]] || die 'node.env IP mismatch'
fi

units_manifest="$BUNDLE_ROOT/roles/$ROLE/manifests/systemd-units.tsv"
while IFS=$'\t' read -r unit _template_state _node_states _purpose; do
    [[ "$unit" == "unit" || -z "$unit" ]] && continue
    enabled=$(systemctl is-enabled "$unit" 2>/dev/null || true)
    active=$(systemctl is-active "$unit" 2>/dev/null || true)
    printf '%s\t%s\t%s\n' "$unit" "$enabled" "$active"
    case "$enabled" in disabled|masked) ;; *) die "unit unexpectedly enabled: $unit ($enabled)" ;; esac
    [[ "$active" == "inactive" ]] || die "unit is not inactive: $unit ($active)"
done <"$units_manifest" | tee "$EVIDENCE_DIR/units-after.tsv"

ip -o -4 addr show >"$EVIDENCE_DIR/ip-after.txt"
ss -lntup >"$EVIDENCE_DIR/listeners-after.txt"
for vip in 10.2.203.115 10.2.203.116 10.2.203.117; do
    if ip -o -4 addr show | grep -Fq "$vip/"; then
        die "VIP must remain absent during clone handoff: $vip"
    fi
done

{
    printf 'run_id=%s\n' "$RUN_ID"
    printf 'role=%s\n' "$ROLE"
    printf 'node_key=%s\n' "$NODE_KEY"
    printf 'expected_ip=%s\n' "$EXPECTED_IP"
    printf 'expected_hostname=%s\n' "$EXPECTED_HOSTNAME"
    printf 'post_clone_execution=%s\n' "$post_clone_execution"
    printf 'clone_handoff_audit=PASS\n'
    printf 'services_activated=NO\n'
    printf 'vip_announced=NO\n'
} >"$EVIDENCE_DIR/result.env"

(cd "$EVIDENCE_DIR" && sha256sum ./* >evidence-index.sha256)
printf 'Q100_140_CLONE_HANDOFF_AUDIT_PASS\n'
printf 'EVIDENCE_DIR=%s\n' "$EVIDENCE_DIR"
