#!/bin/sh
set -eu

PROJECT_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
API_ROOT="$PROJECT_ROOT/tests/fixtures/api-complete"
YAZA_TEST_CLI=${YAZA_TEST_CLI:-yaza}
POSTGRES_IMAGE=${YAZA_POSTGRES_IMAGE:-postgres:18}
POSTGRES_USER=yaza_smoke
POSTGRES_PASSWORD=yaza_lifecycle_smoke_only
POSTGRES_DATABASE=yaza_lifecycle_smoke
CONTAINER_NAME="yaza-api-postgres-smoke-$$"
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/yaza-api-postgres-smoke.XXXXXX")
CONTAINER_STARTED=0

cleanup() {
    if [ "$CONTAINER_STARTED" -eq 1 ]; then
        docker unpause "$CONTAINER_NAME" >/dev/null 2>&1 || true
        docker stop "$CONTAINER_NAME" >/dev/null 2>&1 || true
        docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
    fi
    rm -rf "$TEMP_DIR"
}
trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM

fail() {
    printf 'FAIL %s\n' "$1" >&2
    if [ "$CONTAINER_STARTED" -eq 1 ]; then
        docker logs "$CONTAINER_NAME" >&2 2>/dev/null || true
    fi
    exit 1
}

case "$YAZA_TEST_CLI" in
    /*) ;;
    yaza) YAZA_TEST_CLI=$(command -v yaza || true) ;;
    *) YAZA_TEST_CLI="$PROJECT_ROOT/$YAZA_TEST_CLI" ;;
esac
if [ -z "$YAZA_TEST_CLI" ] || [ ! -x "$YAZA_TEST_CLI" ]; then
    fail 'set YAZA_TEST_CLI to an executable YAZA compiler'
fi

clang -std=c11 -O2 "$PROJECT_ROOT/tests/available_tcp_port.c" -o "$TEMP_DIR/available-tcp-port" || fail 'could not build the PostgreSQL test port selector'
POSTGRES_PORT=$("$TEMP_DIR/available-tcp-port") || fail 'could not select a loopback port for PostgreSQL'

docker run --detach \
    --name "$CONTAINER_NAME" \
    --publish "127.0.0.1:$POSTGRES_PORT:5432" \
    --env "POSTGRES_USER=$POSTGRES_USER" \
    --env "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" \
    --env "POSTGRES_DB=$POSTGRES_DATABASE" \
    "$POSTGRES_IMAGE" > "$TEMP_DIR/container.id" || fail 'could not start the disposable PostgreSQL container'
CONTAINER_STARTED=1

PORT_BINDING=$(docker port "$CONTAINER_NAME" 5432/tcp | sed -n '1p') || fail 'could not resolve the disposable PostgreSQL port'
case "$PORT_BINDING" in
    127.0.0.1:*) BOUND_PORT=${PORT_BINDING##*:} ;;
    *) fail 'PostgreSQL container did not bind exclusively to IPv4 loopback' ;;
esac
[ "$BOUND_PORT" = "$POSTGRES_PORT" ] || fail 'Docker changed the requested fixed PostgreSQL host port'

attempt=0
until docker exec "$CONTAINER_NAME" pg_isready -q -U "$POSTGRES_USER" -d "$POSTGRES_DATABASE" >/dev/null 2>&1; do
    attempt=$((attempt + 1))
    if [ "$attempt" -ge 60 ]; then fail 'disposable PostgreSQL did not become ready within 60 seconds'; fi
    sleep 1
done

DATABASE_URL="postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@127.0.0.1:$POSTGRES_PORT/$POSTGRES_DATABASE?sslmode=disable"
(
    cd "$API_ROOT"
    DATABASE_URL="$DATABASE_URL" PGSSLMODE=disable "$YAZA_TEST_CLI" db migrate
)

(
    cd "$PROJECT_ROOT"
    DATABASE_URL="$DATABASE_URL" PGSSLMODE=disable "$YAZA_TEST_CLI" run tests/db_postgres_live_smoke.yz
) || fail 'live PostgreSQL structured-query smoke failed'

docker exec -i "$CONTAINER_NAME" psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DATABASE" <<'SQL' || fail 'could not install isolated PostgreSQL API error fixtures'
CREATE FUNCTION yaza_api_smoke_error_fixture() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
    IF NEW.payload->>'name' = 'YAZA fixture unique violation' THEN
        RAISE EXCEPTION 'fixture unique violation' USING ERRCODE = '23505';
    ELSIF NEW.payload->>'name' = 'YAZA fixture constraint violation' THEN
        RAISE EXCEPTION 'fixture constraint violation' USING ERRCODE = '23514';
    ELSIF NEW.payload->>'name' = 'YAZA fixture statement timeout' THEN
        PERFORM pg_sleep(4);
    ELSIF NEW.payload->>'name' = 'YAZA fixture active connection loss' THEN
        PERFORM pg_sleep(30);
    END IF;
    RETURN NEW;
END;
$$;
CREATE TRIGGER yaza_api_smoke_error_fixture_trigger
    BEFORE INSERT ON yaza_api_records
    FOR EACH ROW EXECUTE FUNCTION yaza_api_smoke_error_fixture();
SQL

(
    cd "$PROJECT_ROOT"
    DATABASE_URL="$DATABASE_URL" PGSSLMODE=disable \
        YAZA_STDLIB_DIR="$PROJECT_ROOT" YAZA_TEST_CLI="$YAZA_TEST_CLI" \
        YAZA_PG_POOL_SIZE=3 \
        YAZA_TEST_DATABASE_CONTAINER="$CONTAINER_NAME" \
        YAZA_TEST_DATABASE_USER="$POSTGRES_USER" YAZA_TEST_DATABASE_NAME="$POSTGRES_DATABASE" \
        YAZA_TEST_DATABASE_PORT="$POSTGRES_PORT" \
        sh tests/fixtures/api-complete/tests/http-smoke.sh
) || fail 'database-configured API HTTP smoke failed'

# Exercise verified TLS using a disposable test CA and a localhost-only server certificate.
cat > "$TEMP_DIR/ca.cnf" <<'EOF'
[req]
prompt = no
distinguished_name = dn
x509_extensions = ca_ext
[dn]
CN = YAZA PostgreSQL Smoke CA
[ca_ext]
basicConstraints = critical,CA:TRUE
keyUsage = critical,keyCertSign,cRLSign
subjectKeyIdentifier = hash
EOF
cat > "$TEMP_DIR/server.cnf" <<'EOF'
[req]
prompt = no
distinguished_name = dn
req_extensions = server_ext
[dn]
CN = localhost
[server_ext]
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = DNS:localhost
EOF
openssl req -x509 -newkey rsa:2048 -nodes -days 2 -config "$TEMP_DIR/ca.cnf" \
    -keyout "$TEMP_DIR/ca.key" -out "$TEMP_DIR/ca.crt" >/dev/null 2>&1 || fail 'could not create the disposable PostgreSQL TLS CA'
openssl req -newkey rsa:2048 -nodes -config "$TEMP_DIR/server.cnf" \
    -keyout "$TEMP_DIR/server.key" -out "$TEMP_DIR/server.csr" >/dev/null 2>&1 || fail 'could not create the disposable PostgreSQL TLS server key'
openssl x509 -req -days 2 -in "$TEMP_DIR/server.csr" -CA "$TEMP_DIR/ca.crt" \
    -CAkey "$TEMP_DIR/ca.key" -CAcreateserial -extfile "$TEMP_DIR/server.cnf" \
    -extensions server_ext -out "$TEMP_DIR/server.crt" >/dev/null 2>&1 || fail 'could not sign the disposable PostgreSQL TLS server certificate'
docker cp "$TEMP_DIR/server.crt" "$CONTAINER_NAME:/tmp/yaza-server.crt" >/dev/null || fail 'could not copy the PostgreSQL TLS certificate'
docker cp "$TEMP_DIR/server.key" "$CONTAINER_NAME:/tmp/yaza-server.key" >/dev/null || fail 'could not copy the PostgreSQL TLS private key'
docker exec --user root "$CONTAINER_NAME" chown postgres:postgres \
    /tmp/yaza-server.crt /tmp/yaza-server.key || fail 'could not set PostgreSQL TLS file ownership'
docker exec --user root "$CONTAINER_NAME" chmod 600 /tmp/yaza-server.key || fail 'could not restrict the PostgreSQL TLS private key'
docker exec "$CONTAINER_NAME" psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DATABASE" \
    -c "ALTER SYSTEM SET ssl = 'on'" \
    -c "ALTER SYSTEM SET ssl_cert_file = '/tmp/yaza-server.crt'" \
    -c "ALTER SYSTEM SET ssl_key_file = '/tmp/yaza-server.key'" \
    >/dev/null || fail 'could not enable PostgreSQL TLS for the live verification phase'
docker restart "$CONTAINER_NAME" >/dev/null || fail 'could not restart PostgreSQL with TLS enabled'
attempt=0
until docker exec "$CONTAINER_NAME" pg_isready -q -U "$POSTGRES_USER" -d "$POSTGRES_DATABASE" >/dev/null 2>&1; do
    attempt=$((attempt + 1))
    if [ "$attempt" -ge 60 ]; then fail 'TLS-enabled PostgreSQL did not become ready within 60 seconds'; fi
    sleep 1
done
TLS_SERVER_ENABLED=$(docker exec "$CONTAINER_NAME" psql -At -U "$POSTGRES_USER" -d "$POSTGRES_DATABASE" -c 'SHOW ssl')
[ "$TLS_SERVER_ENABLED" = on ] || fail 'PostgreSQL reported that TLS is disabled after the TLS restart'
if ! openssl s_client -connect "127.0.0.1:$POSTGRES_PORT" -servername localhost \
    -CAfile "$TEMP_DIR/ca.crt" -verify_return_error -verify_hostname localhost \
    </dev/null > "$TEMP_DIR/tls-openssl.log" 2>&1; then
    cat "$TEMP_DIR/tls-openssl.log" >&2
    fail 'the disposable PostgreSQL server did not pass the independent OpenSSL TLS check'
fi

TLS_DATABASE_URL="postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:$POSTGRES_PORT/$POSTGRES_DATABASE?sslmode=verify-full"
(
    cd "$API_ROOT"
    DATABASE_URL="$TLS_DATABASE_URL" PGSSLROOTCERT="$TEMP_DIR/ca.crt" \
        "$YAZA_TEST_CLI" db status
) > "$TEMP_DIR/tls-valid-host.log" 2>&1 || {
    cat "$TEMP_DIR/tls-valid-host.log" >&2
    fail 'YAZA did not connect to PostgreSQL with the trusted TLS CA and matching hostname'
}

WRONG_HOST_DATABASE_URL="postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@127.0.0.1:$POSTGRES_PORT/$POSTGRES_DATABASE?sslmode=verify-full"
if (
    cd "$API_ROOT"
    DATABASE_URL="$WRONG_HOST_DATABASE_URL" PGSSLROOTCERT="$TEMP_DIR/ca.crt" \
        "$YAZA_TEST_CLI" db status
) > "$TEMP_DIR/tls-wrong-host.log" 2>&1; then
    fail 'YAZA accepted a PostgreSQL TLS certificate for the wrong hostname'
fi
grep -F 'TLS certificate chain or hostname verification failed' "$TEMP_DIR/tls-wrong-host.log" >/dev/null || fail 'wrong-host TLS rejection did not report a safe verification failure'
if grep -F "$POSTGRES_PASSWORD" "$TEMP_DIR/tls-wrong-host.log" >/dev/null; then
    fail 'PostgreSQL password leaked during wrong-host TLS rejection'
fi

(
    cd "$PROJECT_ROOT"
    DATABASE_URL="$TLS_DATABASE_URL" PGSSLROOTCERT="$TEMP_DIR/ca.crt" \
        YAZA_STDLIB_DIR="$PROJECT_ROOT" YAZA_TEST_CLI="$YAZA_TEST_CLI" \
        YAZA_PG_POOL_SIZE=3 \
        YAZA_TEST_DATABASE_CONTAINER="$CONTAINER_NAME" \
        YAZA_TEST_DATABASE_USER="$POSTGRES_USER" YAZA_TEST_DATABASE_NAME="$POSTGRES_DATABASE" \
        YAZA_TEST_DATABASE_PORT="$POSTGRES_PORT" \
        sh tests/fixtures/api-complete/tests/http-smoke.sh
) || fail 'database-configured API HTTP smoke failed with verified PostgreSQL TLS'

printf 'PostgreSQL container API smoke passed (%s).\n' "$POSTGRES_IMAGE"
