#!/usr/bin/env bash
set -Eeuo pipefail

EXPECTED_HOSTNAME=sqjcDB01
EXPECTED_IP=10.2.208.106
RUNTIME=/opt/zhct-deploy/common/lib/runtime.sh
EXPECTED_RUNTIME_OLD_SHA256=4c06e4ad325de7241607b06d93c99c2f35f3422bfcbdbeeb43b530f2053f4bb6
EXPECTED_RUNTIME_NEW_SHA256=2d9acb64fca36ff23061cf8da0dfb25c384f21fd00b39e750c4afd3d30350dc0
HOOKS=/opt/zhct-deploy/roles/mysql/hooks.sh
EXPECTED_HOOKS_OLD_SHA256=56d68209ab3dc5da3f4ab2c0b679329fc27892749de5b4f65ff9b72e1ccfd8fd
EXPECTED_HOOKS_NEW_SHA256=8dd8ad31318a6111fdaf9e0d346dad12fa5da5e629b2dfd429a2e66a3e5e016a
ARCHIVE=/root/sanquan-mysql-q121-122-offline-package-v3-20260729.tar.gz
EXPECTED_ARCHIVE_SHA256=a7c7b581387163ce0aae3aea34823b41f00de4d1a7e1c0de9a6dae63d7c9ef9c
EXTRACT_DIR=/root/sanquan-mysql-q121-122-offline-package-v3-20260729-v6-run
RUN_ID="q121-framework-repair-v6-$(date '+%Y%m%dT%H%M%S%z')"
EVIDENCE_DIR="/var/backups/zhct-q121-122/$RUN_ID"
AUDIT_LOG="$EVIDENCE_DIR/manual-wrapper.log"
RESULT_FILE="$EVIDENCE_DIR/manual-wrapper-result.env"

umask 077
install -d -m 0700 -o root -g root "$EVIDENCE_DIR"
exec > >(tee -a "$AUDIT_LOG") 2>&1

finish() {
  local rc=$?
  trap - EXIT
  {
    printf 'finished_at=%s\n' "$(date --iso-8601=seconds)"
    printf 'target_hostname=%s\n' "$EXPECTED_HOSTNAME"
    printf 'target_ip=%s\n' "$EXPECTED_IP"
    printf 'action=runtime_etc_mysql_hook_repair_q121_execute\n'
    printf 'repair_persists_on_execute_failure=yes\n'
    printf 'exit_code=%s\n' "$rc"
  } >"$RESULT_FILE"
  printf 'audit_log=%s\n' "$AUDIT_LOG"
  printf 'result_file=%s\n' "$RESULT_FILE"
  printf 'END MYSQL01_Q121_FRAMEWORK_REPAIR_EXECUTE_V6 RC=%s\n' "$rc"
  exit "$rc"
}
trap finish EXIT

printf 'BEGIN MYSQL01_Q121_FRAMEWORK_REPAIR_EXECUTE_V6\n'
printf 'started_at=%s\n' "$(date --iso-8601=seconds)"
printf 'expected_hostname=%s\nexpected_ip=%s\n' "$EXPECTED_HOSTNAME" "$EXPECTED_IP"

[[ $EUID -eq 0 ]] || {
  echo 'ERROR: root is required'
  exit 1
}

actual_hostname=$(hostname -s)
[[ ${actual_hostname,,} == ${EXPECTED_HOSTNAME,,} ]] || {
  printf 'ERROR: hostname mismatch: expected=%s actual=%s\n' \
    "$EXPECTED_HOSTNAME" "$actual_hostname"
  exit 1
}

ip -o -4 addr show scope global | awk -v expected="$EXPECTED_IP" '
  {
    split($4, address, "/")
    if (address[1] == expected) found=1
  }
  END {exit !found}
' || {
  printf 'ERROR: expected global IPv4 is absent: %s\n' "$EXPECTED_IP"
  exit 1
}

