#!/usr/bin/env bash

set -Eeuo pipefail

SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
COMMON_DIR=$(cd "$SCRIPT_DIR/.." && pwd)
# shellcheck source=../lib/runtime.sh
source "$COMMON_DIR/lib/runtime.sh"

usage() {
    cat <<'USAGE'
Usage: role-action.sh ACTION --role ROLE [options]

Actions: install, verify, seal, post-clone, activate, rollback
Options:
  --role ROLE
  --bundle-root PATH
  --node NODE_KEY              Required for post-clone
  --expected-ip IPV4           Required for post-clone
  --backup-id ID               Required for rollback
  --dry-run
  --confirm-seal               Required for a live seal
USAGE
}

ACTION=${1:-}
[[ -n "$ACTION" ]] || { usage; exit 2; }
shift

ROLE=
NODE_KEY=
EXPECTED_IP=
BACKUP_ID=
CONFIRM_SEAL=0

while (($#)); do
    case "$1" in
        --role) ROLE=${2:-}; shift 2 ;;
        --bundle-root) BUNDLE_ROOT=${2:-}; shift 2 ;;
        --node) NODE_KEY=${2:-}; shift 2 ;;
        --expected-ip) EXPECTED_IP=${2:-}; shift 2 ;;
        --backup-id) BACKUP_ID=${2:-}; shift 2 ;;
        --dry-run) DRY_RUN=1; shift ;;
        --confirm-seal) CONFIRM_SEAL=1; shift ;;
        -h|--help) usage; exit 0 ;;
        *) die "unknown argument: $1" ;;
    esac
done

assert_role "$ROLE"
case "$ACTION" in
    install|verify|seal|post-clone|activate|rollback) ;;
    *) die "unknown action: $ACTION" ;;
esac

ROLE_ROOT="$BUNDLE_ROOT/roles/$ROLE"
ROLE_ENV="$ROLE_ROOT/role.env"
HOOKS="$ROLE_ROOT/hooks.sh"
DIGEST_MANIFEST="$ROLE_ROOT/manifests/asset-digests.tsv"
UNITS_MANIFEST="$ROLE_ROOT/manifests/systemd-units.tsv"
export ACTION ROLE ROLE_ROOT ROLE_ENV HOOKS DIGEST_MANIFEST UNITS_MANIFEST
export BUNDLE_ROOT DRY_RUN NODE_KEY EXPECTED_IP BACKUP_ID CONFIRM_SEAL RUN_ID BACKUP_ROOT

require_file "$ROLE_ENV"
require_file "$HOOKS"
# shellcheck disable=SC1090
source "$ROLE_ENV"
# shellcheck disable=SC1090
source "$HOOKS"
[[ "${ROLE_ID:-}" == "$ROLE" ]] || die "role.env ROLE_ID does not match --role"
if [[ "${ZHCT_STATIC_VALIDATE:-0}" == "1" ]]; then
    TEMPLATE_ROOT="$ROLE_ROOT/templates"
fi
require_root

case "$ACTION" in
    install)
        verify_locked_assets "$DIGEST_MANIFEST"
        role_preflight
        role_install
        ;;
    verify)
        role_verify
        ;;
    seal)
        if [[ "$DRY_RUN" != "1" && "$CONFIRM_SEAL" != "1" ]]; then
            die "live seal requires --confirm-seal"
        fi
        stop_disable_units "$UNITS_MANIFEST"
        role_seal
        clean_common_clone_identity
        ;;
    post-clone)
        require_value node "$NODE_KEY"
        require_value expected_ip "$EXPECTED_IP"
        assert_expected_ip "$EXPECTED_IP"
        NODE_IP=$EXPECTED_IP
        NODE_NAME=$(hostname -s)
        assert_node_key "$ROLE_ROOT/manifests/nodes.tsv" "$NODE_KEY"
        EXPECTED_HOSTNAME=$(awk -F'\t' -v node="$NODE_KEY" 'NR > 1 && $1 == node {print $3; exit}' "$ROLE_ROOT/manifests/nodes.tsv")
        require_value expected_hostname "$EXPECTED_HOSTNAME"
        if [[ "${ZHCT_STATIC_VALIDATE:-0}" != "1" ]]; then
            actual_hostname_lc=$(printf '%s' "$NODE_NAME" | tr '[:upper:]' '[:lower:]')
            expected_hostname_lc=$(printf '%s' "$EXPECTED_HOSTNAME" | tr '[:upper:]' '[:lower:]')
            [[ "$actual_hostname_lc" == "$expected_hostname_lc" ]] || die "hostname $NODE_NAME does not match expected $EXPECTED_HOSTNAME"
        fi
        if [[ "${ZHCT_STATIC_VALIDATE:-0}" == "1" ]]; then
            NODE_INTERFACE=static0
        else
            NODE_INTERFACE=$(ip -o -4 addr show scope global | awk -v expected="$EXPECTED_IP" '{split($4,a,"/"); if (a[1] == expected) {print $2; exit}}')
            require_value node_interface "$NODE_INTERFACE"
        fi
        export NODE_IP NODE_NAME NODE_INTERFACE EXPECTED_HOSTNAME
        regenerate_common_clone_identity
        role_post_clone
        write_node_identity
        ;;
    activate)
        if [[ -f /etc/zhct/node.env ]]; then
            # shellcheck disable=SC1091
            source /etc/zhct/node.env
        elif [[ "$DRY_RUN" != "1" || -z "$NODE_KEY" ]]; then
            die 'node identity is not configured; run post-clone first'
        fi
        require_value node "$NODE_KEY"
        assert_node_key "$ROLE_ROOT/manifests/nodes.tsv" "$NODE_KEY"
        role_activate
        apply_node_unit_policy "$UNITS_MANIFEST" "$NODE_KEY"
        ;;
    rollback)
        require_value backup_id "$BACKUP_ID"
        role_rollback
        restore_backup "$BACKUP_ID"
        run systemctl daemon-reload
        ;;
esac

log "$ACTION completed for role=$ROLE dry_run=$DRY_RUN run_id=$RUN_ID"
