/**
 * community blueprint · wiring seam for `@platform-modules/notifications`.
 *
 * Adapter-minimalism (CLAUDE.md §4): notifications is a channel-AGNOSTIC fan-out seam — it owns
 * preference resolution + per-channel dedup, never channel code. This wiring injects TWO channels: an
 * 'inapp' capture channel and an 'email' channel that DELEGATES to `@platform-modules/mail`. That
 * delegation is the point: notifications sits ABOVE mail (mail is one delivery channel, not a peer of
 * the fan-out). A real host adds push/sms the same way; preferences + dedup come from host stores.
 *
 * notify() is PER-RECIPIENT (it resolves one user's enabled channels), so fan-out-to-many followers
 * is host orchestration — the composition test loops followers and calls fanOut once per recipient.
 */
import {
  notify,
  type ChannelAdapter,
  type ChannelResult,
  type DedupStore,
  type DeliveryResult,
  type NotifyEvent,
  type PreferenceStore,
} from '@platform-modules/notifications'
import type { createMail } from '@platform-modules/mail'

export type InAppCapture = { recipient: unknown; subject?: string; body: string }[]

export type FollowerNotifications = {
  /** Deliver one event to one recipient across their enabled channels. */
  fanOut(event: NotifyEvent): Promise<DeliveryResult[]>
  /** What the 'inapp' channel captured — the test asserts against this. */
  inApp: InAppCapture
}

/**
 * Channels a recipient opts into, keyed by event type. Defaulted to community's 'post.published' so
 * the community blueprint calls this with one arg, unchanged. A SECOND consumer (the marketplace
 * compose-blueprint, delivery-stack §4.1.1) reuses this exact seam for its 'order.sold' event by
 * passing its own map — the parameterization a real PreferenceStore resolves per-user. That a second
 * blueprint needed only this one defaulted param (no other change) is the §7 convergence signal.
 */
const DEFAULT_ENABLED: Record<string, string[]> = { 'post.published': ['inapp', 'email'] }

export function createFollowerNotifications(
  mail: ReturnType<typeof createMail>,
  enabled: Record<string, string[]> = DEFAULT_ENABLED,
): FollowerNotifications {
  const inApp: InAppCapture = []
  const marks = new Set<string>()

  const inAppChannel: ChannelAdapter = {
    channel: 'inapp',
    async send(rendered, recipient): Promise<ChannelResult> {
      inApp.push({ recipient, subject: rendered.subject, body: rendered.body })
      return { ok: true, id: `inapp-${inApp.length}` }
    },
  }

  // The 'email' channel composes @platform-modules/mail — notifications ABOVE mail.
  const emailChannel: ChannelAdapter = {
    channel: 'email',
    async send(rendered, recipient): Promise<ChannelResult> {
      const to = typeof recipient === 'string' ? recipient : String(recipient)
      const res = await mail.send({
        from: 'community@blueprint.test',
        to,
        subject: rendered.subject ?? 'New post',
        text: rendered.body,
      })
      return { ok: true, id: res.id }
    },
  }

  const preferences: PreferenceStore = {
    async getEnabledChannels(_userId, eventType) {
      return enabled[eventType] ?? []
    },
  }

  const dedup: DedupStore = {
    async seen(key) {
      return marks.has(key)
    },
    async mark(key) {
      marks.add(key)
    },
  }

  return {
    fanOut(event) {
      return notify(event, { adapters: [inAppChannel, emailChannel], preferences, dedup })
    },
    inApp,
  }
}
