import type { IngestInteractionRequest } from '@insignia/iios-contracts';
import type { AdapterCapability, ChannelAdapter, NormalizeContext, OutboundCommandInput, SendResult } from './types';
import { hmacVerify } from './hmac';
import { SandboxSink } from './sandbox';

/** The canonical webhook payload the reference adapter understands. */
export interface WebhookPayload {
  eventId: string;
  from: string;
  displayName?: string;
  threadRef?: string;
  subject?: string;
  text: string;
  occurredAt?: string;
}

/**
 * Reference HMAC webhook adapter — proves the whole anti-corruption pipeline.
 * New providers implement the same ChannelAdapter contract with their own
 * signature scheme + payload mapping.
 */
export class WebhookAdapter implements ChannelAdapter {
  readonly channelType: string;
  readonly capability: AdapterCapability = {
    canReceive: true,
    canSend: true,
    supportsThreads: true,
    requiresFollowupFetch: false,
  };

  constructor(channelType = 'WEBHOOK') {
    this.channelType = channelType;
  }

  verifySignature(rawBody: string, headers: Record<string, string | undefined>, secret: string): boolean {
    const sig = headers['x-iios-signature'] ?? headers['X-Iios-Signature'];
    return hmacVerify(rawBody, secret, typeof sig === 'string' ? sig : undefined);
  }

  externalEventId(payload: unknown): string | undefined {
    const id = (payload as WebhookPayload)?.eventId;
    return typeof id === 'string' ? id : undefined;
  }

  normalize(payload: unknown, ctx: NormalizeContext): IngestInteractionRequest {
    const p = payload as WebhookPayload;
    return {
      scope: ctx.scope,
      channel: { type: this.channelType, externalChannelId: this.channelType.toLowerCase() },
      source: { handleKind: 'EXTERNAL_VISITOR', externalId: p.from, displayName: p.displayName ?? p.from },
      kind: 'MESSAGE',
      thread: { externalThreadId: p.threadRef ?? `wh_${p.from}`, subject: p.subject },
      parts: [{ kind: 'TEXT', bodyText: p.text }],
      occurredAt: p.occurredAt ?? new Date().toISOString(),
      providerEventId: p.eventId,
    };
  }

  async send(cmd: OutboundCommandInput): Promise<SendResult> {
    return SandboxSink.simulate(cmd);
  }
}
