import { HttpException } from "@nestjs/common";
import { randomBytes } from "node:crypto";
import { sql } from "drizzle-orm";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { getDb, getSql, schema, withUser } from "@slackwsh/data";
import { WorkspacesService } from "./workspaces.service";
import { StatusService } from "./status.service";
import { EmailService } from "../auth/email.service";

const hasDatabase = Boolean(process.env.DATABASE_URL);

afterAll(async () => {
  if (hasDatabase) await getSql().end({ timeout: 1 });
});

describe.skipIf(!hasDatabase)("cross-tenant isolation — workspaces HTTP", () => {
  let wsA: number, wsB: number, userA: number, userB: number;
  let service: WorkspacesService;

  beforeAll(async () => {
    const db = getDb();
    const suffix = randomBytes(4).toString("hex");
    const [orgA] = await db.insert(schema.organizations).values({ name: "ws-iso-a" }).returning();
    const [orgB] = await db.insert(schema.organizations).values({ name: "ws-iso-b" }).returning();
    userA = await db
      .insert(schema.users)
      .values({ email: `ws-a-${suffix}@test.local`, username: `wsa${suffix}`, passwordHash: "x", name: "A" })
      .returning({ id: schema.users.id })
      .then((r) => r[0]!.id);
    userB = await db
      .insert(schema.users)
      .values({ email: `ws-b-${suffix}@test.local`, username: `wsb${suffix}`, passwordHash: "x", name: "B" })
      .returning({ id: schema.users.id })
      .then((r) => r[0]!.id);

    async function own(userId: number, orgId: number, slug: string) {
      return withUser(userId, async (tx) => {
        const [workspace] = await tx.insert(schema.workspaces).values({ orgId, slug, name: slug }).returning();
        if (!workspace) throw new Error("workspace insert failed");
        await tx.execute(sql`select set_config('app.current_workspace_id', ${String(workspace.id)}, true)`);
        await tx.insert(schema.workspaceMembers).values({ workspaceId: workspace.id, userId, role: "owner" });
        return workspace.id;
      });
    }
    wsA = await own(userA, orgA!.id, `ws-iso-a-${suffix}`);
    wsB = await own(userB, orgB!.id, `ws-iso-b-${suffix}`);

    service = new WorkspacesService(
      { sendInviteEmail: async () => undefined } as unknown as EmailService,
      {
        resolveMemberRows: async (_ws: number, _user: number, rows: unknown) => rows,
      } as unknown as StatusService,
    );
  });

  async function expectForbidden(fn: () => Promise<unknown>) {
    try {
      await fn();
    } catch (err) {
      if (err instanceof HttpException) {
        expect(err.getStatus()).toBe(403);
        return;
      }
      throw err;
    }
    throw new Error("expected 403");
  }

  it("listMembers / admin / invites / settings reject a foreign workspace", async () => {
    expect(await service.getMembership(wsB, userA)).toBeNull();
    await expectForbidden(() => service.listMembers(wsB, userA));
    await expectForbidden(() => service.adminOverview(wsB, userA));
    await expectForbidden(() =>
      service.createInvite(wsB, userA, { email: "x@test.local", role: "member", expiresInHours: 24, maxUses: 1 }),
    );
    await expectForbidden(() => service.updateSettings(wsB, userA, { retentionDays: 30 }));
  });

  it("listMyWorkspaces does not include a workspace the user is not in", async () => {
    const mine = await service.listMyWorkspaces(userA);
    expect(mine.map((w) => w.id)).toEqual([wsA]);
  });
});
