// Plan-engine tests. `planNotificationInternal` resolves the audience,
// applies the preference/transactional/critical-bypass gates, renders, and
// writes the outbox rows (Notification QUEUED + DestinationDeliveryLog PENDING).
// It performs NO external delivery — the channel adapters are never called here;
// send behavior lives in internal/deliverNotification.test.ts and
// internal/deliverDestination.test.ts.

import type { CommandContext } from "@tailor-platform/erp-kit/core";
import { createKyselyMock, type KyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it, vi } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import { type DestinationAdapter, type DispatchAdapters } from "../lib/dispatchAdapters";
import { CategoryNotFoundError } from "../lib/errors.generated";
import {
  baseAllowedPreference,
  baseDisabledEmailChannel,
  baseDisabledSlackChannel,
  baseEmailChannel,
  baseEventCategoryBinding,
  baseInAppChannel,
  baseOptionalEventBinding,
  baseOptOutPreference,
  baseQueuedNotification,
  baseSlackBinding,
  baseSlackChannel,
  baseSlackTemplate,
  baseTemplate,
  baseTransactionalCategory,
} from "../testing/fixtures";
import { planNotificationInternal, type PlanNotificationInput } from "./planNotification";

type Mock = KyselyMock<Namespace[keyof Namespace]>;

const ctx: CommandContext = {
  actorId: "system-emitter",
  permissions: ["notification:ingest:logNotificationEvent"],
};

interface SelectStub {
  table: string;
  match?: Record<string, unknown>;
  value: unknown;
}

function stageQueries(mock: Mock, stubs: SelectStub[], insertValue?: unknown) {
  mock.setQueryResolver((query) => {
    const sql = query.sql;
    if (sql.startsWith("select")) {
      const tableMatch = /from "([^"]+)"/.exec(sql);
      const table = tableMatch?.[1] ?? null;
      const cols = Array.from(sql.matchAll(/"([^"]+)" = \$\d+/g)).map((m) => m[1]);
      const where: Record<string, unknown> = {};
      cols.forEach((col, i) => {
        where[col] = query.parameters[i];
      });
      const stub = stubs.find((s) => {
        if (s.table !== table) return false;
        if (!s.match) return true;
        return Object.entries(s.match).every(([k, v]) => where[k] === v);
      });
      return stub?.value === undefined ? [] : [stub.value as Record<string, unknown>];
    }
    if (sql.startsWith("insert")) {
      const tableMatch = /into "([^"]+)"/.exec(sql);
      const table = tableMatch?.[1] ?? null;
      if (table === "Notification" && insertValue !== undefined) {
        return [insertValue as Record<string, unknown>];
      }
      return [];
    }
    return [];
  });
}

function makeAdapters(overrides: Partial<DispatchAdapters> = {}): DispatchAdapters {
  return {
    inApp: { send: vi.fn(() => Promise.resolve({ ok: true as const })) },
    email: {
      send: vi.fn(() => Promise.resolve({ ok: true as const, messageId: "sg-msg-1" })),
    },
    resolveRecipient: vi.fn((_db: unknown, userId: string) =>
      Promise.resolve({
        userId,
        locale: "en-US",
        addresses: { IN_APP: userId, EMAIL: `${userId}@example.com` },
      }),
    ),
    now: () => new Date("2024-06-15T12:00:00.000Z"),
    ...overrides,
  };
}

const inAppTemplate = { ...baseTemplate, channelId: "channel-1" };
const emailTemplate = {
  ...baseTemplate,
  id: "template-2",
  channelId: "channel-2",
  htmlBody: "<p>{{poNumber}}</p>",
};

const dispatchInput: PlanNotificationInput = {
  eventType: "PO_ISSUED",
  sourceType: "PURCHASE_ORDER",
  sourceId: "po-1",
  recipients: [{ userId: "user-1", reason: "SUBSCRIBED" }],
  payloadVars: { poNumber: "PO-001" },
};

function notificationInserts(mock: Mock): Record<string, unknown>[] {
  return mock.inserts.map((q) => q.insertValues()).filter((row) => "deliveryStatus" in row);
}