[[ -f "$RUNTIME" && ! -L "$RUNTIME" ]] || {
  echo 'ERROR: locked runtime is missing or is a symlink'
  exit 1
}
[[ -f "$HOOKS" && ! -L "$HOOKS" ]] || {
  echo 'ERROR: locked MySQL hooks file is missing or is a symlink'
  exit 1
}
command -v runuser >/dev/null || {
  echo 'ERROR: runuser is required'
  exit 1
}
[[ -d /etc && ! -L /etc ]] || {
  echo 'ERROR: /etc is missing or is a symlink'
  exit 1
}
[[ $(stat -c '%U:%G' /etc) == root:root ]] || {
  echo 'ERROR: unexpected /etc owner'
  exit 1
}
case "$(stat -c '%a' /etc)" in
  750|755) ;;
  *)
    echo 'ERROR: unexpected /etc mode'
    exit 1
    ;;
esac
matchpathcon -V /etc

for config in /etc/my56.cnf /etc/my80.cnf; do
  [[ -f "$config" && ! -L "$config" ]] || {
    printf 'ERROR: config missing or symlink: %s\n' "$config"
    exit 1
  }
  [[ $(stat -c '%a %U:%G' "$config") == '640 root:mysql' ]] || {
    printf 'ERROR: unexpected config metadata: %s\n' "$config"
    exit 1
  }
  matchpathcon -V "$config"
done

stat -c 'before mode=%a owner=%U:%G label=%C path=%n' \
  /etc "$RUNTIME" "$HOOKS" /etc/my56.cnf /etc/my80.cnf |
  tee "$EVIDENCE_DIR/metadata-before.txt"
sha256sum "$RUNTIME" | tee "$EVIDENCE_DIR/runtime-before.sha256"
sha256sum "$HOOKS" | tee "$EVIDENCE_DIR/mysql-hooks-before.sha256"
install -m 0600 -o root -g root "$RUNTIME" "$EVIDENCE_DIR/runtime-before.sh"
install -m 0600 -o root -g root "$HOOKS" "$EVIDENCE_DIR/mysql-hooks-before.sh"

runtime_sha256=$(sha256sum "$RUNTIME" | awk '{print $1}')
case "$runtime_sha256" in
  "$EXPECTED_RUNTIME_OLD_SHA256")
    patched_runtime=$(mktemp)
    awk '
      NR == 90 {
        print "    local destination_parent"
        print "    destination_parent=$(dirname \"$destination\")"
        print "    if [[ -d \"$destination_parent\" ]]; then"
        print "        [[ ! -L \"$destination_parent\" ]] || die \"destination parent is a symlink: $destination_parent\""
        print "    else"
        print "        ensure_dir \"$destination_parent\" 0750 root root"
        print "    fi"
        next
      }
      {print}
    ' "$RUNTIME" >"$patched_runtime"
    bash -n "$patched_runtime"
    [[ $(sha256sum "$patched_runtime" | awk '{print $1}') == "$EXPECTED_RUNTIME_NEW_SHA256" ]] || {
      echo 'ERROR: patched runtime SHA256 mismatch'
      exit 1
    }
    install -m 0644 -o root -g root "$patched_runtime" "$RUNTIME"
    rm -f "$patched_runtime"
    restorecon -v "$RUNTIME"
    printf 'runtime_repair=APPLIED\n'
    ;;
  "$EXPECTED_RUNTIME_NEW_SHA256")
    printf 'runtime_repair=ALREADY_APPLIED\n'
    ;;
  *)
    printf 'ERROR: unexpected runtime SHA256: %s\n' "$runtime_sha256"
    exit 1
    ;;
esac

chmod 0755 /etc
restorecon -v /etc
[[ $(stat -c '%a %U:%G' /etc) == '755 root:root' ]] || {
  echo 'ERROR: /etc repair verification failed'
  exit 1
}
[[ $(sha256sum "$RUNTIME" | awk '{print $1}') == "$EXPECTED_RUNTIME_NEW_SHA256" ]] || {
  echo 'ERROR: installed runtime SHA256 verification failed'
  exit 1
}
bash -n "$RUNTIME"

runuser -u mysql -- test -x /etc
runuser -u mysql -- test -r /etc/my56.cnf
runuser -u mysql -- test -r /etc/my80.cnf
printf 'mysql_config_access=PASS\n'

