#!/usr/bin/env bash

set -Eeuo pipefail
export LANG=C
umask 077

EXPECTED_HOSTNAME="sqjcDB01"
EXPECTED_IPV4="10.2.208.106"
EXPECTED_OS="openEuler release 24.03 (LTS-SP1)"
EXPECTED_ARCH="x86_64"
BACKUP_ROOT="/var/backups/zhct-q060"
BUNDLE_ROOT="/opt/zhct-deploy"
PACKAGE_ROOT="${BUNDLE_ROOT}/packages"
MYSQL56="mysql-5.6.51-linux-glibc2.12-x86_64.tar.gz"
MYSQL80="mysql-8.0.46-linux-glibc2.28-x86_64.tar.xz"
MYSQL56_TOP="mysql-5.6.51-linux-glibc2.12-x86_64"
MYSQL80_TOP="mysql-8.0.46-linux-glibc2.28-x86_64"
MYSQL56_FPR="A4A9406876FCBD3C456770C88C718D3B5072E1F5"
MYSQL80_FPR="BCA43417C3B485DD128EC6D4B7B3B788A8D3785C"
TARGET_PORTS_RE=':(3306|3308|9201|9202)([[:space:]]|$)'

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

require_command() {
  command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
}

key_fingerprint() {
  local key_file="$1"
  GNUPGHOME="${GNUPG_HOME}" gpg --batch --with-colons \
    --import-options show-only --import "${key_file}" |
    awk -F: '$1 == "fpr" {print $10; exit}'
}

verify_signature() {
  local archive="$1"
  local signature="$2"
  local expected_fingerprint="$3"
  local status_file="$4"
  set +e
  GNUPGHOME="${GNUPG_HOME}" gpg --batch --status-fd 1 \
    --verify "${signature}" "${archive}" >"${status_file}" 2>&1
  local rc=$?
  set -e
  [[ "${rc}" -eq 0 ]] || die "GPG verification failed: ${archive}"
  grep -Eq "^[[]GNUPG:[]] VALIDSIG ${expected_fingerprint} " "${status_file}" || \
    die "signature fingerprint mismatch: ${archive}"
}

capture_zero_runtime() {
  local prefix="$1"
  ss -lntup >"${prefix}-listeners.txt"
  local target_listeners
  target_listeners="$(ss -lntupH | grep -E "${TARGET_PORTS_RE}" || true)"
  [[ -z "${target_listeners}" ]] || die "a MySQL target listener exists"
  local target_processes
  target_processes="$(ps -eo pid,ppid,user,group,cmd |
    grep -Ei '[m]ysqld|[m]ysqlrouter|[m]ysqladmin.*ping' || true)"
  printf '%s\n' "${target_processes}" >"${prefix}-mysql-processes.txt"
  [[ -z "${target_processes}" ]] || die "a MySQL target process exists"
}

verify_elf_set() {
  local root="$1"
  local label="$2"
  local output="$3"
  local -a candidates=(
    "${root}/bin/mysqld"
    "${root}/bin/mysql"
    "${root}/bin/mysqladmin"
    "${root}/bin/mysqldump"
  )
  while IFS= read -r plugin; do
    candidates+=("${plugin}")
  done < <(find "${root}/lib/plugin" -maxdepth 1 -type f -name 'semisync_*.so' | sort)
  [[ "${#candidates[@]}" -gt 4 ]] || die "${label} has no semisync plugin"
  : >"${output}"
  for candidate in "${candidates[@]}"; do
    [[ -f "${candidate}" ]] || die "required runtime file is missing: ${candidate}"
    printf '### %s\n' "${candidate#${root}/}" >>"${output}"
    file "${candidate}" >>"${output}"
    readelf -d "${candidate}" >>"${output}" 2>&1
    ldd "${candidate}" >>"${output}" 2>&1
  done
  ! grep -Eq '(^|[[:space:]])not found($|[[:space:]])' "${output}" || \
    die "${label} has an unresolved runtime library"
  "${root}/bin/mysqld" --no-defaults --version \
    >"${EVIDENCE_DIR}/${label}-mysqld-version.txt" 2>&1
  "${root}/bin/mysql" --no-defaults --version \
    >"${EVIDENCE_DIR}/${label}-mysql-version.txt" 2>&1
}

