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

PROGRAM=$(basename "$0")
SCRIPT_PATH=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/"$PROGRAM"
EXPECTED_ARCHIVE_NAME=sanquan-mysql-q122-evidence-q122-20260729T190725+0800.tar.gz
EXPECTED_ARCHIVE_PATH=/Users/lqt/Downloads/$EXPECTED_ARCHIVE_NAME
EXPECTED_ARCHIVE_SHA256=d20143cc9df939c33de3aa1b8e08ebd1b4d59b6a4322b32d68d83dc622b69808
EXPECTED_RUN=q122-20260729T190725+0800
MODE=${1:-}

usage() {
  printf '%s\n' \
    "Usage:" \
    "  $PROGRAM --static-validate" \
    "  $PROGRAM verify [$EXPECTED_ARCHIVE_PATH]"
}

die() {
  printf 'Q122_LOCAL_ACCEPTANCE_ERROR=%s\n' "$*" >&2
  return 1
}

need() {
  command -v "$1" >/dev/null 2>&1 || die "missing command: $1"
}

finish() {
  local rc=$?
  printf 'Q122_LOCAL_ACCEPTANCE_MODE=%s\n' "${MODE:-INVALID}"
  printf 'Q122_LOCAL_ACCEPTANCE_END=%s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z')"
  printf 'Q122_LOCAL_ACCEPTANCE_RC=%s\n' "$rc"
  printf 'Q122_REMOTE_EXECUTION=NOT_PERFORMED_BY_LOCAL_VERIFIER\n'
  printf 'Q221_REPLICATION=NOT_PERFORMED_Q221_GATE\n'
  printf 'Q261_BUSINESS_SQL=NOT_PERFORMED_Q261_GATE\n'
  if [[ "$rc" -eq 0 ]]; then
    printf 'Q122_LOCAL_ACCEPTANCE_JUDGMENT=PASS\n'
  else
    printf 'Q122_LOCAL_ACCEPTANCE_JUDGMENT=FAIL_CLOSED\n'
  fi
  printf 'END Q122_DOWNLOADED_EVIDENCE_LOCAL_ACCEPTANCE RC=%s\n' "$rc"
  return "$rc"
}
trap finish EXIT

printf 'BEGIN Q122_DOWNLOADED_EVIDENCE_LOCAL_ACCEPTANCE\n'
printf 'Q122_LOCAL_ACCEPTANCE_START=%s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z')"
printf 'Q122_EXPECTED_RUN=%s\n' "$EXPECTED_RUN"
printf 'Q122_EXPECTED_ARCHIVE_SHA256=%s\n' "$EXPECTED_ARCHIVE_SHA256"

static_validate() {
  local marker
  bash -n "$SCRIPT_PATH"
  for marker in \
    "$EXPECTED_ARCHIVE_NAME" \
    "$EXPECTED_ARCHIVE_SHA256" \
    "$EXPECTED_RUN" \
    Q122_DOWNLOADED_EVIDENCE_SHA256_PASS \
    Q122_DOWNLOADED_EVIDENCE_SAFE_ARCHIVE_PASS \
    Q122_DOWNLOADED_EVIDENCE_RESULT_CONTRACT_PASS \
    Q122_DOWNLOADED_EVIDENCE_LOCAL_ACCEPTANCE_PASS \
    replication=NOT_PERFORMED_Q221_GATE \
    business_sql=NOT_PERFORMED_Q261_GATE
  do
    grep -Fq -- "$marker" "$SCRIPT_PATH" ||
      die "static marker missing: $marker"
  done
  printf 'Q122_DOWNLOADED_EVIDENCE_LOCAL_VERIFIER_STATIC_PASS\n'
  printf 'Q122_EVIDENCE_DOWNLOAD=NOT_PERFORMED_BY_STATIC_VALIDATE\n'
}

assert_env_line() {
  local text=$1 expected=$2
  grep -Fxq -- "$expected" <<<"$text" ||
    die "required result contract line missing: $expected"
}

member_text() {
  local archive=$1 member=$2
  tar -xOzf "$archive" "$member"
}

