// PostgreSQL backend for the async DB adapter. Same interface as db/sqlite.js — prepare(sql).{get,all,run},
// exec(sql), tx(fn), init() — so repos and app code are engine-agnostic. Selected by DB_BACKEND=pg;
// connection string from DATABASE_URL.
const { Pool, types } = require('pg');
const fs = require('fs');
const path = require('path');

// BIGINT (int8, OID 20) defaults to STRING in node-postgres to avoid precision loss. Every BIGINT here is
// an epoch-ms timestamp or a byte size — all far below Number.MAX_SAFE_INTEGER — so parse them as numbers
// to match the SQLite backend. Otherwise `expires_at < Date.now()` would compare a string to a number.
types.setTypeParser(20, (v) => (v === null ? null : parseInt(v, 10)));

const pool = new Pool({ connectionString: process.env.DATABASE_URL, max: 10 });

// Repos use '?' placeholders (SQLite style); Postgres wants $1,$2,… — replace positionally. Safe because
// no literal '?' appears inside any SQL string literal in this codebase.
function toPg(sql) { let i = 0; return sql.replace(/\?/g, () => '$' + (++i)); }

function prepare(sql) {
  const q = toPg(sql);
  return {
    get: (...p) => pool.query(q, p).then((r) => r.rows[0]),
    all: (...p) => pool.query(q, p).then((r) => r.rows),
    run: (...p) => pool.query(q, p).then((r) => ({ changes: r.rowCount, lastInsertRowid: undefined })),
  };
}

function exec(sql) { return pool.query(sql).then(() => {}); }

// Transaction on ONE pooled client (a pool would scatter BEGIN/COMMIT across connections). Same runner
// shape the sqlite backend's tx() exposes, so repos.mergeInto is identical on both engines.
async function tx(fn) {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');
    const t = {
      run: (sql, ...p) => client.query(toPg(sql), p).then((r) => ({ changes: r.rowCount })),
      get: (sql, ...p) => client.query(toPg(sql), p).then((r) => r.rows[0]),
      all: (sql, ...p) => client.query(toPg(sql), p).then((r) => r.rows),
    };
    const out = await fn(t);
    await client.query('COMMIT');
    return out;
  } catch (e) {
    try { await client.query('ROLLBACK'); } catch (_) {}
    throw e;
  } finally {
    client.release();
  }
}

// Apply the schema (all CREATE ... IF NOT EXISTS — idempotent). Multi-statement, no params, so it runs via
// the simple-query protocol in one call. MUST be awaited before serving (server.js boot).
async function init() {
  const sql = fs.readFileSync(path.join(__dirname, 'schema.pg.sql'), 'utf8');
  await pool.query(sql);
}

module.exports = { prepare, exec, tx, init, name: 'pg', _pool: pool };
