#!/usr/bin/env bash
set -u -o pipefail

ACTION=${1:-run}
FAIL=0

now() {
  date --iso-8601=seconds 2>/dev/null || date -Iseconds 2>/dev/null || date '+%Y-%m-%dT%H:%M:%S%z'
}

fail() {
  echo "GATE_FAIL=$*"
  FAIL=1
}

static_validate() {
  bash -n "$0"
  command -v awk date firewall-cmd getent grep hostname ip sort ss stat timedatectl timeout >/dev/null
  echo Q200_MYSQL_IP_READONLY_STATIC_VALIDATE_PASS
}

if [[ "$ACTION" == --static-validate ]]; then
  static_validate
  exit 0
fi
[[ "$ACTION" == run ]] || { echo 'ERROR: usage: script --static-validate|run'; exit 2; }

echo 'BEGIN Q200_MYSQL_IP_READONLY_GATE'
echo "started_at=$(now)"

actual_host=$(hostname -s)
case "$actual_host" in
  sqjcDB01) expected_ip=10.2.208.106; node=mysql01; peer_ip=10.2.208.107; expect_replica=0 ;;
  sqjcDB02) expected_ip=10.2.208.107; node=mysql02; peer_ip=10.2.208.106; expect_replica=1 ;;
  *) echo "ERROR: wrong hostname: $actual_host"; echo 'END Q200_MYSQL_IP_READONLY_GATE RC=1'; exit 1 ;;
esac
echo "node=$node"
echo "target_hostname=$actual_host"
echo "target_ip=$expected_ip"
if ip -o -4 addr show scope global | awk -v ip="$expected_ip" '{split($4,a,"/"); if(a[1]==ip) ok=1} END{exit !ok}'; then
  echo Q200_IDENTITY_GATE=PASS
else
  fail "expected IP missing: $expected_ip"
fi

echo '=== TIME ==='
date --iso-8601=seconds 2>/dev/null || date
timedatectl show -p Timezone -p NTPSynchronized 2>&1 || fail 'timedatectl query failed'
if [[ $(timedatectl show -p NTPSynchronized --value 2>/dev/null) == yes ]]; then
  echo Q200_TIME_SYNC_GATE=PASS
else
  fail 'NTP is not synchronized'
fi
if command -v chronyc >/dev/null 2>&1; then
  chronyc tracking 2>&1 || true
  chronyc sources -n 2>&1 || true
fi

echo '=== HOSTNAME_RESOLUTION_OBSERVATION_NON_BLOCKING ==='
while read -r ipaddr name; do
  resolved=$(getent ahostsv4 "$name" 2>/dev/null | awk 'NR==1{print $1}')
  printf 'RESOLUTION_OBSERVATION name=%s expected=%s actual=%s\n' "$name" "$ipaddr" "${resolved:-ABSENT}"
done <<'MAP'
10.2.203.102 sqjcnginx01
10.2.203.103 sqjcnginx02
10.2.203.104 sqjcapp01
10.2.203.105 sqjcapp02
10.2.208.106 sqjcDB01
10.2.208.107 sqjcDB02
10.2.203.108 sqjcmqtt01
10.2.203.109 sqjcmqtt02
10.2.203.110 sqjcmqtt03
10.2.203.112 sqjcredis01
10.2.203.113 sqjcredis02
MAP
echo HOSTNAME_DNS_GATE=OBSERVATION_ONLY_SUPERSEDED_BY_IP_BASED_Q200_POLICY

echo '=== FORMAL_CONFIG_HOSTNAME_REFERENCE_GATE ==='
config_files=(/etc/my56.cnf /etc/my80.cnf /etc/systemd/system/mysql56.service /etc/systemd/system/mysql80.service /usr/local/sbin/mysql-primary-health.sh)
refs=$(grep -hoE 'sqjc(nginx0[12]|app0[12]|DB0[12]|mqtt0[123]|redis0[12])' "${config_files[@]}" 2>/dev/null | sort -u || true)
if [[ -z "$refs" ]]; then
  echo FORMAL_CONFIG_CROSS_NODE_HOSTNAME_REFERENCES=ABSENT_IP_BASED_PASS
else
  while read -r ref; do
    [[ -n "$ref" ]] || continue
    resolved=$(getent ahostsv4 "$ref" 2>/dev/null | awk 'NR==1{print $1}')
    printf 'FORMAL_CONFIG_HOSTNAME_REFERENCE name=%s resolved=%s\n' "$ref" "${resolved:-ABSENT}"
    [[ -n "$resolved" ]] || fail "formal config references unresolved hostname: $ref"
  done <<<"$refs"
fi

echo '=== FIXED_IP_ROUTES ==='
for dst in 10.2.203.102 10.2.203.103 10.2.203.104 10.2.203.105 10.2.203.112 "$peer_ip"; do
  ip route get "$dst" 2>&1 || fail "route unavailable: $dst"
done