describe("planNotification (plan engine)", () => {
  it("plans one QUEUED IN_APP and one QUEUED EMAIL Notification for one recipient on default preferences, without calling any channel adapter", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        { table: "EventCategoryBinding", value: baseEventCategoryBinding },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "NotificationChannel", match: { channelId: "EMAIL" }, value: baseEmailChannel },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
        { table: "NotificationTemplate", match: { channelId: "channel-2" }, value: emailTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const adapters = makeAdapters();
    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, dispatchInput, ctx, adapters),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.notifications).toHaveLength(2);
      for (const n of result.value.notifications) expect(n.deliveryStatus).toBe("QUEUED");
    }
    expect(adapters.inApp.send).not.toHaveBeenCalled();
    expect(adapters.email?.send).not.toHaveBeenCalled();
  });

  it("a newly persisted Notification has deliveryStatus=QUEUED and engagementStatuses=empty", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        { table: "EventCategoryBinding", value: baseEventCategoryBinding },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "NotificationChannel", match: { channelId: "EMAIL" }, value: baseEmailChannel },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
        { table: "NotificationTemplate", match: { channelId: "channel-2" }, value: emailTemplate },
      ],
      { ...baseQueuedNotification },
    );

    await mock.withTx((trx) => planNotificationInternal(trx, dispatchInput, ctx, makeAdapters()));

    const insertedRows = notificationInserts(mock);
    expect(insertedRows.length).toBeGreaterThan(0);
    for (const row of insertedRows) {
      expect(row.deliveryStatus).toBe("QUEUED");
      expect(row.engagementStatuses).toEqual([]);
    }
  });

  it("returns CATEGORY_NOT_FOUND when eventType is absent from the Event Catalog and persists zero Notifications", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(mock, [{ table: "EventCategoryBinding", value: undefined }]);

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, dispatchInput, ctx, makeAdapters()),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) expect(result.error).toBeInstanceOf(CategoryNotFoundError);
    expect(mock.inserts.length).toBe(0);
  });

  it("returns no-op success and persists zero Notifications when the resolved audience is empty", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(mock, [{ table: "EventCategoryBinding", value: baseEventCategoryBinding }]);

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, { ...dispatchInput, recipients: [] }, ctx, makeAdapters()),
    );

    expect(result.ok).toBe(true);
    if (result.ok) expect(result.value.notifications).toHaveLength(0);
    expect(mock.inserts.length).toBe(0);
  });

  it("fans out to five recipients × two channels and plans ten QUEUED Notifications when all opt in", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        { table: "EventCategoryBinding", value: baseEventCategoryBinding },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "NotificationChannel", match: { channelId: "EMAIL" }, value: baseEmailChannel },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
        { table: "NotificationTemplate", match: { channelId: "channel-2" }, value: emailTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const result = await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        {
          ...dispatchInput,
          recipients: ["u1", "u2", "u3", "u4", "u5"].map((userId) => ({
            userId,
            reason: "SUBSCRIBED" as const,
          })),
        },
        ctx,
        makeAdapters(),
      ),
    );

    expect(result.ok).toBe(true);
    if (result.ok) expect(result.value.notifications).toHaveLength(10);
  });

  it("skips the EMAIL channel for a recipient who opted out on EMAIL only and plans one IN_APP Notification", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        {
          table: "EventCategoryBinding",
          value: { ...baseOptionalEventBinding, defaultChannels: ["IN_APP", "EMAIL"] },
        },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "NotificationChannel", match: { channelId: "EMAIL" }, value: baseEmailChannel },
        { table: "Notification", value: undefined },
        {
          table: "NotificationPreference",
          match: { channelId: "channel-1" },
          value: { ...baseAllowedPreference, channelId: "channel-1" },
        },
        {
          table: "NotificationPreference",
          match: { channelId: "channel-2" },
          value: { ...baseOptOutPreference, channelId: "channel-2" },
        },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const result = await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        { ...dispatchInput, eventType: "ANNOUNCEMENT_PUBLISHED" },
        ctx,
        makeAdapters(),
      ),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.notifications).toHaveLength(1);
      expect(result.value.notifications[0].channelId).toBe("channel-1");
    }
  });

  it("plans zero Notifications and surfaces a PREFERENCE_MUTED skip per pair when the recipient opted out on every channel for an optional category", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(mock, [
      {
        table: "EventCategoryBinding",
        value: { ...baseOptionalEventBinding, defaultChannels: ["IN_APP", "EMAIL"] },
      },
      { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
      { table: "NotificationChannel", match: { channelId: "EMAIL" }, value: baseEmailChannel },
      { table: "Notification", value: undefined },
      { table: "NotificationPreference", value: { ...baseOptOutPreference } },
    ]);

    const result = await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        { ...dispatchInput, eventType: "ANNOUNCEMENT_PUBLISHED" },
        ctx,
        makeAdapters(),
      ),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.notifications).toHaveLength(0);
      expect(result.value.skipped).toEqual(
        expect.arrayContaining([
          { recipientUserId: "user-1", channelId: "IN_APP", reason: "PREFERENCE_MUTED" },
          { recipientUserId: "user-1", channelId: "EMAIL", reason: "PREFERENCE_MUTED" },
        ]),
      );
    }
    expect(mock.inserts.length).toBe(0);
  });

  it("plans one Notification per enabled channel for a transactional category even when the recipient opted out everywhere", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        { table: "EventCategoryBinding", value: baseEventCategoryBinding },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "NotificationChannel", match: { channelId: "EMAIL" }, value: baseEmailChannel },
        { table: "Notification", value: undefined },
        { table: "NotificationPreference", value: { ...baseOptOutPreference } },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
        { table: "NotificationTemplate", match: { channelId: "channel-2" }, value: emailTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, dispatchInput, ctx, makeAdapters()),
    );

    expect(result.ok).toBe(true);
    if (result.ok) expect(result.value.notifications).toHaveLength(2);
  });

  it("treats a binding as transactional when its category has optOutAllowed=false even if binding.transactional=false", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        { table: "EventCategoryBinding", value: baseOptionalEventBinding },
        {
          table: "NotificationCategory",
          value: { ...baseTransactionalCategory, id: "category-2" },
        },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "Notification", value: undefined },
        {
          table: "NotificationPreference",
          value: { ...baseOptOutPreference, channelId: "channel-1" },
        },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const result = await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        { ...dispatchInput, eventType: "ANNOUNCEMENT_PUBLISHED" },
        ctx,
        makeAdapters(),
      ),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.notifications).toHaveLength(1);
      expect(result.value.notifications[0]?.deliveryStatus).toBe("QUEUED");
      expect(result.value.skipped).toHaveLength(0);
    }
  });

  it("records a CHANNEL_DISABLED skip for a default channel whose NotificationChannel row is disabled, while siblings are planned", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        { table: "EventCategoryBinding", value: baseEventCategoryBinding },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        {
          table: "NotificationChannel",
          match: { channelId: "EMAIL" },
          value: baseDisabledEmailChannel,
        },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, dispatchInput, ctx, makeAdapters()),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.notifications).toHaveLength(1);
      expect(result.value.skipped).toEqual([
        { recipientUserId: "user-1", channelId: "EMAIL", reason: "CHANNEL_DISABLED" },
      ]);
    }
  });

  it("falls back to the default-locale template when the recipient's locale template is missing", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        {
          table: "EventCategoryBinding",
          value: { ...baseEventCategoryBinding, defaultChannels: ["IN_APP"] },
        },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { locale: "ja-JP" }, value: undefined },
        { table: "NotificationTemplate", match: { locale: "en-US" }, value: inAppTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const adapters = makeAdapters({
      resolveRecipient: vi.fn((_db: unknown, userId: string) =>
        Promise.resolve({
          userId,
          locale: "ja-JP",
          addresses: { IN_APP: userId, EMAIL: `${userId}@example.com` },
        }),
      ),
    });

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, dispatchInput, ctx, adapters),
    );

    expect(result.ok).toBe(true);
    if (result.ok) expect(result.value.notifications).toHaveLength(1);
  });

  it("renders the override locale when supplied on the dispatch call", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        {
          table: "EventCategoryBinding",
          value: { ...baseEventCategoryBinding, defaultChannels: ["IN_APP"] },
        },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "Notification", value: undefined },
        {
          table: "NotificationTemplate",
          match: { locale: "fr-FR" },
          value: { ...inAppTemplate, locale: "fr-FR" },
        },
      ],
      { ...baseQueuedNotification },
    );

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, { ...dispatchInput, locale: "fr-FR" }, ctx, makeAdapters()),
    );

    expect(result.ok).toBe(true);
    if (result.ok) expect(result.value.notifications).toHaveLength(1);
  });

  it("marks a pair FAILED with TEMPLATE_NOT_FOUND at plan time and audits it, while the sibling pair stays QUEUED", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        { table: "EventCategoryBinding", value: baseEventCategoryBinding },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "NotificationChannel", match: { channelId: "EMAIL" }, value: baseEmailChannel },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-2" }, value: emailTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, dispatchInput, ctx, makeAdapters()),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      const inApp = result.value.notifications.find((n) => n.channelId === "channel-1");
      const email = result.value.notifications.find((n) => n.channelId === "channel-2");
      expect(inApp?.deliveryStatus).toBe("FAILED");
      expect(inApp?.reason).toBe("TEMPLATE_NOT_FOUND");
      expect(email?.deliveryStatus).toBe("QUEUED");
    }
    const failedAudits = mock.inserts
      .map((q) => q.insertValues())
      .filter((row) => row.eventType === "FAILED");
    expect(failedAudits).toHaveLength(1);
    expect(failedAudits[0].errorClass).toBe("TemplateNotFound");
  });

  it("marks a pair FAILED with MISSING_RECIPIENT_ADDRESS at plan time when the resolved user has no address for that channel", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        { table: "EventCategoryBinding", value: baseEventCategoryBinding },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "NotificationChannel", match: { channelId: "EMAIL" }, value: baseEmailChannel },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
        { table: "NotificationTemplate", match: { channelId: "channel-2" }, value: emailTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const adapters = makeAdapters({
      resolveRecipient: vi.fn((_db: unknown, userId: string) =>
        Promise.resolve({
          userId,
          locale: "en-US",
          addresses: { IN_APP: userId, EMAIL: null },
        }),
      ),
    });

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, dispatchInput, ctx, adapters),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      const email = result.value.notifications.find((n) => n.channelId === "channel-2");
      const inApp = result.value.notifications.find((n) => n.channelId === "channel-1");
      expect(email?.deliveryStatus).toBe("FAILED");
      expect(email?.reason).toBe("MISSING_RECIPIENT_ADDRESS");
      expect(inApp?.deliveryStatus).toBe("QUEUED");
    }
  });

  it("marks a pair FAILED with TEMPLATE_VAR_VALIDATION_FAILED when payloadVars violates the template variable schema", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        {
          table: "EventCategoryBinding",
          value: { ...baseEventCategoryBinding, defaultChannels: ["IN_APP"] },
        },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, { ...dispatchInput, payloadVars: {} }, ctx, makeAdapters()),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.notifications[0]?.deliveryStatus).toBe("FAILED");
      expect(result.value.notifications[0]?.reason).toBe("TEMPLATE_VAR_VALIDATION_FAILED");
    }
  });

  it("HTML-escapes payload variables in htmlBody (but not in subject/body) on the plan path", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        {
          table: "EventCategoryBinding",
          value: { ...baseEventCategoryBinding, defaultChannels: ["EMAIL"] },
        },
        { table: "NotificationChannel", match: { channelId: "EMAIL" }, value: baseEmailChannel },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-2" }, value: emailTemplate },
      ],
      { ...baseQueuedNotification },
    );

    await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        { ...dispatchInput, payloadVars: { poNumber: "<script>alert('x')</script>" } },
        ctx,
        makeAdapters(),
      ),
    );

    const row = notificationInserts(mock)[0] as { body?: string; htmlBody?: string };
    expect(row.htmlBody).toContain("&lt;script&gt;");
    expect(row.htmlBody).not.toContain("<script>");
    expect(row.body).toContain("<script>alert('x')</script>");
  });

  it("collapses a re-emission matching an existing idempotency key to the existing rows and reports them in deduped", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(mock, [
      {
        table: "EventCategoryBinding",
        value: { ...baseEventCategoryBinding, defaultChannels: ["IN_APP"] },
      },
      { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
      { table: "Notification", value: { ...baseQueuedNotification, deliveryStatus: "DELIVERED" } },
    ]);

    const result = await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        { ...dispatchInput, idempotencyKey: "explicit-key-1" },
        ctx,
        makeAdapters(),
      ),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.deduped).toHaveLength(1);
      expect(result.value.notifications).toHaveLength(0);
    }
    expect(mock.inserts.length).toBe(0);
  });

  it("produces two distinct idempotency keys across two distinct dayBuckets when no idempotencyKey is supplied", async () => {
    const insertedKeys: string[] = [];

    const plan = async (now: Date) => {
      const mock = createKyselyMock<Namespace[keyof Namespace]>();
      stageQueries(
        mock,
        [
          {
            table: "EventCategoryBinding",
            value: { ...baseEventCategoryBinding, defaultChannels: ["IN_APP"] },
          },
          { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
          { table: "Notification", value: undefined },
          { table: "NotificationTemplate", value: inAppTemplate },
        ],
        { ...baseQueuedNotification },
      );
      await mock.withTx((trx) =>
        planNotificationInternal(trx, dispatchInput, ctx, makeAdapters({ now: () => now })),
      );
      for (const q of mock.inserts) {
        const row = q.insertValues() as { idempotencyKey?: string };
        if (row.idempotencyKey) insertedKeys.push(row.idempotencyKey);
      }
    };

    await plan(new Date("2024-06-15T12:00:00.000Z"));
    await plan(new Date("2024-06-16T12:00:00.000Z"));

    expect(insertedKeys[0]).not.toEqual(insertedKeys[1]);
  });

  it("writes a QUEUED audit row for every persisted Notification", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        {
          table: "EventCategoryBinding",
          value: { ...baseEventCategoryBinding, defaultChannels: ["IN_APP"] },
        },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
      ],
      { ...baseQueuedNotification },
    );

    await mock.withTx((trx) => planNotificationInternal(trx, dispatchInput, ctx, makeAdapters()));

    const auditEvents = mock.inserts
      .map((q) => q.insertValues())
      .filter((row) => row.eventType !== undefined && row.notificationId !== undefined)
      .map((row) => row.eventType);
    expect(auditEvents).toContain("QUEUED");
  });

  it("the persisted Notification carries sourceType, sourceId, and original payloadVars exactly as supplied", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        {
          table: "EventCategoryBinding",
          value: { ...baseEventCategoryBinding, defaultChannels: ["IN_APP"] },
        },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
      ],
      { ...baseQueuedNotification },
    );

    await mock.withTx((trx) => planNotificationInternal(trx, dispatchInput, ctx, makeAdapters()));

    const notificationRow = notificationInserts(mock)[0];
    expect(notificationRow?.sourceType).toBe("PURCHASE_ORDER");
    expect(notificationRow?.sourceId).toBe("po-1");
    expect(notificationRow?.payloadVars).toBe(JSON.stringify({ poNumber: "PO-001" }));
  });

  it("skips a recipientUserId the user-management lookup cannot resolve per-pair without aborting the rest", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        {
          table: "EventCategoryBinding",
          value: { ...baseEventCategoryBinding, defaultChannels: ["IN_APP"] },
        },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const adapters = makeAdapters({
      resolveRecipient: vi.fn((_db: unknown, userId: string) => {
        if (userId === "unresolvable-user") return Promise.resolve(null);
        return Promise.resolve({
          userId,
          locale: "en-US",
          addresses: { IN_APP: userId, EMAIL: `${userId}@example.com` },
        });
      }),
    });

    const result = await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        {
          ...dispatchInput,
          recipients: [
            { userId: "unresolvable-user", reason: "SUBSCRIBED" },
            { userId: "user-1", reason: "SUBSCRIBED" },
          ],
        },
        ctx,
        adapters,
      ),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.notifications).toHaveLength(1);
      expect(result.value.notifications[0]?.recipientUserId).toBe("user-1");
      expect(result.value.skipped).toEqual([
        { recipientUserId: "unresolvable-user", reason: "RECIPIENT_UNRESOLVED" },
      ]);
    }
  });
});