verify_archive() {
  local archive=$1 actual_sha member prefix result action
  local id56 id80 schemas56 schemas80 repl56 repl80 units listeners unit port
  local sid56 uuid56 ro56 sid80 uuid80 ro80 sro80
  local -a required_members

  [[ "$(uname -s)" == Darwin ]] ||
    die 'this acceptance entry is local-mac-only and must not run on a server'
  [[ "$archive" == /* && "$archive" == /Users/* ]] ||
    die 'archive path must be an absolute local path below /Users'
  [[ "$(basename "$archive")" == "$EXPECTED_ARCHIVE_NAME" ]] ||
    die 'archive basename mismatch'
  [[ -f "$archive" && ! -L "$archive" ]] ||
    die "downloaded evidence archive is absent, not regular, or is a symlink: $archive"

  actual_sha=$(sha256sum "$archive" | awk '{print $1}')
  [[ "$actual_sha" == "$EXPECTED_ARCHIVE_SHA256" ]] ||
    die "archive SHA256 mismatch: $actual_sha"
  printf 'Q122_DOWNLOADED_EVIDENCE_SHA256_PASS\n'

  need tar
  need awk
  need grep
  need sort
  need uniq

  tar -tzf "$archive" |
    awk '
      substr($0,1,1)=="/" {bad=1}
      {
        count=split($0, part, "/")
        for (i=1; i<=count; i++) if (part[i]=="..") bad=1
      }
      END {exit bad ? 1 : 0}
    ' || die 'archive contains an unsafe member path'

  [[ -z "$(tar -tzf "$archive" | sort | uniq -d)" ]] ||
    die 'archive contains duplicate member names'

  tar -tvzf "$archive" |
    awk 'substr($0,1,1)!="-" && substr($0,1,1)!="d" {bad=1} END {exit bad ? 1 : 0}' ||
    die 'archive contains a link, device, or unsupported member type'

  prefix="$EXPECTED_RUN"
  required_members=(
    "$prefix/action-result.env"
    "$prefix/result.env"
    "$prefix/mysql56-identity.tsv"
    "$prefix/mysql80-identity.tsv"
    "$prefix/mysql56-schemas.txt"
    "$prefix/mysql80-schemas.txt"
    "$prefix/mysql56-replication-status.txt"
    "$prefix/mysql80-replication-status.txt"
    "$prefix/units-verify.tsv"
    "$prefix/listeners-verify.txt"
    "$prefix/secret-metadata-verify.tsv"
  )
  for member in "${required_members[@]}"; do
    [[ "$(tar -tzf "$archive" | grep -Fxc -- "$member")" -eq 1 ]] ||
      die "required archive member is missing or duplicated: $member"
  done
  printf 'Q122_DOWNLOADED_EVIDENCE_SAFE_ARCHIVE_PASS\n'

  result=$(member_text "$archive" "$prefix/result.env")
  action=$(member_text "$archive" "$prefix/action-result.env")
  assert_env_line "$result" 'result=PASS'
  assert_env_line "$result" 'task=Q-122'
  assert_env_line "$result" "run_id=$EXPECTED_RUN"
  assert_env_line "$result" 'node_key=mysql02'
  assert_env_line "$result" 'hostname=sqjcDB02'
  assert_env_line "$result" 'ip=10.2.208.107'
  assert_env_line "$result" 'replication=NOT_PERFORMED_Q221_GATE'
  assert_env_line "$result" 'business_sql=NOT_PERFORMED_Q261_GATE'
  assert_env_line "$result" 'rollback=AVAILABLE_WITH_THIS_RUN_ID'
  assert_env_line "$action" 'action=execute'
  assert_env_line "$action" 'exit_code=0'

  id56=$(member_text "$archive" "$prefix/mysql56-identity.tsv")
  id80=$(member_text "$archive" "$prefix/mysql80-identity.tsv")
  [[ "$(wc -l <<<"$id56" | tr -d ' ')" -eq 1 ]] ||
    die 'MySQL5.6 identity evidence line count mismatch'
  [[ "$(wc -l <<<"$id80" | tr -d ' ')" -eq 1 ]] ||
    die 'MySQL8.0 identity evidence line count mismatch'
  read -r sid56 uuid56 ro56 <<<"$id56"
  read -r sid80 uuid80 ro80 sro80 <<<"$id80"
  [[ "$sid56" == 56107 && "$ro56" == 1 ]] ||
    die 'MySQL5.6 server_id/read_only evidence mismatch'
  [[ "$sid80" == 80107 && "$ro80" == 1 && "$sro80" == 1 ]] ||
    die 'MySQL8.0 server_id/read_only/super_read_only evidence mismatch'
  [[ "$uuid56" =~ ^[0-9A-Fa-f-]{36}$ && "$uuid80" =~ ^[0-9A-Fa-f-]{36}$ &&
     "$uuid56" != "$uuid80" ]] ||
    die 'local MySQL UUID evidence is malformed or duplicated'
  printf 'Q122_DOWNLOADED_EVIDENCE_IDENTITY_CONTRACT_PASS\n'

  schemas56=$(member_text "$archive" "$prefix/mysql56-schemas.txt")
  schemas80=$(member_text "$archive" "$prefix/mysql80-schemas.txt")
  [[ "$(printf '%s\n' "$schemas56" | sort)" == "$(printf '%s\n' \
    information_schema binlog mysql performance_schema relay | sort)" ]] ||
    die 'MySQL5.6 empty-schema allowlist evidence mismatch'
  [[ "$(printf '%s\n' "$schemas80" | sort)" == "$(printf '%s\n' \
    information_schema mysql performance_schema sys | sort)" ]] ||
    die 'MySQL8.0 empty-schema allowlist evidence mismatch'
  repl56=$(member_text "$archive" "$prefix/mysql56-replication-status.txt")
  repl80=$(member_text "$archive" "$prefix/mysql80-replication-status.txt")
  [[ -z "$repl56" && -z "$repl80" ]] ||
    die 'replication status exists before Q-221'
  printf 'Q122_DOWNLOADED_EVIDENCE_EMPTY_AND_NO_REPLICATION_PASS\n'

  units=$(member_text "$archive" "$prefix/units-verify.tsv")
  for unit in \
    mysql56.service mysql80.service mysql-health56.service mysql-health80.service
  do
    grep -Fxq "$unit"$'\t''enabled=enabled'$'\t''active=active' <<<"$units" ||
      die "unit acceptance evidence mismatch: $unit"
  done
  [[ "$(wc -l <<<"$units" | tr -d ' ')" -eq 4 ]] ||
    die 'unit acceptance evidence line count mismatch'

  listeners=$(member_text "$archive" "$prefix/listeners-verify.txt")
  for port in 3306 3308 9201 9202; do
    grep -Eq "10\\.2\\.208\\.107:${port}([[:space:]]|$)" <<<"$listeners" ||
      die "listener acceptance evidence mismatch: 10.2.208.107:$port"
  done
  ! grep -Eq ':33060([[:space:]]|$)' <<<"$listeners" ||
    die 'forbidden MySQL X listener exists in acceptance evidence'
  printf 'Q122_DOWNLOADED_EVIDENCE_UNIT_LISTENER_CONTRACT_PASS\n'

  printf 'Q122_DOWNLOADED_EVIDENCE_RESULT_CONTRACT_PASS\n'
  printf 'Q122_DOWNLOADED_EVIDENCE_LOCAL_ACCEPTANCE_PASS\n'
  printf 'Q122_DOWNLOADED_EVIDENCE_ARCHIVE=%s\n' "$archive"
  printf 'Q122_DOWNLOADED_EVIDENCE_SHA256=%s\n' "$actual_sha"
}

case "$MODE" in
  --static-validate)
    [[ $# -eq 1 ]] || { usage; exit 2; }
    static_validate
    ;;
  verify)
    [[ $# -le 2 ]] || { usage; exit 2; }
    verify_archive "${2:-$EXPECTED_ARCHIVE_PATH}"
    ;;
  *)
    usage
    exit 2
    ;;
esac