echo '=== LISTENERS ==='
listeners=$(ss -lntp 2>&1)
printf '%s\n' "$listeners" | grep -E ':(3306|3308|9201|9202)([[:space:]]|$)' || true
for port in 3306 3308 9201 9202; do
  if grep -Eq "${expected_ip}:${port}([[:space:]]|$)" <<<"$listeners"; then
    echo "LISTENER_GATE port=$port status=PASS"
  else
    fail "required listener missing: $expected_ip:$port"
  fi
done

echo '=== FIREWALL_READONLY ==='
fw_state=$(firewall-cmd --state 2>&1 || true)
echo "firewalld_state=$fw_state"
[[ "$fw_state" == running ]] || fail 'firewalld is not running'
zone=$(firewall-cmd --get-zone-of-interface=ens192 2>/dev/null || true)
echo "firewalld_zone=${zone:-ABSENT}"
[[ -n "$zone" ]] || fail 'ens192 has no firewalld zone'
firewall-cmd --zone="$zone" --list-all 2>&1 || fail 'runtime firewall read failed'
firewall-cmd --permanent --zone="$zone" --list-all 2>&1 || fail 'permanent firewall read failed'

check_rule() {
  local source=$1 port=$2 purpose=$3 r
  r="rule family=\"ipv4\" source address=\"$source/32\" port port=\"$port\" protocol=\"tcp\" accept"
  if firewall-cmd --zone="$zone" --query-rich-rule="$r" >/dev/null 2>&1; then
    echo "FIREWALL_RUNTIME source=$source port=$port purpose=$purpose status=PASS"
  else
    fail "runtime rich rule missing source=$source port=$port purpose=$purpose"
  fi
  if firewall-cmd --permanent --zone="$zone" --query-rich-rule="$r" >/dev/null 2>&1; then
    echo "FIREWALL_PERMANENT source=$source port=$port purpose=$purpose status=PASS"
  else
    fail "permanent rich rule missing source=$source port=$port purpose=$purpose"
  fi
}

for source in 10.2.203.102 10.2.203.103; do
  for port in 3306 3308 9201 9202; do
    check_rule "$source" "$port" nginx_haproxy_orchestrator
  done
done
check_rule "$peer_ip" 3306 mysql_peer
check_rule "$peer_ip" 3308 mysql_peer

echo '=== PEER_TCP_FIXED_IP ==='
for port in 3306 3308; do
  if timeout 4 bash -c "exec 3<>/dev/tcp/$peer_ip/$port" >/dev/null 2>&1; then
    echo "PEER_TCP target=$peer_ip port=$port status=PASS"
  else
    fail "peer TCP unreachable target=$peer_ip port=$port"
  fi
done

echo '=== REPLICATION_OBSERVATION_NO_CHANGE ==='
MYSQL56=/opt/mysql/5.6.51/bin/mysql
MYSQL80=/opt/mysql/8.0.46/bin/mysql
if [[ "$expect_replica" -eq 1 ]]; then
  s56=$($MYSQL56 --no-defaults --protocol=socket --socket=/run/mysql56/mysql.sock --vertical -e 'SHOW SLAVE STATUS' 2>&1 || true)
  s80=$($MYSQL80 --no-defaults --protocol=socket --socket=/run/mysql80/mysql.sock --vertical -e 'SHOW REPLICA STATUS' 2>&1 || true)
  printf '%s\n' "$s56" | awk -F: '/^[[:space:]]*(Master_Host|Master_Port|Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master|Last_IO_Errno|Last_SQL_Errno):/{print}'
  printf '%s\n' "$s80" | awk -F: '/^[[:space:]]*(Source_Host|Source_Port|Replica_IO_Running|Replica_SQL_Running|Seconds_Behind_Source|Last_IO_Errno|Last_SQL_Errno):/{print}'
  grep -q 'Slave_IO_Running: Yes' <<<"$s56" && grep -q 'Slave_SQL_Running: Yes' <<<"$s56" || fail 'MySQL56 replication threads unhealthy'
  grep -q 'Replica_IO_Running: Yes' <<<"$s80" && grep -q 'Replica_SQL_Running: Yes' <<<"$s80" || fail 'MySQL80 replication threads unhealthy'
else
  echo REPLICATION_PRIMARY_NODE=OBSERVED_NO_RECONFIGURATION
fi

echo Q200_REMOTE_MUTATION=ZERO
echo Q200_HOSTS_DNS_FAILURE=SUPERSEDED_BY_IP_BASED_Q200_POLICY
echo "finished_at=$(now)"
if [[ "$FAIL" -eq 0 ]]; then
  echo Q200_MYSQL_IP_READONLY_GATE_PASS
  echo 'END Q200_MYSQL_IP_READONLY_GATE RC=0'
  exit 0
fi
echo Q200_MYSQL_IP_READONLY_GATE_BLOCKED
echo 'END Q200_MYSQL_IP_READONLY_GATE RC=1'
exit 1