hooks_sha256=$(sha256sum "$HOOKS" | awk '{print $1}')
case "$hooks_sha256" in
  "$EXPECTED_HOOKS_OLD_SHA256")
    patched_hooks=$(mktemp)
    awk '
      NR == 107 {
        print "    runuser -u mysql -- /opt/mysql/5.6.51/bin/mysqld --defaults-file=/etc/my56.cnf --verbose --help >/dev/null"
        next
      }
      NR == 108 {
        print "    runuser -u mysql -- /opt/mysql/8.0.46/bin/mysqld --defaults-file=/etc/my80.cnf --validate-config"
        next
      }
      {print}
    ' "$HOOKS" >"$patched_hooks"
    bash -n "$patched_hooks"
    [[ $(sha256sum "$patched_hooks" | awk '{print $1}') == "$EXPECTED_HOOKS_NEW_SHA256" ]] || {
      echo 'ERROR: patched MySQL hooks SHA256 mismatch'
      exit 1
    }
    install -m 0750 -o root -g root "$patched_hooks" "$HOOKS"
    rm -f "$patched_hooks"
    restorecon -v "$HOOKS"
    printf 'mysql_hooks_repair=APPLIED\n'
    ;;
  "$EXPECTED_HOOKS_NEW_SHA256")
    printf 'mysql_hooks_repair=ALREADY_APPLIED\n'
    ;;
  *)
    printf 'ERROR: unexpected MySQL hooks SHA256: %s\n' "$hooks_sha256"
    exit 1
    ;;
esac
[[ $(sha256sum "$HOOKS" | awk '{print $1}') == "$EXPECTED_HOOKS_NEW_SHA256" ]] || {
  echo 'ERROR: installed MySQL hooks SHA256 verification failed'
  exit 1
}
bash -n "$HOOKS"
grep -Fq 'runuser -u mysql -- /opt/mysql/5.6.51/bin/mysqld' "$HOOKS"
grep -Fq 'runuser -u mysql -- /opt/mysql/8.0.46/bin/mysqld' "$HOOKS"
printf 'mysql_hooks_identity_gate=PASS\n'

stat -c 'after mode=%a owner=%U:%G label=%C path=%n' \
  /etc "$RUNTIME" "$HOOKS" /etc/my56.cnf /etc/my80.cnf |
  tee "$EVIDENCE_DIR/metadata-after.txt"
sha256sum "$RUNTIME" | tee "$EVIDENCE_DIR/runtime-after.sha256"
sha256sum "$HOOKS" | tee "$EVIDENCE_DIR/mysql-hooks-after.sha256"

[[ -f "$ARCHIVE" ]] || {
  echo 'ERROR: v3 Q-121 archive is missing'
  exit 1
}
actual_archive_sha256=$(sha256sum "$ARCHIVE" | awk '{print $1}')
[[ "$actual_archive_sha256" == "$EXPECTED_ARCHIVE_SHA256" ]] || {
  printf 'ERROR: archive SHA256 mismatch: expected=%s actual=%s\n' \
    "$EXPECTED_ARCHIVE_SHA256" "$actual_archive_sha256"
  exit 1
}
printf 'archive_sha256=%s\narchive_sha256_check=PASS\n' "$actual_archive_sha256"

[[ ! -e "$EXTRACT_DIR" ]] || {
  printf 'ERROR: extract target already exists: %s\n' "$EXTRACT_DIR"
  exit 1
}
install -d -m 0700 -o root -g root "$EXTRACT_DIR"
tar --extract --gzip --file="$ARCHIVE" --directory="$EXTRACT_DIR" --no-same-owner

cd "$EXTRACT_DIR"
sha256sum -c SHA256SUMS
bash -n q121-122-mysql.sh
bash q121-122-mysql.sh --static-validate
bash q121-122-mysql.sh q121 execute

[[ $(stat -c '%a %U:%G' /etc) == '755 root:root' ]] || {
  echo 'ERROR: /etc mode regressed after Q-121 execute'
  exit 1
}
[[ $(sha256sum "$RUNTIME" | awk '{print $1}') == "$EXPECTED_RUNTIME_NEW_SHA256" ]] || {
  echo 'ERROR: runtime regressed after Q-121 execute'
  exit 1
}
[[ $(sha256sum "$HOOKS" | awk '{print $1}') == "$EXPECTED_HOOKS_NEW_SHA256" ]] || {
  echo 'ERROR: MySQL hooks regressed after Q-121 execute'
  exit 1
}
runuser -u mysql -- test -r /etc/my56.cnf
runuser -u mysql -- test -r /etc/my80.cnf
printf 'MYSQL01_Q121_FRAMEWORK_REPAIR_EXECUTE_V6_PASS\n'