// --- Audience resolution (watcher union, reason precedence, actor suppression)
// and the reason-driven critical bypass ---

const txInAppBinding = { ...baseEventCategoryBinding, defaultChannels: ["IN_APP"] };

describe("planNotification audience resolution & critical bypass", () => {
  it("resolves SUBSCRIBED watchers from NotificationSubscription and plans for them even when the emitter supplied no recipients", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        { table: "EventCategoryBinding", value: txInAppBinding },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "NotificationSubscription", value: { userId: "watcher-1" } },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const result = await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        {
          eventType: "PO_ISSUED",
          sourceType: "PURCHASE_ORDER",
          sourceId: "po-1",
          payloadVars: { poNumber: "PO-001" },
        },
        ctx,
        makeAdapters(),
      ),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.notifications).toHaveLength(1);
      expect(result.value.notifications[0].recipientUserId).toBe("watcher-1");
    }
    const rows = notificationInserts(mock);
    expect(rows).toHaveLength(1);
    expect(rows[0].reason).toBe("SUBSCRIBED");
  });

  it("unions emitter recipients with watchers and dedups by userId", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        { table: "EventCategoryBinding", value: txInAppBinding },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "NotificationSubscription", value: { userId: "w1" } },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const result = await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        {
          eventType: "PO_ISSUED",
          sourceType: "PURCHASE_ORDER",
          sourceId: "po-1",
          recipients: [{ userId: "r2", reason: "ASSIGNED" }],
          payloadVars: { poNumber: "PO-001" },
        },
        ctx,
        makeAdapters(),
      ),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      const ids = new Set(result.value.notifications.map((n) => n.recipientUserId));
      expect(ids).toEqual(new Set(["r2", "w1"]));
    }
  });

  it("keeps the higher-precedence reason (ASSIGNED) when a user is both a watcher and an emitter-supplied ASSIGNED recipient", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        { table: "EventCategoryBinding", value: txInAppBinding },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "NotificationSubscription", value: { userId: "u1" } },
        { table: "Notification", value: undefined },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
      ],
      { ...baseQueuedNotification },
    );

    await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        {
          eventType: "PO_ISSUED",
          sourceType: "PURCHASE_ORDER",
          sourceId: "po-1",
          recipients: [{ userId: "u1", reason: "ASSIGNED" }],
          payloadVars: { poNumber: "PO-001" },
        },
        ctx,
        makeAdapters(),
      ),
    );

    const rows = notificationInserts(mock);
    expect(rows).toHaveLength(1);
    expect(rows[0].reason).toBe("ASSIGNED");
  });

  it("suppresses the actor by default and includes it only when notifySelf is set", async () => {
    const baseStubs = [
      { table: "EventCategoryBinding", value: txInAppBinding },
      { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
      { table: "Notification", value: undefined },
      { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
    ];

    const m1 = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(m1, baseStubs, { ...baseQueuedNotification });
    const r1 = await m1.withTx((trx) =>
      planNotificationInternal(
        trx,
        {
          eventType: "PO_ISSUED",
          sourceType: "PURCHASE_ORDER",
          sourceId: "po-1",
          recipients: [
            { userId: "u1", reason: "ASSIGNED" },
            { userId: "u2", reason: "AUTHOR" },
          ],
          actorUserId: "u1",
          payloadVars: { poNumber: "PO-001" },
        },
        ctx,
        makeAdapters(),
      ),
    );
    expect(r1.ok).toBe(true);
    if (r1.ok) expect(r1.value.notifications.map((n) => n.recipientUserId)).toEqual(["u2"]);

    const m2 = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(m2, baseStubs, { ...baseQueuedNotification });
    const r2 = await m2.withTx((trx) =>
      planNotificationInternal(
        trx,
        {
          eventType: "PO_ISSUED",
          sourceType: "PURCHASE_ORDER",
          sourceId: "po-1",
          recipients: [
            { userId: "u1", reason: "ASSIGNED" },
            { userId: "u2", reason: "AUTHOR" },
          ],
          actorUserId: "u1",
          notifySelf: true,
          payloadVars: { poNumber: "PO-001" },
        },
        ctx,
        makeAdapters(),
      ),
    );
    expect(r2.ok).toBe(true);
    if (r2.ok) {
      expect(new Set(r2.value.notifications.map((n) => n.recipientUserId))).toEqual(
        new Set(["u1", "u2"]),
      );
    }
  });

  it("plans for an ASSIGNED recipient on an optional category despite an allowed=false preference (critical bypass)", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        { table: "EventCategoryBinding", value: baseOptionalEventBinding },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "Notification", value: undefined },
        {
          table: "NotificationPreference",
          value: { ...baseOptOutPreference, channelId: "channel-1" },
        },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const result = await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        {
          eventType: "ANNOUNCEMENT_PUBLISHED",
          sourceType: "PURCHASE_ORDER",
          sourceId: "po-1",
          recipients: [{ userId: "user-1", reason: "ASSIGNED" }],
          payloadVars: { poNumber: "PO-001" },
        },
        ctx,
        makeAdapters(),
      ),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.notifications).toHaveLength(1);
      expect(result.value.notifications[0].deliveryStatus).toBe("QUEUED");
    }
  });

  it("suppresses a SUBSCRIBED recipient on an optional category when an allowed=false preference exists (no bypass)", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageQueries(
      mock,
      [
        { table: "EventCategoryBinding", value: baseOptionalEventBinding },
        { table: "NotificationChannel", match: { channelId: "IN_APP" }, value: baseInAppChannel },
        { table: "NotificationSubscription", value: { userId: "user-1" } },
        { table: "Notification", value: undefined },
        {
          table: "NotificationPreference",
          value: { ...baseOptOutPreference, channelId: "channel-1" },
        },
        { table: "NotificationTemplate", match: { channelId: "channel-1" }, value: inAppTemplate },
      ],
      { ...baseQueuedNotification },
    );

    const result = await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        {
          eventType: "ANNOUNCEMENT_PUBLISHED",
          sourceType: "PURCHASE_ORDER",
          sourceId: "po-1",
          payloadVars: { poNumber: "PO-001" },
        },
        ctx,
        makeAdapters(),
      ),
    );

    expect(result.ok).toBe(true);
    if (result.ok) expect(result.value.notifications).toHaveLength(0);
    expect(notificationInserts(mock)).toHaveLength(0);
  });
});

