/**
 * Abstract base class for platform adapters (Layer 1).
 *
 * Adapters handle platform-specific I/O only:
 *   - Connection management (start/stop)
 *   - Message consumption (pull model via consumeOne)
 *   - Message sending
 *   - Permission UI rendering
 *
 * All business logic (policy, routing, agent invocation, delivery)
 * lives in higher layers.
 */

import type { ChannelType, IncomingMessage, OutboundMessage, ImPermissionRequest } from '../types'
import type { PlatformContext } from '@/lib/types'
import type { DeliveryLayer } from '../core/delivery'
import { EditDraftRenderer } from './edit-draft-renderer'
import type { StreamRenderer, StreamRenderContext } from './stream-renderer'

export abstract class ChannelAdapter {
  /** Platform identifier */
  abstract readonly channelType: ChannelType

  // ---------------------------------------------------------------------------
  // Lifecycle
  // ---------------------------------------------------------------------------

  /**
   * Start the adapter with platform credentials.
   * Must be idempotent — calling start on a running adapter should reconnect.
   */
  abstract start(config: Record<string, string>): Promise<void>

  /** Gracefully stop the adapter and release all resources. */
  abstract stop(): Promise<void>

  /** Whether the adapter is currently running and can consume/send messages. */
  abstract isRunning(): boolean

  // ---------------------------------------------------------------------------
  // Message consumption (pull model)
  // ---------------------------------------------------------------------------

  /**
   * Wait for and return the next inbound message.
   *
   * The Bridge Manager calls this in a tight loop. Implementations should
   * block (with timeout) until a message is available or the signal is aborted.
   *
   * @returns The next message, or null if the wait timed out with no message.
   * @throws If the underlying connection is lost (triggers reconnect in L2).
   */
  abstract consumeOne(signal: AbortSignal): Promise<IncomingMessage | null>

  // ---------------------------------------------------------------------------
  // Outbound messaging
  // ---------------------------------------------------------------------------

  /**
   * Send a message to the platform.
   * @returns The platform message ID (for edit/delete), or undefined.
   */
  abstract send(msg: OutboundMessage): Promise<string | undefined>

  /**
   * Send an image to the platform.
   */
  abstract sendImage(chatId: string, imageBuffer: Buffer, caption?: string): Promise<void>

  /**
   * Send a file (non-image) to the platform.
   */
  abstract sendFile(chatId: string, fileBuffer: Buffer, filename: string, caption?: string): Promise<void>

  /**
   * Send a typing indicator to the platform.
   */
  abstract sendTypingIndicator(chatId: string): Promise<void>

  // ---------------------------------------------------------------------------
  // Permission UI
  // ---------------------------------------------------------------------------

  /**
   * Render a permission prompt on the platform (buttons, text command, etc.).
   */
  abstract sendPermissionPrompt(req: ImPermissionRequest): Promise<void>

  /**
   * Register a callback for when the user responds to a permission prompt.
   * The adapter must call this callback with (requestId, 'allow'|'deny').
   */
  abstract onPermissionResponse(
      callback: (requestId: string, decision: 'allow' | 'deny') => void,
  ): void

  // ---------------------------------------------------------------------------
  // Config validation
  // ---------------------------------------------------------------------------

  /**
   * Whether the platform supports editing/deleting sent messages.
   * Used by Bridge Manager to decide whether to send streaming draft previews.
   */
  readonly supportsMessageEditing: boolean = true

  // ---------------------------------------------------------------------------
  // Reaction support (optional)
  // ---------------------------------------------------------------------------

  /**
   * Add a reaction (emoji) to a message.
   * Default: no-op, platforms that don't support reactions can ignore this.
   */
  async addReaction(_messageId: string, _emojiType: string): Promise<void> {
    // Default: no-op
  }

  /**
   * Remove a reaction (emoji) from a message.
   * Default: no-op, platforms that don't support reactions can ignore this.
   */
  async removeReaction(_messageId: string, _emojiType: string): Promise<void> {
    // Default: no-op
  }

  // ---------------------------------------------------------------------------
  // Channel authentication (optional)
  // ---------------------------------------------------------------------------

  /**
   * Ensure per-user/per-channel auth is ready before processing a message.
   * Returns the channel's credentials to populate platformContext, or undefined
   * if this channel needs no auth (default) or auth could not be established.
   *
   * Bridge Manager calls this uniformly instead of knowing any platform's auth
   * internals (Feishu OAuth device flow + token refresh, WeCom CLI dir, DingTalk
   * app credentials, etc.). Each adapter owns its auth lifecycle.
   */
  async ensureChannelAuth(_msg: IncomingMessage): Promise<Partial<PlatformContext> | undefined> {
    return undefined
  }

  // ---------------------------------------------------------------------------
  // Stream rendering (optional)
  // ---------------------------------------------------------------------------

  /**
   * Create a stream renderer for one conversation turn. Default: EditDraftRenderer
   * (edit-message draft previews). Platforms with native streaming/cards override.
   * Bridge Manager calls this instead of holding platform if-else (C-2).
   */
  createStreamRenderer(ctx: StreamRenderContext): StreamRenderer {
    return new EditDraftRenderer(ctx)
  }

  /**
   * Validate platform credentials before attempting to connect.
   */
  abstract validateConfig(config: Record<string, string>): { valid: boolean; error?: string }

  // ---------------------------------------------------------------------------
  // Platform capability hooks (optional — bridge calls these instead of
  // instanceof checks, so new platforms add behavior via overrides, not edits
  // to bridge-manager).
  // ---------------------------------------------------------------------------

  /** Whether this platform uses a reaction as typing indicator (Feishu 👍). */
  supportsReactionIndicator?(): boolean

  /** Queue-busy 预建平台专属载体（DingTalk AI Card）。返回透传给 renderer 的上下文。 */
  async preCreateBusyCard?(_chatId: string, _ackText: string): Promise<unknown | undefined>

  /** renderer.abort 是否已自处理错误投递（WeCom replyStream 自带错误文本）。 */
  handlesErrorDeliveryInRenderer?(): boolean

  /**
   * 平台特化的最终投递。返回 true 表示已处理（bridge 不再走默认文本投递）。
   * 默认不实现 → bridge 走 delivery.deliver() 纯文本 + markdown 渲染。
   */
  async deliverPlatformFinal?(_chatId: string, _text: string, _delivery: DeliveryLayer): Promise<boolean>
}
