// apps/web/src/server/notifications/send.ts
import type { DrizzleClient } from '@/server/db/client.js';
import type { MultidealEnv } from '@/server/env.js';
import { insertNotification } from '@/server/db/queries/live-notifications.js';
import { insertOutboxRow } from '@/server/db/queries/outbox.js';
import { loadPrefs, allowsEvent, type NotifPrefs } from '@/server/db/queries/notif-prefs.js';
import { EVENT_REGISTRY, type NotifEvent, type NotifTier } from './event-registry.js';
import { renderTitle, renderBody } from './render.js';

export type WriteCtx = { db: DrizzleClient; env: MultidealEnv; req?: Request };
export type ChannelName = string;

export async function writeNotification(args: {
  userId: string;
  event: NotifEvent;
  data?: unknown;
  ctx: WriteCtx;
  link?: string;
  _testHooks?: { prefs?: NotifPrefs };
}): Promise<void> {
  const meta = EVENT_REGISTRY[args.event];
  const tier: NotifTier = meta.tier;
  const prefs = args._testHooks?.prefs ?? (await loadPrefs(args.ctx.db, args.userId));
  if (!allowsEvent(prefs, args.event, tier)) return;

  const title = renderTitle(args.event, args.data);
  const body = renderBody(args.event, args.data);
  const row = await insertNotification(args.ctx.db, {
    userId: args.userId,
    event: args.event,
    tier,
    titleHe: title.he,
    titleEn: title.en,
    bodyHe: body.he,
    bodyEn: body.en,
    link: args.link ?? null,
    payload: (args.data as Record<string, unknown>) ?? {},
    expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
  });

  if (meta.durable) {
    const outboxRow = await insertOutboxRow(args.ctx.db, {
      aggregateType: 'notification',
      aggregateId: args.userId,
      eventType: 'notification.deliver',
      payload: { notificationId: row.id, userId: args.userId },
    });
    await args.ctx.env.OUTBOX_QUEUE.send({ outboxId: outboxRow.id });
  }
}

export async function publishLive(args: {
  channel: ChannelName;
  event: NotifEvent;
  payload: unknown;
  ctx: { env: MultidealEnv };
}): Promise<void> {
  const meta = EVENT_REGISTRY[args.event];
  const stub = args.ctx.env.TOPIC_DO.get(args.ctx.env.TOPIC_DO.idFromName(`topic:${args.channel}`));
  await (
    stub as unknown as {
      publish(e: {
        ch: string;
        kind: string;
        tier: string;
        event: string;
        payload: unknown;
        ts: number;
      }): Promise<void>;
    }
  ).publish({
    ch: args.channel,
    kind: 'live',
    tier: meta.tier,
    event: args.event,
    payload: args.payload,
    ts: Date.now(),
  });
}