// --- DESTINATION topology (Stage 2): planning writes PENDING outbox logs ---

const destinationOnlyBinding = { ...baseEventCategoryBinding, defaultChannels: [] };

interface DestResolverOpts {
  bindings?: Record<string, unknown>[];
  channelsByChannelId?: Record<string, unknown>;
  templatesByChannelDbId?: Record<string, unknown>;
  /** Existing DestinationDeliveryLog rows the dedup SELECT should return. */
  existingLogs?: Record<string, unknown>[];
}

function stageDestination(mock: Mock, opts: DestResolverOpts = {}) {
  const {
    bindings = [],
    channelsByChannelId = { SLACK: baseSlackChannel },
    templatesByChannelDbId = { "channel-4": baseSlackTemplate },
    existingLogs = [],
  } = opts;

  mock.setQueryResolver((query) => {
    const sql = query.sql;
    if (sql.startsWith("insert")) return [];
    if (!sql.startsWith("select")) return [];

    const table = /from "([^"]+)"/.exec(sql)?.[1] ?? null;
    const cols = Array.from(sql.matchAll(/"([^"]+)" = \$\d+/g)).map((m) => m[1]);
    const where: Record<string, unknown> = {};
    cols.forEach((col, i) => {
      where[col] = query.parameters[i];
    });

    if (table === "EventCategoryBinding") return [destinationOnlyBinding];
    if (table === "ChannelRoutingBinding") {
      return bindings.filter((b) => Object.entries(where).every(([k, v]) => b[k] === v));
    }
    if (table === "NotificationChannel") {
      const ch = channelsByChannelId[where.channelId as string];
      return ch ? [ch as Record<string, unknown>] : [];
    }
    if (table === "NotificationTemplate") {
      const t = templatesByChannelDbId[where.channelId as string];
      return t ? [t as Record<string, unknown>] : [];
    }
    if (table === "DestinationDeliveryLog") {
      return existingLogs.filter((l) => Object.entries(where).every(([k, v]) => l[k] === v));
    }
    return [];
  });
}

