import { drizzle } from 'drizzle-orm/postgres-js'
import { sql } from 'drizzle-orm'
import postgres from 'postgres'
import * as schema from './schema/index.js'
import { type Context, isSystem } from './actor.js'

/**
 * THE single database entry point. Required by #11 decision 4:
 *
 *   "Every data path must run inside a transaction, and any code touching the
 *    pool outside that discipline silently runs with no actor set. The code
 *    layout needs a single database entry point that makes bypassing it
 *    awkward — this is a structural requirement on the layout, not a
 *    convention to remember."
 *
 * The pool is not exported. `withActor` is the only way in.
 */
const pool = postgres(process.env.DATABASE_URL!, { max: 10 })
const db = drizzle(pool, { schema })

export type Db = typeof db
export type Tx = Parameters<Parameters<Db['transaction']>[0]>[0]

/**
 * Open a transaction with actor context established. Everything else in the
 * codebase takes a `Tx`, never the pool.
 *
 * Empty results are the failure mode for authorization bugs here, which are
 * harder to diagnose than explicit denials — #11 flagged observability around
 * policy denial as not optional. That belongs at this boundary.
 */
export async function withActor<T>(ctx: Context, fn: (tx: Tx) => Promise<T>): Promise<T> {
  return db.transaction(async (tx) => {
    if (isSystem(ctx)) {
      await tx.execute(sql`SELECT set_config('app.system_job', ${ctx.job}, true)`)
    } else {
      await tx.execute(sql`SELECT set_config('app.actor_id', ${ctx.actorId}, true)`)
      await tx.execute(sql`SELECT set_config('app.org_id', ${ctx.orgId}, true)`)
      await tx.execute(sql`SELECT set_config('app.capability', ${ctx.capability}, true)`)
    }
    return fn(tx)
  })
}

export { schema }
