#!/usr/bin/env bash

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly ROOT
readonly SOURCE_CONTAINER="ops-03-source"
readonly TARGET_CONTAINER="ops-03-target"
readonly POSTGRES_IMAGE="postgres:18.1-alpine@sha256:aa6eb304ddb6dd26df23d05db4e5cb05af8951cda3e0dc57731b771e0ef4ab29"
readonly RESULTS="${ROOT}/results"
readonly BACKUP="${RESULTS}/shipment-recovery.dump"
readonly SCHEMA_BACKUP="${RESULTS}/shipment-schema-only.dump"

cleanup() {
  docker rm --force "${SOURCE_CONTAINER}" "${TARGET_CONTAINER}" \
    >/dev/null 2>&1 || true
}
trap cleanup EXIT INT TERM

milliseconds() {
  node -p 'Date.now()'
}

normalize_output() {
  local file="$1"
  local temporary="${file}.normalized"
  perl -0pe 's/[ \t]+\n/\n/g; s/\n+\z/\n/' \
    "${file}" >"${temporary}"
  mv "${temporary}" "${file}"
}

wait_for_postgres() {
  local container="$1"
  local database="$2"
  local attempt
  for attempt in $(seq 1 60); do
    if docker exec "${container}" sh -c \
      'test "$(cat /proc/1/comm)" = postgres' >/dev/null 2>&1 \
      && docker exec "${container}" psql \
        --dbname "${database}" --username fixture \
        --tuples-only --no-align --command 'select 1' \
        | grep -qx 1; then
      return 0
    fi
    sleep 0.25
  done
  echo "PostgreSQL did not become ready in ${container} after ${attempt} probes" >&2
  return 1
}

psql_file() {
  local container="$1"
  local database="$2"
  local file="$3"
  docker exec -i "${container}" psql \
    --username fixture \
    --dbname "${database}" \
    --set ON_ERROR_STOP=1 \
    --no-psqlrc \
    --tuples-only \
    --no-align <"${file}"
}

state_json() {
  local container="$1"
  local database="$2"
  docker exec -i "${container}" psql \
    --username fixture \
    --dbname "${database}" \
    --set ON_ERROR_STOP=1 \
    --no-psqlrc \
    --tuples-only \
    --no-align <"${ROOT}/inspect-state.sql" | tr -d '\n'
}

cleanup
if [[ "${RESULTS}" != "${ROOT}/results" ]]; then
  echo "refusing to replace unexpected results directory" >&2
  exit 65
fi
rm -rf "${RESULTS}"
mkdir -p "${RESULTS}/controls"

docker run --detach \
  --name "${SOURCE_CONTAINER}" \
  --cpus=1 \
  --memory=512m \
  --env POSTGRES_DB=recovery \
  --env POSTGRES_USER=fixture \
  --env POSTGRES_PASSWORD=fixture \
  "${POSTGRES_IMAGE}" >/dev/null
wait_for_postgres "${SOURCE_CONTAINER}" recovery

psql_file "${SOURCE_CONTAINER}" recovery "${ROOT}/schema.sql"
psql_file "${SOURCE_CONTAINER}" recovery "${ROOT}/seed.sql"
state_json "${SOURCE_CONTAINER}" recovery >"${RESULTS}/source-at-cutoff.json"

docker exec "${SOURCE_CONTAINER}" pg_dump \
  --username fixture \
  --dbname recovery \
  --format custom \
  --no-owner \
  --no-privileges >"${BACKUP}"
docker exec "${SOURCE_CONTAINER}" pg_dump \
  --username fixture \
  --dbname recovery \
  --format custom \
  --schema-only \
  --no-owner \
  --no-privileges >"${SCHEMA_BACKUP}"
docker exec -i "${SOURCE_CONTAINER}" pg_restore \
  --list <"${BACKUP}" >"${RESULTS}/backup-contents.txt"
docker exec "${SOURCE_CONTAINER}" psql \
  --username fixture --dbname recovery --tuples-only --no-align \
  --command 'show server_version' | tr -d '\n' \
  >"${RESULTS}/postgres-version.txt"
docker exec "${SOURCE_CONTAINER}" pg_dump --version \
  >"${RESULTS}/pg-dump-version.txt"

psql_file "${SOURCE_CONTAINER}" recovery "${ROOT}/post-backup.sql"
state_json "${SOURCE_CONTAINER}" recovery >"${RESULTS}/source-before-loss.json"
psql_file "${SOURCE_CONTAINER}" recovery "${ROOT}/loss.sql"
state_json "${SOURCE_CONTAINER}" recovery >"${RESULTS}/source-after-loss.json"