[[ "${EUID}" -eq 0 ]] || die "must run as root"
[[ $# -eq 1 ]] || die "usage: $0 <q060-baseline-run-id>"
RUN_ID="$1"
[[ "${RUN_ID}" =~ ^[0-9]{8}T[0-9]{6}[+-][0-9]{4}$ ]] || die "invalid run id"
EVIDENCE_DIR="${BACKUP_ROOT}/${RUN_ID}"
VERIFY_ROOT="/var/tmp/zhct-q060-verify-${RUN_ID}"
GNUPG_HOME="${VERIFY_ROOT}/gnupg"
HISTORY_BEFORE="${EVIDENCE_DIR}/verify-dnf-history-before.txt"
HISTORY_AFTER="${EVIDENCE_DIR}/verify-dnf-history-after.txt"

for command_name in gpg sha256sum tar file readelf ldd ss ps dnf find awk grep; do
  require_command "${command_name}"
done
[[ "$(hostname -s)" == "${EXPECTED_HOSTNAME}" ]] || die "hostname mismatch"
[[ "$(uname -m)" == "${EXPECTED_ARCH}" ]] || die "architecture mismatch"
[[ "$(tr -d '\r' </etc/openEuler-release)" == "${EXPECTED_OS}" ]] || die "OS mismatch"
ip -o -4 addr show scope global | awk '{print $4}' | cut -d/ -f1 |
  grep -Fxq "${EXPECTED_IPV4}" || die "expected IPv4 is not configured"
[[ ! -e "${VERIFY_ROOT}" ]] || die "verification root already exists: ${VERIFY_ROOT}"
for required in \
  "${EVIDENCE_DIR}/source-assets-result.env" \
  "${EVIDENCE_DIR}/mysql-source-assets.sha256" \
  "${EVIDENCE_DIR}/mysql56-release-key.asc" \
  "${EVIDENCE_DIR}/mysql80-release-key.asc" \
  "${EVIDENCE_DIR}/mysql-release-keys.sha256" \
  "${EVIDENCE_DIR}/rpm-closure-result.env" \
  "${PACKAGE_ROOT}/rpms.sha256" \
  "${PACKAGE_ROOT}/${MYSQL56}" \
  "${PACKAGE_ROOT}/${MYSQL56}.asc" \
  "${PACKAGE_ROOT}/${MYSQL80}" \
  "${PACKAGE_ROOT}/${MYSQL80}.asc"; do
  [[ -s "${required}" ]] || die "required asset/evidence is missing: ${required}"
done
grep -Fxq 'source_asset_download=PASS' "${EVIDENCE_DIR}/source-assets-result.env" || \
  die "source asset gate is not PASS"
grep -Fxq 'fresh_installroot_dependency_resolution=PASS_ASSUMENO' \
  "${EVIDENCE_DIR}/rpm-closure-result.env" || die "fresh-install closure gate is not PASS"
grep -Fxq 'install_execution=NOT_PERFORMED' "${EVIDENCE_DIR}/rpm-closure-result.env" || \
  die "closure phase records an installation"

capture_zero_runtime "${EVIDENCE_DIR}/verify-before"
dnf history list >"${HISTORY_BEFORE}" 2>&1
(
  cd "${PACKAGE_ROOT}"
  sha256sum -c "${EVIDENCE_DIR}/mysql-source-assets.sha256"
  sha256sum -c rpms.sha256
) >"${EVIDENCE_DIR}/verify-sha256.txt"
sha256sum -c "${EVIDENCE_DIR}/mysql-release-keys.sha256" \
  >"${EVIDENCE_DIR}/verify-key-sha256.txt"

install -d -m 0700 "${GNUPG_HOME}" "${VERIFY_ROOT}/mysql56" "${VERIFY_ROOT}/mysql80"
[[ "$(key_fingerprint "${EVIDENCE_DIR}/mysql56-release-key.asc")" == \
  "${MYSQL56_FPR}" ]] || die "MySQL 5.6 key fingerprint mismatch"
[[ "$(key_fingerprint "${EVIDENCE_DIR}/mysql80-release-key.asc")" == \
  "${MYSQL80_FPR}" ]] || die "MySQL 8.0 key fingerprint mismatch"
GNUPGHOME="${GNUPG_HOME}" gpg --batch --import \
  "${EVIDENCE_DIR}/mysql56-release-key.asc" \
  "${EVIDENCE_DIR}/mysql80-release-key.asc" \
  >"${EVIDENCE_DIR}/verify-gpg-import.txt" 2>&1
verify_signature "${PACKAGE_ROOT}/${MYSQL56}" "${PACKAGE_ROOT}/${MYSQL56}.asc" \
  "${MYSQL56_FPR}" "${EVIDENCE_DIR}/verify-mysql56-gpg-status.txt"
verify_signature "${PACKAGE_ROOT}/${MYSQL80}" "${PACKAGE_ROOT}/${MYSQL80}.asc" \
  "${MYSQL80_FPR}" "${EVIDENCE_DIR}/verify-mysql80-gpg-status.txt"

tar -xzf "${PACKAGE_ROOT}/${MYSQL56}" -C "${VERIFY_ROOT}/mysql56"
tar -xJf "${PACKAGE_ROOT}/${MYSQL80}" -C "${VERIFY_ROOT}/mysql80"
[[ -d "${VERIFY_ROOT}/mysql56/${MYSQL56_TOP}" ]] || die "MySQL 5.6 top directory mismatch"
[[ -d "${VERIFY_ROOT}/mysql80/${MYSQL80_TOP}" ]] || die "MySQL 8.0 top directory mismatch"
verify_elf_set "${VERIFY_ROOT}/mysql56/${MYSQL56_TOP}" mysql56 \
  "${EVIDENCE_DIR}/mysql56-runtime.tsv"
verify_elf_set "${VERIFY_ROOT}/mysql80/${MYSQL80_TOP}" mysql80 \
  "${EVIDENCE_DIR}/mysql80-runtime.tsv"

rm -rf "${VERIFY_ROOT}"
dnf history list >"${HISTORY_AFTER}" 2>&1
cmp -s "${HISTORY_BEFORE}" "${HISTORY_AFTER}" || \
  die "DNF history changed during temporary verification"
capture_zero_runtime "${EVIDENCE_DIR}/verify-after"
for formal_path in \
  /opt/mysql/5.6.51 /opt/mysql/8.0.46 \
  /etc/my56.cnf /etc/my80.cnf \
  /etc/systemd/system/mysql56.service /etc/systemd/system/mysql80.service; do
  [[ ! -e "${formal_path}" && ! -L "${formal_path}" ]] || \
    die "formal MySQL path exists after temporary verification: ${formal_path}"
done

{
  printf 'captured_at=%s\n' "$(date --iso-8601=seconds)"
  printf 'source_sha256_recheck=PASS\n'
  printf 'gpg_recheck=PASS\n'
  printf 'rpm_sha256_recheck=PASS\n'
  printf 'fresh_installroot_dependency_resolution=PASS_ASSUMENO\n'
  printf 'mysql56_runtime_ldd=PASS\n'
  printf 'mysql80_runtime_ldd=PASS\n'
  printf 'temporary_extract_cleanup=PASS\n'
  printf 'formal_runtime_paths=ABSENT\n'
  printf 'install_execution=NOT_PERFORMED\n'
  printf 'post_clone_execution=NOT_PERFORMED\n'
} >"${EVIDENCE_DIR}/locked-assets-verification-result.env"
chmod 0600 "${EVIDENCE_DIR}"/verify-* "${EVIDENCE_DIR}"/*-runtime.tsv \
  "${EVIDENCE_DIR}"/*-version.txt "${EVIDENCE_DIR}/locked-assets-verification-result.env"

printf 'Q060_LOCKED_ASSETS_VERIFY_OK\n'
cat "${EVIDENCE_DIR}/locked-assets-verification-result.env"
