#!/usr/bin/env bash

set -Eeuo pipefail
export LANG=C
umask 077

EXPECTED_HOSTNAME="sqjcmqtt01"
EXPECTED_IPV4="10.2.203.108"
RUN_ID=${1:-}
Q080_RUN_ID=${2:-}
CONFIRM=${3:-}
EVIDENCE_DIR="/var/backups/zhct-q081/evidence/${RUN_ID}"
Q080_EVIDENCE="/root/q080-evidence/${Q080_RUN_ID}"
Q080_BACKUP="/var/backups/zhct-q080/${Q080_RUN_ID}"

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

verify_install_only() {
  local history=$1
  grep -Eiq '^[[:space:]]*(Upgrade|Downgrade|Erase|Remove|Reinstall|Obsolete)[[:space:]]' "${history}" &&
    die "transaction contains a non-install action"
  grep -Eiq '^[[:space:]]*Install[[:space:]]' "${history}" ||
    die "transaction does not contain an Install action"
}

[[ "${EUID}" -eq 0 ]] || die "must run as root"
[[ "${RUN_ID}" =~ ^q081-[0-9]{8}T[0-9]{6}[+]0800$ ]] || die "invalid Q081 run id"
[[ "${Q080_RUN_ID}" =~ ^q080-[0-9]{8}T[0-9]{6}[+]0800$ ]] || die "invalid Q080 run id"
[[ "${CONFIRM}" == "--execute-q081-rollback" ]] || die "explicit Q081 rollback flag is required"
[[ "$(hostname -s)" == "${EXPECTED_HOSTNAME}" ]] || die "hostname mismatch"
ip -o -4 addr show scope global | awk '{print $4}' | cut -d/ -f1 |
  grep -Fxq "${EXPECTED_IPV4}" || die "expected IPv4 is not configured"
[[ -s "${EVIDENCE_DIR}/dnf-closure-tx-id.txt" ]] || die "closure transaction id is missing"
[[ -s "${EVIDENCE_DIR}/dnf-emqx-tx-id.txt" ]] || die "EMQX transaction id is missing"
[[ -s "${Q080_EVIDENCE}/path-state-before.tsv" ]] || die "Q080 path baseline is missing"
[[ -s "${Q080_EVIDENCE}/account-state-before.tsv" ]] || die "Q080 account baseline is missing"
[[ ! -e "${EVIDENCE_DIR}/rollback-complete.marker" ]] || die "rollback already completed"
closure_tx=$(tr -d '[:space:]' <"${EVIDENCE_DIR}/dnf-closure-tx-id.txt")
emqx_tx=$(tr -d '[:space:]' <"${EVIDENCE_DIR}/dnf-emqx-tx-id.txt")
[[ "${closure_tx}" =~ ^[0-9]+$ && "${emqx_tx}" =~ ^[0-9]+$ ]] || die "invalid DNF transaction id"
((emqx_tx > closure_tx)) || die "DNF transaction order is invalid"

exec 9>/run/lock/zhct-q081-rollback.lock
flock -n 9 || die "another Q081 rollback is running"
systemctl disable --now emqx.service mosquitto.service >/dev/null 2>&1 || true
dnf history info "${emqx_tx}" >"${EVIDENCE_DIR}/rollback-emqx-history.txt" 2>&1
dnf history info "${closure_tx}" >"${EVIDENCE_DIR}/rollback-closure-history.txt" 2>&1
verify_install_only "${EVIDENCE_DIR}/rollback-emqx-history.txt"
verify_install_only "${EVIDENCE_DIR}/rollback-closure-history.txt"
grep -Eiq '(^|[[:space:]])emqx([-.[:space:]]|$)' "${EVIDENCE_DIR}/rollback-emqx-history.txt" ||
  die "EMQX transaction ownership check failed"
grep -Eiq '(^|[[:space:]])mosquitto([-.[:space:]]|$)' "${EVIDENCE_DIR}/rollback-closure-history.txt" ||
  die "closure transaction does not contain mosquitto"
