// Integration test for db.withCompany (Task 2 of
// docs/superpowers/plans/2026-07-31-local-app.md): creates two companies
// through the real HTTP API, then proves withCompany's transaction-scoped
// app.company_id actually enforces RLS — an insert made under company A's
// scope must be completely invisible under company B's.

import { describe, it, expect } from "vitest";
import { createTestDb } from "../helpers/db.js";
// Where the database is, resolved once for the whole run: your ADMIN_URL /
// DATABASE_URL if you set one, otherwise the PostgreSQL Balanced Sum installed on
// this machine. Every file in this directory used to carry its own copy of that
// decision, hardcoded to the Docker container that no longer exists.
import { dbUrl } from "../helpers/test-db-admin.js";
import { buildApp } from "../../server/src/app.js";

async function buildTestApp(dbName: string) {
  return buildApp({
    databaseUrl: await dbUrl(dbName, "app", "app"),
    adminDatabaseUrl: await dbUrl(dbName),
  });
}

// Each test provisions its own database inside the test body, so it gets the
// same 60s budget the rest of the suite gives its beforeAll provisioning
// rather than vitest's 5s default. See tests/server/companies.test.ts.
const PROVISION = 60_000;

describe("db.withCompany", () => {
  it("scopes RLS strictly to one company per transaction", async () => {
    const testDb = await createTestDb();
    const app = await buildTestApp(testDb.dbName);
    try {
      const postA = await app.inject({
        method: "POST", url: "/api/companies", payload: { name: "WithCompany A" },
      });
      const postB = await app.inject({
        method: "POST", url: "/api/companies", payload: { name: "WithCompany B" },
      });
      const companyAId = (postA.json() as { id: string }).id;
      const companyBId = (postB.json() as { id: string }).id;

      await app.db.withCompany(companyAId, async (client) => {
        await client.query(
          `INSERT INTO accounts (company_id, code, name, type) VALUES ($1, '1000', 'Cash', 'asset')`,
          [companyAId],
        );
      });

      await app.db.withCompany(companyBId, async (client) => {
        const res = await client.query("SELECT * FROM accounts");
        expect(res.rows).toHaveLength(0);
      });

      await app.db.withCompany(companyAId, async (client) => {
        const res = await client.query("SELECT name FROM accounts");
        expect(res.rows).toHaveLength(1);
        expect(res.rows[0]!.name).toBe("Cash");
      });
    } finally {
      await app.close();
      await testDb.close();
    }
  }, PROVISION);

  it("rolls back on error, leaving nothing behind (and releases the client cleanly)", async () => {
    const testDb = await createTestDb();
    const app = await buildTestApp(testDb.dbName);
    try {
      const post = await app.inject({
        method: "POST", url: "/api/companies", payload: { name: "Rollback Co" },
      });
      const companyId = (post.json() as { id: string }).id;

      await expect(
        app.db.withCompany(companyId, async (client) => {
          await client.query(
            `INSERT INTO accounts (company_id, code, name, type) VALUES ($1, '1000', 'Cash', 'asset')`,
            [companyId],
          );
          throw new Error("boom");
        }),
      ).rejects.toThrow("boom");

      await app.db.withCompany(companyId, async (client) => {
        const res = await client.query("SELECT * FROM accounts");
        expect(res.rows).toHaveLength(0);
      });
    } finally {
      await app.close();
      await testDb.close();
    }
  }, PROVISION);
});
