import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import { PrismaClient } from '@prisma/client';
import { DlqService } from '../outbox/dlq.service';
import { resetDb } from '../test-utils/reset-db';
import { makeFakePorts, portalMessageBasic, replayTwice, isIdempotent } from '@insignia/iios-testkit';
import { IngestService } from '../interactions/ingest.service';
import { OutboxRelay } from './outbox.relay';
import { OutboxBus } from './outbox.bus';
import type { PrismaService } from '../prisma/prisma.service';

const url = process.env.DATABASE_URL ?? 'postgresql://iios:iios@localhost:5434/iios?schema=public';
const prisma = new PrismaClient({ datasources: { db: { url } } });
const asService = prisma as unknown as PrismaService;

async function clean(): Promise<void> {
  await resetDb(prisma);
}

beforeAll(async () => { await prisma.$connect(); });
afterAll(async () => { await prisma.$disconnect(); });
beforeEach(async () => { await clean(); });

describe('outbox relay + replay', () => {
  it('replaying a fixture twice produces no duplicate state', async () => {
    const ingest = new IngestService(asService, makeFakePorts());
    const result = await replayTwice(
      portalMessageBasic,
      (f) => ingest.ingest(f, f.providerEventId!),
      () => prisma.iiosInteraction.count(),
    );
    expect(isIdempotent(result)).toBe(true);
    expect(result.countAfterSecond).toBe(1);
  });

  it('relay publishes exactly one event per interaction, then nothing new', async () => {
    const ingest = new IngestService(asService, makeFakePorts());
    const bus = new OutboxBus();
    const received: string[] = [];
    bus.onAny((eventType) => received.push(eventType));
    const relay = new OutboxRelay(asService, bus, new DlqService(asService));

    await ingest.ingest(portalMessageBasic, portalMessageBasic.providerEventId!);

    const published = await relay.relayOnce();
    expect(published).toBe(1);
    expect(received).toEqual(['com.insignia.iios.interaction.normalized.v1']);
    expect(await prisma.iiosOutboxEvent.count({ where: { status: 'PUBLISHED' } })).toBe(1);
    expect(await prisma.iiosProcessedEvent.count()).toBe(1);

    // A second relay pass finds nothing PENDING.
    expect(await relay.relayOnce()).toBe(0);
  });
});
