#!/bin/sh
# AEGIS CYBER container entrypoint.
#
# Responsibilities:
#   1. Wait for the bundled Postgres to accept connections (compose
#      healthcheck handles most of this, but we double-check from the
#      app's network perspective in case of override / external DB).
#   2. Apply the current Drizzle schema to the database (idempotent —
#      `drizzle-kit push` is a no-op when the schema already matches).
#   3. Exec into the CMD (the bundled server) under dumb-init.
#
# Exits non-zero on failure so docker compose restart policies fire
# rather than running a partially-initialised platform.

set -e

echo "[entrypoint] AEGIS CYBER on-prem container starting..."
echo "[entrypoint] NODE_ENV=${NODE_ENV:-(unset)}  PORT=${PORT:-(unset)}"

# Postgres connectivity probe. Uses the pg client already in node_modules
# so we do not depend on netcat / wait-for-it shell idioms.
echo "[entrypoint] probing database at ${PGHOST:-(from DATABASE_URL)}:${PGPORT:-5432}..."
MAX_TRIES=60
TRY=0
until node -e "const{Client}=require('pg');const c=new Client({connectionString:process.env.DATABASE_URL});c.connect().then(()=>{c.end();process.exit(0)}).catch(()=>process.exit(1))" 2>/dev/null; do
  TRY=$((TRY + 1))
  if [ "$TRY" -ge "$MAX_TRIES" ]; then
    echo "[entrypoint] database did not become reachable after ${MAX_TRIES}s — aborting"
    exit 1
  fi
  echo "  ...not ready yet (attempt $TRY/$MAX_TRIES)"
  sleep 1
done
echo "[entrypoint] database is reachable"

# ── KNOWN-BENIGN drizzle-kit push noise (investigated 2026-07-09; deliberately
#    NOT "fixed" — see the roadmap wart entry for the full reasoning) ──────────
# Every boot, the `drizzle-kit push --force` below prints an interactive prompt:
#
#   · You're about to add ccr_tenant_id_uniq unique constraint to the table,
#     which contains 1 items ... Do you want to truncate compliance_check_runs
#     table?  ❯ No, add the constraint without truncating the table
#
# This is EXPECTED and HARMLESS. Four tables carry a composite UNIQUE(tenant_id,
# id) that is the REQUIRED target for tenant-bound composite FKs (a PK on `id`
# alone cannot satisfy a (tenant_id, id) FK):
#     ccr_tenant_id_uniq      (compliance_check_runs)
#     sec_inc_tenant_id_uniq  (security_incidents)
#     fcc_tenant_id_uniq      (fincrime_cases)
#     fsr_tenant_id_uniq      (fincrime_str_reports)
# The composite unique OVERLAPS each table's single-column PK (`id`) — a known
# drizzle-kit diff blind spot: push does not recognise the already-present
# constraint and re-proposes ADDing it every boot. The constraint already exists,
# the FKs are satisfied, `--force` auto-answers "No" (NEVER truncates), push exits
# 0, and rls-init (M15/M55) re-asserts the same constraints effect-idempotently.
# Net effect: zero — log noise only.
#
# Only ccr_tenant_id_uniq shows the "truncate?" line today because
# compliance_check_runs has 1 row (drizzle only warns about truncation on a
# NON-empty table). The other three hit the identical diff-mismatch SILENTLY
# (empty tables) — so if any of them ever gets data, its own "truncate?" prompt
# appears. That is the SAME benign cause, not a spread. Seeing 2, 3, or 4 of
# these prompts simultaneously is the same cause at a different data state — the
# count tracks which of the four tables are non-empty, NOT severity.
#
# WHAT WOULD *NOT* BE BENIGN (real signals — do NOT skim past these):
#   • a truncate actually EXECUTING (anything other than the "No, add ... without
#     truncating" choice being taken);
#   • a constraint name OTHER than the four listed above;
#   • this push exiting NON-ZERO (the block below prints "schema push failed",
#     exit 1);
#   • the "[entrypoint] schema is in sync" line below being ABSENT.
# Those indicate a genuine schema divergence — investigate, do not ignore.
#
# ── TWO DISTINCT PROMPT CLASSES on this push — do not conflate them ──────────
# THE LOAD-BEARING DISTINCTION: `--force` auto-answers DATA-LOSS confirmations
# but NOT SCHEMA-RESOLUTION prompts. So the two prompts this push can emit are
# handled OPPOSITELY, and the right fix for one is the wrong fix for the other:
#
#   (1) "truncate?"  — the four composite-unique constraints above. A DATA-LOSS
#       confirmation; --force auto-answers "No". Benign, log-noise only. DO NOT
#       "fix" this via drizzle.config.ts or the schema: this boot-push path has
#       caused two real incidents (data_keys orphan-drop, fixed cccd2a7; the
#       audit-chain fork window, fixed d77cc87 — read both before touching it),
#       and every candidate fix costs MORE than the noise (removing the
#       constraints trades the prompt for an FK-blocked DROP error; schema
#       restructuring touches the tenant-isolation FK targets or changes diff
#       behaviour schema-wide).
#
#   (2) "create or rename?"  — a SCHEMA-RESOLUTION prompt, emitted when a NEW
#       drizzle-schema table resembles a runtime-created orphan (a table not in
#       the schema). --force does NOT answer it → the non-interactive entrypoint
#       HANGS → crash-loop. This one IS correctly fixed in drizzle.config.ts:
#       every runtime-created table is `!`-excluded in tablesFilter so push never
#       pairs it against a new table (2026-07-09 — kyc_screening_results triggered
#       this against timestamp_anchors; fixed by excluding the three runtime
#       orphans). Editing drizzle.config is the RIGHT fix for (2), the WRONG fix
#       for (1). See drizzle.config.ts for the full orphan list + reasoning.
#
# Apply current schema. --force is required by drizzle-kit when running
# non-interactively; the operation is idempotent (no-op on matching schema).
echo "[entrypoint] applying schema via drizzle-kit push..."
if ! npx --no-install drizzle-kit push --force 2>&1; then
  echo "[entrypoint] schema push failed"
  exit 1
