// RLS isolation for qaf_process_mappings after the KAR-890 fix.
//
// qaf_process_mappings (Stoppuhr-Matching, KAR-103) originally shipped with a
// weaker policy than the 14 qaf_* QAF-Differences tables: `qaf_process_mappings_auth_all`
// used `USING (auth.uid() IS NOT NULL)` — any logged-in user could read/write
// mappings across every project, not just their own. KAR-890 replaces it with
// the standard `_own`/`_admin` pair (same predicate as qaf_comparison in
// rls-qaf-differences-isolation.test.ts). Potentially commercially sensitive
// QAF cost-structure/matching data must never leak across owners.
//
// Gated on RLS_TEST_DATABASE_URL → skips in CI, runs via scripts/rls-test/run.sh.

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'
const USER_A_PROJECT = '9999aaaa-0000-0000-0000-0000000000a1'
const USER_A_PROCESS_STEP = '9999aaaa-0000-0000-0000-0000000000b1'

describe.skipIf(!DB_URL)('RLS isolation — qaf_process_mappings (KAR-890)', () => {
  let client: Client

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

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

  async function asUser(uid: string, sql: string, params: unknown[] = []) {
    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, params)
  }

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

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

  it('user_b UPDATE on user_a qaf_process_mappings affects 0 rows', async () => {
    const r = await asUser(
      USER_B,
      "UPDATE public.qaf_process_mappings SET qaf_row_key = 'hijacked' WHERE qaf_row_key = 'USERA-ROW-1'",
    )
    expect(r.rowCount).toBe(0)
  })

  it('user_b DELETE on user_a qaf_process_mappings affects 0 rows', async () => {
    const r = await asUser(USER_B, "DELETE FROM public.qaf_process_mappings WHERE qaf_row_key = 'USERA-ROW-1'")
    expect(r.rowCount).toBe(0)
  })

  it('user_b INSERT into user_a project is rejected by RLS', async () => {
    await expect(
      asUser(
        USER_B,
        'INSERT INTO public.qaf_process_mappings (project_id, process_step_id, qaf_row_key, match_source) VALUES ($1, $2, $3, $4)',
        [USER_A_PROJECT, USER_A_PROCESS_STEP, 'INJECTED', 'manual'],
      ),
    ).rejects.toThrow(/row-level security/i)
  })

  it('the qaf_process_mappings row is still intact after user_b write attempts', async () => {
    const r = await asUser(
      USER_A,
      'SELECT qaf_row_key FROM public.qaf_process_mappings WHERE project_id = $1 LIMIT 1',
      [USER_A_PROJECT],
    )
    expect(r.rows[0]?.qaf_row_key).toBe('USERA-ROW-1')
  })
})
