// RLS user-scoped isolation — REAL integration test (KAR-779 / R61).
//
// Unlike the parametrised skeleton in rls-user-scoped.test.ts, this test
// actually executes against a Postgres instance carrying the app's RLS
// policies and proves that the `projects_own` policy isolates users:
//   user_b cannot SELECT / UPDATE / DELETE user_a's projects, and cannot
//   INSERT a project with user_a's user_id (WITH CHECK).
//
// It is gated on RLS_TEST_DATABASE_URL so it SKIPS cleanly in CI (which has
// no Postgres service) and runs locally / on staging. Spin the DB and run it
// with:  scripts/rls-test/run.sh
//
// The mock auth layer resolves auth.uid() from
//   current_setting('request.jwt.claim.sub'); we impersonate a user by
//   SET ROLE authenticated + SET request.jwt.claim.sub.

import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { Client } from 'pg'

const DB_URL  = process.env.RLS_TEST_DATABASE_URL
const USER_A  = 'aaaa1111-0000-0000-0000-000000000001'
const USER_B  = 'bbbb2222-0000-0000-0000-000000000002'

describe.skipIf(!DB_URL)('RLS projects user-scoped isolation (R61)', () => {
  let client: Client

  beforeAll(async () => {
    client = new Client({ connectionString: DB_URL })
    await client.connect()
  })

  afterAll(async () => {
    await client?.end()
  })

  // Impersonate an authenticated user: drop superuser, become `authenticated`,
  // and set the JWT subject the mock auth.uid() reads.
  async function asUser(uid: string, sql: string) {
    await client.query('RESET ROLE')
    await client.query('SET ROLE authenticated')
    await client.query("SELECT set_config('request.jwt.claim.sub', $1, false)", [uid])
    return client.query(sql)
  }

  it('user_a can see their own project', async () => {
    const r = await asUser(USER_A, 'SELECT count(*)::int AS n FROM public.projects')
    expect(r.rows[0].n).toBeGreaterThanOrEqual(1)
  })

  it('user_b sees an empty set for user_a-owned projects', async () => {
    const r = await asUser(USER_B, 'SELECT count(*)::int AS n FROM public.projects')
    expect(r.rows[0].n).toBe(0)
  })

  it('user_b UPDATE on user_a rows affects 0 rows', async () => {
    const r = await asUser(
      USER_B,
      `UPDATE public.projects SET supplier_name = 'hijacked' WHERE user_id = '${USER_A}'`,
    )
    expect(r.rowCount).toBe(0)
  })

  it('user_b DELETE on user_a rows affects 0 rows', async () => {
    const r = await asUser(
      USER_B,
      `DELETE FROM public.projects WHERE user_id = '${USER_A}'`,
    )
    expect(r.rowCount).toBe(0)
  })

  it("user_b INSERT with user_a's user_id is rejected by the WITH CHECK policy", async () => {
    await expect(
      asUser(
        USER_B,
        `INSERT INTO public.projects (user_id, supplier_name) VALUES ('${USER_A}', 'forged')`,
      ),
    ).rejects.toThrow(/row-level security/i)
  })

  it("user_a's project is still intact after user_b's write attempts", async () => {
    const r = await asUser(
      USER_A,
      `SELECT supplier_name FROM public.projects WHERE user_id = '${USER_A}' LIMIT 1`,
    )
    expect(r.rows[0]?.supplier_name).not.toBe('hijacked')
  })
})