function makeDestAdapters(personalOverrides: Partial<DispatchAdapters> = {}): DispatchAdapters {
  const destination: DestinationAdapter = {
    send: vi.fn(() => Promise.resolve({ ok: true as const, externalMessageId: "slack-ts-1" })),
  };
  return {
    inApp: { send: vi.fn(() => Promise.resolve({ ok: true as const })) },
    email: { send: vi.fn(() => Promise.resolve({ ok: true as const, messageId: "sg-1" })) },
    resolveRecipient: vi.fn((_db: unknown, userId: string) =>
      Promise.resolve({
        userId,
        locale: "en-US",
        addresses: { IN_APP: userId, EMAIL: `${userId}@example.com` },
      }),
    ),
    destination,
    now: () => new Date("2024-06-15T12:00:00.000Z"),
    ...personalOverrides,
  };
}

const destInput: PlanNotificationInput = {
  eventType: "PO_ISSUED",
  sourceType: "PurchaseOrder",
  sourceId: "po-1",
  recipients: [],
  payloadVars: { poNumber: "PO-001" },
};

function destinationLogInserts(mock: Mock): Record<string, unknown>[] {
  return mock.inserts
    .map((q) => q.insertValues())
    .filter((row) => "externalChannelRef" in row && "targetType" in row);
}

