/**
 * Outbox dispatcher — routes outbox events to their registered handlers.
 *
 * Used by BOTH the cron safety-net (process-outbox.ts) and the queue consumer
 * (src/server/platform/queues/outbox-consumer.ts).
 *
 * Behaviour:
 * - Looks up handler by event.eventType in HANDLER_BY_TYPE.
 * - Unknown types: console.warn + ack silently (do NOT throw — protects in-flight).
 * - Validation failure: captureCaught(warning) + RE-THROW (existing retry handles).
 * - Handler throws: propagates to caller for retry/DLQ.
 */

import { CACHE_INVALIDATE_EVENT } from '@/server/cache/invalidate.js';
import { withSentry } from '@/server/observability/with-sentry';
import { captureCaught } from '@/server/observability/capture.server';
import { HANDLER_BY_TYPE } from './registry.js';
import type { DispatchEnv } from './types.js';
import type { OutboxEvent } from '../../db/queries/outbox.js';

type CacheDispatchEnv = DispatchEnv & { CACHE_EPOCH_DO: DurableObjectNamespace };

async function dispatchOutboxRowImpl(env: DispatchEnv, event: OutboxEvent): Promise<void> {
  if (event.eventType === CACHE_INVALIDATE_EVENT) {
    const cacheEnv = env as CacheDispatchEnv;
    const stub = cacheEnv.CACHE_EPOCH_DO.get(cacheEnv.CACHE_EPOCH_DO.idFromName('catalog'));
    await stub.fetch(
      new Request('https://do/coalesce', {
        method: 'POST',
        body: JSON.stringify(event.payload),
        headers: { 'Content-Type': 'application/json' },
      }),
    );
    return;
  }

  const handler = HANDLER_BY_TYPE.get(event.eventType);

  if (!handler) {
    // Unknown type: warn and ack silently — do not throw so in-flight unknown
    // events are not DLQ'd. The cron sweep logs this for investigation.
    console.warn(
      JSON.stringify({
        event: 'outbox_unknown_kind',
        outboxId: event.id,
        eventType: event.eventType,
      }),
    );
    return;
  }

  // Parse payload with handler's schema (passthrough — v1 rule).
  const parseResult = handler.payloadSchema.safeParse(event.payload);
  if (!parseResult.success) {
    const err = new Error(
      `[outbox] payload validation failed for ${event.eventType}: ${parseResult.error.message}`,
    );
    captureCaught(err, {
      scope: `server.workflows.outbox.${event.eventType}`,
      severity: 'warning',
    });
    // RE-THROW: existing retry / DLQ machinery handles poison messages.
    throw err;
  }

  await handler.handle(env, parseResult.data, event);
}

/**
 * Per-row outbox dispatch boundary — wrapped in `withSentry` so each row's
 * side effect gets its own span. Both the cron sweep and queue consumer funnel
 * through this single entry point.
 */
export const dispatchOutboxRow = withSentry(dispatchOutboxRowImpl, {
  name: 'outbox.dispatch',
  kind: 'outbox',
});

export type { DispatchEnv } from './types.js';