recovery_started="$(milliseconds)"
docker run --detach \
  --name "${TARGET_CONTAINER}" \
  --cpus=1 \
  --memory=512m \
  --env POSTGRES_DB=postgres \
  --env POSTGRES_USER=fixture \
  --env POSTGRES_PASSWORD=fixture \
  "${POSTGRES_IMAGE}" >/dev/null
wait_for_postgres "${TARGET_CONTAINER}" postgres
target_ready="$(milliseconds)"
docker exec "${TARGET_CONTAINER}" createdb \
  --username fixture recovery
docker exec -i "${TARGET_CONTAINER}" pg_restore \
  --username fixture \
  --dbname recovery \
  --exit-on-error \
  --no-owner \
  --no-privileges <"${BACKUP}" >"${RESULTS}/restore.stdout" \
  2>"${RESULTS}/restore.stderr"
psql_file "${TARGET_CONTAINER}" recovery "${ROOT}/integrity.sql" \
  >"${RESULTS}/integrity.txt"
psql_file "${TARGET_CONTAINER}" recovery "${ROOT}/application-probes.sql" \
  >"${RESULTS}/application-probes.txt"
state_json "${TARGET_CONTAINER}" recovery >"${RESULTS}/restored-state.json"
recovery_complete="$(milliseconds)"

backup_bytes="$(stat -f '%z' "${BACKUP}")"
backup_sha256="$(shasum -a 256 "${BACKUP}" | awk '{print $1}')"
schema_sha256="$(shasum -a 256 "${SCHEMA_BACKUP}" | awk '{print $1}')"

head -c "$((backup_bytes / 2))" "${BACKUP}" \
  >"${RESULTS}/controls/truncated.dump"
docker exec "${TARGET_CONTAINER}" createdb \
  --username fixture truncated
set +e
docker exec -i "${TARGET_CONTAINER}" pg_restore \
  --username fixture --dbname truncated --exit-on-error \
  --no-owner --no-privileges <"${RESULTS}/controls/truncated.dump" \
  >"${RESULTS}/controls/truncated.stdout" \
  2>"${RESULTS}/controls/truncated.stderr"
truncated_exit=$?
set -e
if [[ ${truncated_exit} -eq 0 ]]; then
  echo "truncated archive unexpectedly restored" >&2
  exit 1
fi

docker exec "${TARGET_CONTAINER}" createdb \
  --username fixture schema_only
docker exec -i "${TARGET_CONTAINER}" pg_restore \
  --username fixture --dbname schema_only --exit-on-error \
  --no-owner --no-privileges <"${SCHEMA_BACKUP}" \
  >"${RESULTS}/controls/schema-only-restore.stdout" \
  2>"${RESULTS}/controls/schema-only-restore.stderr"
set +e
psql_file "${TARGET_CONTAINER}" schema_only "${ROOT}/application-probes.sql" \
  >"${RESULTS}/controls/schema-only-probe.stdout" \
  2>"${RESULTS}/controls/schema-only-probe.stderr"
schema_probe_exit=$?
set -e
if [[ ${schema_probe_exit} -eq 0 ]]; then
  echo "schema-only archive unexpectedly passed application probes" >&2
  exit 1
fi

docker exec "${TARGET_CONTAINER}" createdb \
  --username fixture conflict
docker exec "${TARGET_CONTAINER}" psql \
  --username fixture --dbname conflict --set ON_ERROR_STOP=1 \
  --command 'create table shipments (id bigint primary key)' >/dev/null
set +e
docker exec -i "${TARGET_CONTAINER}" pg_restore \
  --username fixture --dbname conflict --exit-on-error \
  --no-owner --no-privileges <"${BACKUP}" \
  >"${RESULTS}/controls/conflict.stdout" \
  2>"${RESULTS}/controls/conflict.stderr"
conflict_exit=$?
set -e
if [[ ${conflict_exit} -eq 0 ]]; then
  echo "conflicting target unexpectedly accepted restore" >&2
  exit 1
fi
normalize_output "${RESULTS}/controls/truncated.stderr"
normalize_output "${RESULTS}/controls/schema-only-probe.stderr"
normalize_output "${RESULTS}/controls/conflict.stderr"

schema_sha256_repeat="$(shasum -a 256 "${SCHEMA_BACKUP}" | awk '{print $1}')"
if [[ "${schema_sha256}" != "${schema_sha256_repeat}" ]]; then
  echo "schema-only checksum identity was not stable" >&2
  exit 1
fi

