// Copyright (c) 2026 Hellmai Ltd
// SPDX-License-Identifier: AGPL-3.0-only

/**
 * WU-2735 (INIT-060 WU-7a, ADR-013 §ChannelBridge):
 * ChannelBridge port.
 *
 * Port contract — load-bearing across adapters (ADR-013 §ChannelBridge):
 *
 * - `send` is at-least-once for `queue` envelopes, fail-silent for
 *   `ephemeral` envelopes (ADR-013 §4 backpressure split).
 * - `receive` yields envelopes in emit order on a single channel (§3). No
 *   cross-channel ordering guarantee.
 * - `register` is idempotent on `BridgeConfig` identity — same
 *   `(provider, name)` returns the same `ChannelId`.
 * - `disconnect` flushes queued envelopes before closing; in-flight ephemerals
 *   may drop.
 *
 * The port MUST NOT leak filesystem- or network-specific types. Both the
 * filesystem adapter (this WU) and the cloud adapter (WU-2737) implement this
 * interface unmodified.
 */

import type {
  BridgeConfig,
  ChannelEnvelope,
  ChannelId,
  SendResult,
} from '../domain/channel.types.js';

export interface ChannelBridge {
  /**
   * Send an envelope on a registered channel.
   *
   * @throws if `channelId` is not registered, or has been disconnected.
   */
  send(channelId: ChannelId, envelope: ChannelEnvelope): Promise<SendResult>;

  /**
   * Stream envelopes on the given channel in emit order. The iterator
   * terminates after `disconnect(channelId)`; otherwise it is long-lived.
   */
  receive(channelId: ChannelId): AsyncIterable<ChannelEnvelope>;

  /**
   * Register a channel and return its id. Idempotent on `(provider, name)`.
   */
  register(bridgeConfig: BridgeConfig): Promise<ChannelId>;

  /**
   * Close the channel. Queued envelopes flush before this resolves; ephemerals
   * in flight may be dropped.
   */
  disconnect(channelId: ChannelId): Promise<void>;
}

/**
 * Structural type guard. Useful for adapter tests and runtime payload
 * validation at the port boundary.
 */
export function isChannelEnvelope(value: unknown): value is ChannelEnvelope {
  if (!value || typeof value !== 'object') {
    return false;
  }
  const v = value as Record<string, unknown>;
  return (
    typeof v.id === 'string' &&
    (v.kind === 'ephemeral' || v.kind === 'queue') &&
    typeof v.content_type === 'string' &&
    'body' in v &&
    typeof v.emitted_at === 'string'
  );
}

export type { BridgeConfig, ChannelEnvelope, ChannelId, SendResult };