describe("planNotification — DESTINATION topology (plan)", () => {
  it("plans one PENDING DestinationDeliveryLog per active binding, capturing rendered subject/body, without calling the adapter", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageDestination(mock, { bindings: [{ ...baseSlackBinding }] });
    const adapters = makeDestAdapters();

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, destInput, ctx, adapters),
    );

    expect(adapters.destination?.send).not.toHaveBeenCalled();
    if (result.ok) {
      expect(result.value.destinations).toHaveLength(1);
      expect(result.value.destinations[0]).toMatchObject({
        status: "PENDING",
        externalChannelRef: "C0123456789",
      });
    }
    const logs = destinationLogInserts(mock);
    expect(logs).toHaveLength(1);
    expect(logs[0]).toMatchObject({
      status: "PENDING",
      eventType: "PO_ISSUED",
      channelId: "SLACK",
      externalChannelRef: "C0123456789",
      subject: "PO PO-001 issued",
      failureReason: null,
    });
  });

  it("reuses an existing log instead of inserting a duplicate when the same logical event is re-planned", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    // destInput carries no idempotencyKey, so planNotification derives the
    // fallback `${eventType}:${sourceType}:${sourceId}`.
    const existingLog = {
      id: "ddl-existing",
      status: "SENT",
      idempotencyKey: "PO_ISSUED:PurchaseOrder:po-1",
      channelId: "SLACK",
      externalChannelRef: "C0123456789",
    };
    stageDestination(mock, { bindings: [{ ...baseSlackBinding }], existingLogs: [existingLog] });
    const adapters = makeDestAdapters();

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, destInput, ctx, adapters),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.destinations).toHaveLength(1);
      expect(result.value.destinations[0]).toMatchObject({
        logId: "ddl-existing",
        status: "SENT",
        reason: "DEDUPED",
      });
    }
    // No second post to the shared channel.
    expect(destinationLogInserts(mock)).toHaveLength(0);
  });

  it("inserts a separate log for a distinct event (different payloadHash) on the same binding", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    // An existing log for a different payloadHash must NOT dedup this event.
    const otherPayloadLog = {
      id: "ddl-other",
      status: "SENT",
      idempotencyKey: "PO_ISSUED:PurchaseOrder:po-1:hashA",
      channelId: "SLACK",
      externalChannelRef: "C0123456789",
    };
    stageDestination(mock, {
      bindings: [{ ...baseSlackBinding }],
      existingLogs: [otherPayloadLog],
    });
    const adapters = makeDestAdapters();

    const result = await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        { ...destInput, idempotencyKey: "PO_ISSUED:PurchaseOrder:po-1:hashB" },
        ctx,
        adapters,
      ),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.destinations).toHaveLength(1);
      expect(result.value.destinations[0]?.status).toBe("PENDING");
    }
    const logs = destinationLogInserts(mock);
    expect(logs).toHaveLength(1);
    expect(logs[0]?.idempotencyKey).toBe("PO_ISSUED:PurchaseOrder:po-1:hashB");
  });

  it("plans no log for an event whose (sourceType, sourceId) has no matching binding", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageDestination(mock, { bindings: [] });
    const adapters = makeDestAdapters();

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, destInput, ctx, adapters),
    );

    expect(result.ok).toBe(true);
    if (result.ok) expect(result.value.destinations).toHaveLength(0);
    expect(destinationLogInserts(mock)).toHaveLength(0);
  });

  it("plans one PENDING log per binding for an event with two active bindings on the same target", async () => {
    const second = {
      ...baseSlackBinding,
      id: "binding-slack-3",
      externalChannelRef: "C9999999999",
    };
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageDestination(mock, { bindings: [{ ...baseSlackBinding }, second] });
    const adapters = makeDestAdapters();

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, destInput, ctx, adapters),
    );

    if (result.ok) expect(result.value.destinations).toHaveLength(2);
    expect(destinationLogInserts(mock)).toHaveLength(2);
  });

  it("skips a binding that is inactive, whose channel is disabled, or whose channel is PERSONAL-kind", async () => {
    const m1 = createKyselyMock<Namespace[keyof Namespace]>();
    stageDestination(m1, { bindings: [{ ...baseSlackBinding, isActive: false }] });
    const r1 = await m1.withTx((trx) =>
      planNotificationInternal(trx, destInput, ctx, makeDestAdapters()),
    );
    if (r1.ok) expect(r1.value.destinations).toHaveLength(0);

    const m2 = createKyselyMock<Namespace[keyof Namespace]>();
    stageDestination(m2, {
      bindings: [{ ...baseSlackBinding }],
      channelsByChannelId: { SLACK: baseDisabledSlackChannel },
    });
    const r2 = await m2.withTx((trx) =>
      planNotificationInternal(trx, destInput, ctx, makeDestAdapters()),
    );
    if (r2.ok) expect(r2.value.destinations).toHaveLength(0);

    const m3 = createKyselyMock<Namespace[keyof Namespace]>();
    stageDestination(m3, {
      bindings: [{ ...baseSlackBinding, channelId: "IN_APP" }],
      channelsByChannelId: { IN_APP: baseInAppChannel },
    });
    const r3 = await m3.withTx((trx) =>
      planNotificationInternal(trx, destInput, ctx, makeDestAdapters()),
    );
    if (r3.ok) expect(r3.value.destinations).toHaveLength(0);
  });

  it("writes a FAILED log (template_not_found) when no template resolves for the destination channel", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageDestination(mock, {
      bindings: [{ ...baseSlackBinding }],
      templatesByChannelDbId: {},
    });
    const adapters = makeDestAdapters();

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, destInput, ctx, adapters),
    );

    if (result.ok) {
      expect(result.value.destinations).toHaveLength(1);
      expect(result.value.destinations[0]).toMatchObject({
        status: "FAILED",
        reason: "TEMPLATE_NOT_FOUND",
      });
    }
    const logs = destinationLogInserts(mock);
    expect(logs).toHaveLength(1);
    expect(logs[0]).toMatchObject({ status: "FAILED", failureReason: "template_not_found" });
  });

  it("plans no destination log when no destination adapter is injected", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    stageDestination(mock, { bindings: [{ ...baseSlackBinding }] });
    const { destination: _omit, ...personalOnly } = makeDestAdapters();
    void _omit;

    const result = await mock.withTx((trx) =>
      planNotificationInternal(trx, destInput, ctx, personalOnly),
    );

    expect(result.ok).toBe(true);
    expect(destinationLogInserts(mock)).toHaveLength(0);
  });

  it("runs Stage 1 (PERSONAL) and Stage 2 (DESTINATION) independently in the same plan", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    mock.setQueryResolver((query) => {
      const sql = query.sql;
      if (sql.startsWith("insert")) {
        const into = /into "([^"]+)"/.exec(sql)?.[1] ?? null;
        return into === "Notification" ? [{ ...baseQueuedNotification }] : [];
      }
      if (!sql.startsWith("select")) return [];
      const table = /from "([^"]+)"/.exec(sql)?.[1] ?? null;
      const cols = Array.from(sql.matchAll(/"([^"]+)" = \$\d+/g)).map((m) => m[1]);
      const where: Record<string, unknown> = {};
      cols.forEach((col, i) => {
        where[col] = query.parameters[i];
      });
      if (table === "EventCategoryBinding") {
        return [{ ...baseEventCategoryBinding, defaultChannels: ["IN_APP"] }];
      }
      if (table === "NotificationChannel") {
        if (where.channelId === "IN_APP") return [baseInAppChannel];
        if (where.channelId === "SLACK") return [baseSlackChannel];
        return [];
      }
      if (table === "NotificationTemplate") {
        if (where.channelId === "channel-1") return [baseTemplate];
        if (where.channelId === "channel-4") return [baseSlackTemplate];
        return [];
      }
      if (table === "ChannelRoutingBinding") {
        return [baseSlackBinding].filter((b) =>
          Object.entries(where).every(([k, v]) => (b as Record<string, unknown>)[k] === v),
        );
      }
      return [];
    });

    const result = await mock.withTx((trx) =>
      planNotificationInternal(
        trx,
        { ...destInput, recipients: [{ userId: "user-1", reason: "SUBSCRIBED" }] },
        ctx,
        makeDestAdapters(),
      ),
    );

    if (result.ok) {
      const inApp = result.value.notifications.find((n) => n.channelId === "channel-1");
      expect(inApp?.deliveryStatus).toBe("QUEUED");
      expect(result.value.destinations).toHaveLength(1);
      expect(result.value.destinations[0]?.status).toBe("PENDING");
    }
  });
});