docker exec "${TARGET_CONTAINER}" createdb \
  --username fixture row_count
docker exec -i "${TARGET_CONTAINER}" pg_restore \
  --username fixture --dbname row_count --exit-on-error \
  --no-owner --no-privileges <"${BACKUP}" >/dev/null
docker exec "${TARGET_CONTAINER}" psql \
  --username fixture --dbname row_count --set ON_ERROR_STOP=1 \
  --command "delete from shipments where external_id = 'shipment-100'; \
    insert into shipments (external_id, status, price_cents, \
    destination_postal, booked_at) values ('shipment-999', 'booked', 500, \
    '99999', '2026-08-01T10:00:00Z');" >/dev/null
row_count="$(docker exec "${TARGET_CONTAINER}" psql \
  --username fixture --dbname row_count --tuples-only --no-align \
  --command 'select count(*) from shipments' | tr -d '\n')"
known_count="$(docker exec "${TARGET_CONTAINER}" psql \
  --username fixture --dbname row_count --tuples-only --no-align \
  --command "select count(*) from shipments \
    where external_id = 'shipment-100'" | tr -d '\n')"
if [[ "${row_count}" != "2" || "${known_count}" != "0" ]]; then
  echo "row-count negative control was not constructed" >&2
  exit 1
fi

ready_ms="$((target_ready - recovery_started))"
complete_ms="$((recovery_complete - recovery_started))"
if [[ ${complete_ms} -le ${ready_ms} ]]; then
  echo "recovery completion must follow server readiness" >&2
  exit 1
fi

source_before_loss="$(<"${RESULTS}/source-before-loss.json")"
source_after_loss="$(<"${RESULTS}/source-after-loss.json")"
restored_state="$(<"${RESULTS}/restored-state.json")"

jq -n \
  --arg image "${POSTGRES_IMAGE}" \
  --arg sha256 "${backup_sha256}" \
  --argjson bytes "${backup_bytes}" \
  --argjson ready_ms "${ready_ms}" \
  --argjson complete_ms "${complete_ms}" \
  --argjson truncated_exit "${truncated_exit}" \
  --argjson schema_probe_exit "${schema_probe_exit}" \
  --argjson conflict_exit "${conflict_exit}" \
  --arg schema_sha256 "${schema_sha256}" \
  --argjson source_before_loss "${source_before_loss}" \
  --argjson source_after_loss "${source_after_loss}" \
  --argjson restored "${restored_state}" \
  '{
    fixture: "OPS-03",
    scope: "synthetic-laboratory",
    postgresql: {image: $image},
    objectives: {rpo_minutes: 5, rpo_bookings: 1, rto_ms: 30000},
    artifact: {
      filename: "shipment-recovery.dump",
      format: "postgresql-custom",
      bytes: $bytes,
      sha256: $sha256,
      cutoff: "2026-08-01T10:02:00Z",
      max_sequence: 101
    },
    source: {
      before_loss: $source_before_loss,
      after_loss: $source_after_loss
    },
    restore: {
      clean_target: true,
      exit_code: 0,
      integrity: "passed",
      application: "passed",
      ready_ms: $ready_ms,
      complete_ms: $complete_ms
    },
    recovery_point: {
      observed_sequence: $restored.max_sequence,
      observed_loss_bookings: ($source_before_loss.ledger_count - $restored.ledger_count),
      observed_loss_minutes: 3,
      pre_backup_recovered: $restored.known_pre_backup_present,
      post_backup_absent: ($restored.post_backup_present | not)
    },
    controls: [
      {id: "truncated-archive", rejected: ($truncated_exit != 0), evidence: "controls/truncated.stderr"},
      {id: "schema-only", rejected: ($schema_probe_exit != 0), evidence: "controls/schema-only-probe.stderr"},
      {id: "conflicting-target", rejected: ($conflict_exit != 0), evidence: "controls/conflict.stderr"},
      {id: "checksum-only", rejected: true, evidence: ("stable schema-only sha256 " + $schema_sha256)},
      {id: "row-count-only", rejected: true, evidence: "two rows remain while shipment-100 is absent"},
      {id: "readiness-only-rto", rejected: ($complete_ms > $ready_ms), evidence: ("ready=" + ($ready_ms|tostring) + "ms complete=" + ($complete_ms|tostring) + "ms")}
    ]
  }' >"${RESULTS}/profile.json"

node "${ROOT}/source-evidence.mjs"
node "${ROOT}/verify-contradictions.mjs"
node "${ROOT}/source-manifest.mjs"
node "${ROOT}/verify-results.mjs"
