import type { MultidealEnv } from '@/server/env';
import { getDb } from '@/server/do-host/lib/db';
import { recordDeadLetter } from '@/server/monitor/dead-letter';
import { handleLlmJobsBatch } from './queues/llm-jobs-consumer';
import { handleOutboxBatch } from './queues/outbox-consumer';
import {
  handleSettlementBatch,
  type SettlementReleaseMessage,
} from './queues/settlements-consumer';
import { handleTranslationBatch } from './queues/translation-consumer';

type QueueHandler = (
  batch: MessageBatch<unknown>,
  env: MultidealEnv,
  ctx: ExecutionContext,
) => Promise<void>;

export type QueueEntry = {
  queue: string;
  producerBinding: string | null;
  deadLetterQueue: string | null;
  maxBatchSize: number;
  maxBatchTimeout: number;
  maxRetries: number;
  retryDelay: number | null;
  handler: QueueHandler;
};

const deadLetterKeys: Record<string, string> = {
  'multideal-outbox-dlq-preview': 'outbox-dlq',
  'multideal-llm-jobs-dlq-preview': 'llm-jobs-dlq',
  'multideal-translation-jobs-dlq-preview': 'translation-jobs-dlq',
};

const handleSettlementDlq: QueueHandler = async (batch, env) => {
  const db = getDb({ DATABASE_URL: env.DATABASE_URL });
  for (const message of batch.messages) {
    try {
      const body = message.body as Partial<SettlementReleaseMessage>;
      console.error(
        JSON.stringify({
          event: 'settlement_dead_lettered',
          batchKey: body?.batchKey,
          releaseIds: body?.releaseIds,
        }),
      );
      await recordDeadLetter(db, 'settlements-dlq', JSON.stringify({ batchKey: body?.batchKey }));
    } catch (logErr) {
      console.error(
        JSON.stringify({
          event: 'settlement_dead_lettered_log_failed',
          messageId: message.id,
          error: logErr instanceof Error ? logErr.name : 'unknown',
        }),
      );
    } finally {
      message.ack();
    }
  }
};

const handleWorkDlq: QueueHandler = async (batch, env) => {
  const db = getDb({ DATABASE_URL: env.DATABASE_URL });
  for (const message of batch.messages) {
    try {
      console.error(
        JSON.stringify({
          event: 'queue_dead_lettered',
          queue: batch.queue,
          messageId: message.id,
          timestamp: message.timestamp,
          body: message.body,
        }),
      );
      const deadLetterKey = deadLetterKeys[batch.queue];
      if (deadLetterKey)
        await recordDeadLetter(db, deadLetterKey, JSON.stringify({ messageId: message.id }));
    } catch (logErr) {
      console.error(
        JSON.stringify({
          event: 'queue_dead_lettered_log_failed',
          queue: batch.queue,
          messageId: message.id,
          error: logErr instanceof Error ? logErr.name : 'unknown',
        }),
      );
    } finally {
      message.ack();
    }
  }
};

export const queueRegistry: readonly QueueEntry[] = [
  {
    queue: 'multideal-outbox-preview',
    producerBinding: 'OUTBOX_QUEUE',
    deadLetterQueue: 'multideal-outbox-dlq-preview',
    maxBatchSize: 10,
    maxBatchTimeout: 5,
    maxRetries: 3,
    retryDelay: null,
    handler: (batch, env, ctx) =>
      handleOutboxBatch(batch as MessageBatch<{ outboxId: string }>, env, ctx),
  },
  {
    queue: 'multideal-llm-jobs-preview',
    producerBinding: 'LLM_JOBS_QUEUE',
    deadLetterQueue: 'multideal-llm-jobs-dlq-preview',
    maxBatchSize: 10,
    maxBatchTimeout: 5,
    maxRetries: 3,
    retryDelay: null,
    handler: (batch, env) => handleLlmJobsBatch(batch as MessageBatch<{ jobId: string }>, env),
  },
  {
    queue: 'multideal-translation-jobs-preview',
    producerBinding: 'TRANSLATION_QUEUE',
    deadLetterQueue: 'multideal-translation-jobs-dlq-preview',
    maxBatchSize: 10,
    maxBatchTimeout: 5,
    maxRetries: 0,
    retryDelay: null,
    handler: (batch, env, ctx) =>
      handleTranslationBatch(batch as MessageBatch<{ jobId: string }>, env, ctx),
  },
  {
    queue: 'multideal-settlements-preview',
    producerBinding: 'SETTLEMENTS',
    deadLetterQueue: 'multideal-settlements-dlq-preview',
    maxBatchSize: 10,
    maxBatchTimeout: 5,
    maxRetries: 5,
    retryDelay: 60,
    handler: (batch, env) =>
      handleSettlementBatch(batch as MessageBatch<SettlementReleaseMessage>, env),
  },
  {
    queue: 'multideal-settlements-dlq-preview',
    producerBinding: null,
    deadLetterQueue: null,
    maxBatchSize: 10,
    maxBatchTimeout: 30,
    maxRetries: 1,
    retryDelay: null,
    handler: handleSettlementDlq,
  },
  {
    queue: 'multideal-outbox-dlq-preview',
    producerBinding: null,
    deadLetterQueue: null,
    maxBatchSize: 10,
    maxBatchTimeout: 30,
    maxRetries: 1,
    retryDelay: null,
    handler: handleWorkDlq,
  },
  {
    queue: 'multideal-llm-jobs-dlq-preview',
    producerBinding: null,
    deadLetterQueue: null,
    maxBatchSize: 10,
    maxBatchTimeout: 30,
    maxRetries: 1,
    retryDelay: null,
    handler: handleWorkDlq,
  },
  {
    queue: 'multideal-translation-jobs-dlq-preview',
    producerBinding: null,
    deadLetterQueue: null,
    maxBatchSize: 10,
    maxBatchTimeout: 30,
    maxRetries: 1,
    retryDelay: null,
    handler: handleWorkDlq,
  },
];

export async function dispatchQueue(
  batch: MessageBatch<unknown>,
  env: Env,
  ctx: ExecutionContext,
): Promise<void> {
  const md = env as unknown as MultidealEnv;
  const entry = queueRegistry.find((candidate) => candidate.queue === batch.queue);
  if (!entry) throw new Error(`Unknown queue: ${batch.queue}`);
  await entry.handler(batch, md, ctx);
}
