import { describe, expect, it } from '@jest/globals';
import fs from 'node:fs';
import path from 'node:path';

const schemaPath = path.resolve(__dirname, '../../../prisma/schema.prisma');
const migrationPath = path.resolve(
  __dirname,
  '../../../prisma/migrations/20260824000000_async_manifest_delivery/migration.sql'
);

const modelBlock = (schema: string, modelName: string): string => {
  const match = schema.match(new RegExp(`model ${modelName} \\{([\\s\\S]*?)\\n\\}`));
  if (!match) throw new Error(`Missing Prisma model: ${modelName}`);
  return match[1];
};

describe('async translation manifest schema contract', () => {
  it('adds nullable, globally unique submission identity and fingerprint fields', () => {
    const schema = fs.readFileSync(schemaPath, 'utf8');
    const job = modelBlock(schema, 'TranslationJob');

    expect(job).toMatch(/submission_id\s+String\?\s+@unique\s+@db\.Uuid/);
    expect(job).toMatch(/submission_fingerprint\s+String\?\s+@db\.Char\(64\)/);
  });

  it('preserves queue, lease, callback, and multiple-outbox-row fields', () => {
    const schema = fs.readFileSync(schemaPath, 'utf8');
    const job = modelBlock(schema, 'TranslationJob');
    const outbox = modelBlock(schema, 'WebhookOutbox');

    expect(job).toMatch(/callback_url\s+String\?/);
    expect(job).toMatch(/callback_secret\s+String\?/);
    expect(job).toMatch(/processing_lease_id\s+String\?\s+@db\.Uuid/);
    expect(job).toMatch(/processing_lease_expires_at\s+DateTime\?/);
    expect(job).toMatch(/queue_lease_id\s+String\?\s+@db\.Uuid/);
    expect(job).toMatch(/queue_lease_expires_at\s+DateTime\?/);
    expect(job).toMatch(/queue_payload\s+Json\?/);

    expect(outbox).toMatch(/delivery_sequence\s+Int\s+@default\(0\)/);
    expect(outbox).toMatch(/is_final\s+Boolean\s+@default\(true\)/);
    expect(outbox).toContain('@@unique([job_id, event, delivery_sequence])');
    expect(outbox).toContain('@@index([status, created_at])');
  });

  it('adds durable outbox timing and dead-letter fields with due and stale indexes', () => {
    const schema = fs.readFileSync(schemaPath, 'utf8');
    const outbox = modelBlock(schema, 'WebhookOutbox');

    expect(outbox).toMatch(/next_attempt_at\s+DateTime\?/);
    expect(outbox).toMatch(/first_attempt_at\s+DateTime\?/);
    expect(outbox).toMatch(/last_attempt_at\s+DateTime\?/);
    expect(outbox).toMatch(/dead_at\s+DateTime\?/);
    expect(outbox).toMatch(/response_status\s+Int\?/);
    expect(outbox).toContain('@@index([status, next_attempt_at])');
    expect(outbox).toContain('@@index([status, claimed_at])');
  });

  it('uses nullable additive columns and concurrent-safe indexes for populated tables', () => {
    const migration = fs.readFileSync(migrationPath, 'utf8');

    expect(migration).toContain('ADD COLUMN "submission_id" UUID');
    expect(migration).toContain('ADD COLUMN "submission_fingerprint" CHAR(64)');
    expect(migration).toContain('ADD COLUMN "next_attempt_at" TIMESTAMP(3)');
    expect(migration).toContain('ADD COLUMN "first_attempt_at" TIMESTAMP(3)');
    expect(migration).toContain('ADD COLUMN "last_attempt_at" TIMESTAMP(3)');
    expect(migration).toContain('ADD COLUMN "dead_at" TIMESTAMP(3)');
    expect(migration).toContain('ADD COLUMN "response_status" INTEGER');
    expect(migration).not.toMatch(/ADD COLUMN[^;]+NOT NULL/i);

    expect(migration).toContain(
      'CREATE UNIQUE INDEX CONCURRENTLY "translation_jobs_submission_id_key"'
    );
    expect(migration).toContain(
      'CREATE INDEX CONCURRENTLY "webhook_outbox_status_next_attempt_at_idx"'
    );
    expect(migration).toContain(
      'CREATE INDEX CONCURRENTLY "webhook_outbox_status_claimed_at_idx"'
    );
    expect(migration).not.toMatch(/\b(?:DROP|DELETE|UPDATE|TRUNCATE)\b/i);
  });
});