grep -Eiq '(^|[[:space:]])libatomic([-.[:space:]]|$)' "${EVIDENCE_DIR}/rollback-closure-history.txt" ||
  die "closure transaction does not contain libatomic"

dnf --disablerepo='*' history undo -y "${emqx_tx}" >"${EVIDENCE_DIR}/rollback-emqx-undo.log" 2>&1
dnf --disablerepo='*' history undo -y "${closure_tx}" >"${EVIDENCE_DIR}/rollback-closure-undo.log" 2>&1
if [[ -f "${Q080_BACKUP}/system-config-before-q080.tar.gz" ]]; then
  tar --acls --xattrs --numeric-owner -xzpf "${Q080_BACKUP}/system-config-before-q080.tar.gz" -C /
fi
while IFS=$'\t' read -r path state; do
  [[ "${state}" == "ABSENT" ]] || continue
  case "${path}" in
    /etc/emqx|/var/lib/emqx|/var/log/emqx|/usr/lib/emqx|/etc/mosquitto)
      rm -rf -- "${path}"
      ;;
    /etc/systemd/system/emqx.service|/usr/lib/systemd/system/emqx.service|/etc/systemd/system/mosquitto.service|/usr/lib/systemd/system/mosquitto.service)
      rm -f -- "${path}"
      ;;
    *) die "unexpected path in Q080 baseline: ${path}" ;;
  esac
done <"${Q080_EVIDENCE}/path-state-before.tsv"
while IFS=$'\t' read -r account user_state group_state; do
  case "${account}" in emqx|mosquitto) ;; *) die "unexpected account in Q080 baseline: ${account}" ;; esac
  if [[ "${user_state}" == "ABSENT" ]]; then
    userdel "${account}" >/dev/null 2>&1 || true
  elif [[ "${user_state}" != "PRESENT" ]]; then
    die "invalid user baseline state for ${account}"
  fi
  if [[ "${group_state}" == "ABSENT" ]]; then
    groupdel "${account}" >/dev/null 2>&1 || true
  elif [[ "${group_state}" != "PRESENT" ]]; then
    die "invalid group baseline state for ${account}"
  fi
done <"${Q080_EVIDENCE}/account-state-before.tsv"
systemctl daemon-reload
systemctl disable --now emqx.service mosquitto.service >/dev/null 2>&1 || true
rpm -qa --qf '%{NAME}\t%{EPOCHNUM}:%{VERSION}-%{RELEASE}\t%{ARCH}\n' |
  sort >"${EVIDENCE_DIR}/rpm-after-rollback.tsv"
if [[ -s "${EVIDENCE_DIR}/rpm-before.tsv" ]]; then
  diff -u "${EVIDENCE_DIR}/rpm-before.tsv" "${EVIDENCE_DIR}/rpm-after-rollback.tsv" \
    >"${EVIDENCE_DIR}/rpm-rollback-diff.txt" || die "RPM set does not match the pre-install baseline"
fi
ss -lntup >"${EVIDENCE_DIR}/listeners-after-rollback.txt"
grep -Eq ':(1883|8083|18083|4370|5370|8883|8084)([[:space:]]|$)' \
  "${EVIDENCE_DIR}/listeners-after-rollback.txt" && die "EMQX target listener remains after rollback"
{
  printf 'run_id=%s\n' "${RUN_ID}"
  printf 'q080_run_id=%s\n' "${Q080_RUN_ID}"
  printf 'emqx_transaction_undo=PASS\n'
  printf 'closure_transaction_undo=PASS\n'
  printf 'undo_order=EMQX_THEN_CLOSURE\n'
  printf 'q080_backup_restore=PASS\n'
  printf 'account_group_baseline_restore=PASS\n'
  printf 'rpm_baseline_restore=PASS\n'
  printf 'target_listener_count=0\n'
  printf 'rollback_execution=PASS\n'
} >"${EVIDENCE_DIR}/rollback-result.env"
touch "${EVIDENCE_DIR}/rollback-complete.marker"
chmod -R go-rwx "${EVIDENCE_DIR}"
printf 'EMQX01_Q081_ROLLBACK_OK\n'
cat "${EVIDENCE_DIR}/rollback-result.env"