fi
echo "[entrypoint] schema is in sync"

# Pre-create the express-session backing table. connect-pg-simple's
# createTableIfMissing:true relies on a table.sql template file that is
# not bundled into the production image (only compiled dist/ + a slim
# node_modules subset is copied in), so the auto-create silently fails
# and every login produces a 200 with no persisted session — the next
# request reads an empty session and 401s. Pre-creating here removes
# the dependency on connect-pg-simple's runtime file lookup. Schema
# matches the one shipped by connect-pg-simple itself. Idempotent.
echo "[entrypoint] ensuring express-session backing table exists..."
if ! node -e "
const {Client} = require('pg');
const c = new Client({connectionString: process.env.DATABASE_URL});
c.connect()
  .then(() => c.query(\`
    CREATE TABLE IF NOT EXISTS \\\"session\\\" (
      \\\"sid\\\" varchar NOT NULL COLLATE \\\"default\\\",
      \\\"sess\\\" json NOT NULL,
      \\\"expire\\\" timestamp(6) NOT NULL,
      CONSTRAINT \\\"session_pkey\\\" PRIMARY KEY (\\\"sid\\\") NOT DEFERRABLE INITIALLY IMMEDIATE
    ) WITH (OIDS=FALSE);
    CREATE INDEX IF NOT EXISTS \\\"IDX_session_expire\\\" ON \\\"session\\\" (\\\"expire\\\");
  \`))
  .then(() => { c.end(); process.exit(0); })
  .catch(err => { console.error(err.message); c.end(); process.exit(1); });
" 2>&1; then
  echo "[entrypoint] session table creation failed"
  exit 1
fi
echo "[entrypoint] session table ready"

echo "[entrypoint] handing off to: $*"
exec "$@"
