import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { sql } from "drizzle-orm";
import { createPgliteClient } from "@platform-modules/db/postgres/pglite";
import { describe, expect, it } from "vitest";
import type {
  AuditRecord,
  AuditRecordId,
  BusinessEvent,
  CorrelationId,
  EventId,
  OutboxMessageId,
  PrincipalId,
  ProjectId,
} from "@awp/contracts";
import { unsafeOpaqueId } from "@awp/contracts";
import { PostgresUnitOfWork, schema } from "@awp/persistence";

const migrationPath = fileURLToPath(
  new URL("../../packages/persistence/drizzle/0000_bizarre_midnight.sql", import.meta.url),
);

async function database() {
  const db = createPgliteClient({ schema });
  const migration = readFileSync(migrationPath, "utf8");
  for (const statement of migration.split("--> statement-breakpoint")) {
    const sqlText = statement.trim();
    if (sqlText.length > 0) await db.execute(sql.raw(sqlText));
  }
  return db;
}

function fixtures(suffix: string) {
  const projectId = unsafeOpaqueId<ProjectId>(`project-${suffix}`);
  const correlationId = unsafeOpaqueId<CorrelationId>(`correlation-${suffix}`);
  const principalId = unsafeOpaqueId<PrincipalId>(`principal-${suffix}`);
  const event: BusinessEvent = {
    id: unsafeOpaqueId<EventId>(`event-${suffix}`),
    type: "project.created",
    schemaVersion: 1,
    occurredAt: "2026-08-20T00:00:00Z",
    aggregateType: "Project",
    aggregateId: projectId,
    aggregateRevision: 1,
    projectId,
    principalId,
    correlationId,
    payload: { projectId },
  };
  const audit: AuditRecord = {
    id: unsafeOpaqueId<AuditRecordId>(`audit-${suffix}`),
    occurredAt: "2026-08-20T00:00:00Z",
    principalId,
    action: "project.create",
    targetType: "Project",
    targetId: projectId,
    projectId,
    disposition: "allowed",
    correlationId,
    safeMetadata: {},
  };
  return { projectId, event, audit, outboxId: unsafeOpaqueId<OutboxMessageId>(`outbox-${suffix}`) };
}

describe("PostgreSQL foundation transaction", () => {
  it("commits authoritative state, event, audit and outbox atomically", async () => {
    const db = await database();
    const uow = new PostgresUnitOfWork(db);
    const f = fixtures("commit");
    await uow.transaction(async (tx) => {
      await tx.projects.insert({ id: f.projectId, revision: 1 });
      await tx.events.append(f.event);
      await tx.audit.append(f.audit);
      await tx.outbox.append({
        id: f.outboxId,
        topic: "business-event",
        payload: f.event,
        occurredAt: f.event.occurredAt,
      });
    });
    expect((await db.select().from(schema.projects)).map((row) => row.id)).toContain(f.projectId);
    expect((await db.select().from(schema.businessEvents)).map((row) => row.id)).toContain(
      f.event.id,
    );
    expect((await db.select().from(schema.auditRecords)).map((row) => row.id)).toContain(
      f.audit.id,
    );
    expect((await db.select().from(schema.outboxMessages)).map((row) => row.id)).toContain(
      f.outboxId,
    );
    await db.$client.close();
  });

  it("rolls all four families back when the application transaction fails", async () => {
    const db = await database();
    const uow = new PostgresUnitOfWork(db);
    const f = fixtures("rollback");
    await expect(
      uow.transaction(async (tx) => {
        await tx.projects.insert({ id: f.projectId, revision: 1 });
        await tx.events.append(f.event);
        throw new Error("injected failure before audit/outbox");
      }),
    ).rejects.toThrow(/injected failure/);
    expect(await db.select().from(schema.projects)).toHaveLength(0);
    expect(await db.select().from(schema.businessEvents)).toHaveLength(0);
    expect(await db.select().from(schema.auditRecords)).toHaveLength(0);
    expect(await db.select().from(schema.outboxMessages)).toHaveLength(0);
    await db.$client.close();
  });
});
