/**
 * Ports: How eval reports reach team-defined notification channels.
 *
 * Two hexagonal ports (D0002) keep report→channel matching pure and I/O-free
 * in the domain kernel while the side effects live in `packages/eval`:
 *
 * - `TeamSubscriptionSource` — read port: load the teams whose channels might
 *   subscribe to a report (a GROQ read adapter implements it).
 * - `NotificationChannelAdapter` — deliver port: post resolved content to one
 *   channel (the Slack bot-token adapter implements it).
 *
 * The matcher in `notifications/match.ts` depends on neither — it turns a
 * report plus a set of teams into `ChannelDelivery[]`. A `NotificationService`
 * (in eval) composes the source, the matcher, and the adapters.
 */
import type { DigestSummary, Report } from "../types/index.js";
import type { NotificationChannel, NotificationDelivery, Team } from "../types/team.js";
/**
 * What a delivery carries, discriminated by `kind`. `report` is the immediate
 * per-report path; `digest` is the scheduled daily/weekly roll-up, tagged with
 * the cadence that produced it.
 */
export type NotificationContent = {
    kind: "report";
    report: Report;
} | {
    kind: "digest";
    digest: DigestSummary;
    cadence: NotificationDelivery;
};
/**
 * One resolved delivery target: a channel that matched, plus the slim team
 * identity it belongs to (enough to label the delivery, not the whole team).
 */
export interface ChannelDelivery {
    team: Pick<Team, "slug" | "displayName">;
    channel: NotificationChannel;
}
/**
 * The result of attempting one delivery. `skipped` is distinct from `failed`
 * so an operational fix (e.g. `not_in_channel` → "invite the bot") reads
 * differently from a genuine error.
 */
export type DeliveryOutcome = {
    status: "delivered";
    detail?: string;
} | {
    status: "failed";
    error: string;
} | {
    status: "skipped";
    reason: string;
};
/** READ port: load active teams that have notification channels. */
export interface TeamSubscriptionSource {
    loadSubscribedTeams(): Promise<Team[]>;
}
/** DELIVER port: post content to one resolved channel of a given type. */
export interface NotificationChannelAdapter {
    /** The channel type this adapter handles (`slack`, `email`, `webhook`). */
    readonly type: NotificationChannel["type"];
    deliver(target: ChannelDelivery, content: NotificationContent): Promise<DeliveryOutcome>;
}
/**
 * The result of dispatching to one channel: the stable channel key, the channel
 * type and owning-team slug (enough to label the operational record), and the
 * delivery outcome. The service returns an array of these; the publish step
 * emits one `notificationResults` artifact per entry, mirroring `sinkResults`.
 */
export interface ChannelDispatchResult {
    /** Stable per-channel key (e.g. `slack:C0123`); see `channelKey` in `match`. */
    channelKey: string;
    /** The channel type that was dispatched to. */
    channelType: NotificationChannel["type"];
    /** Slug of the team whose channel this is. */
    team: string;
    /** Outcome of the delivery attempt (`delivered` | `skipped` | `failed`). */
    outcome: DeliveryOutcome;
}
/**
 * SERVICE port: composes the read source, the pure matcher, and the deliver
 * adapters into a fire-and-forget dispatcher. Lives behind
 * `AppContext.notifications`; the eval `NotificationService` implements it.
 *
 * Both methods resolve to a per-channel result array and never reject (P6 — a
 * notification failure must not break the pipeline's publish step, which has
 * already written the report to the system of record by the time it dispatches).
 */
export interface NotificationServicePort {
    /** Dispatch a freshly published report to its subscribed `immediate` channels. */
    dispatchReport(report: Report): Promise<ChannelDispatchResult[]>;
    /**
     * Dispatch a scheduled digest to channels subscribed at the given cadence.
     * Used by the `daily`/`weekly` sweep (M6); the caller supplies `teams`
     * (the sweep loads them once for the window).
     */
    dispatchDigest(digest: DigestSummary, cadence: NotificationDelivery, teams: Team[]): Promise<ChannelDispatchResult[]>;
}
