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

/** Canonical inbound-email shape. Vendor payloads (SendGrid Inbound Parse, Mailgun
 *  Routes, Postmark) map into this — provider glue stays a thin transform. */
export interface EmailInboundPayload {
  messageId: string; // RFC 5322 Message-ID → providerEventId (dedup on redelivery)
  from: string;
  fromName?: string;
  to?: string[];
  cc?: string[];
  subject?: string;
  text?: string;
  html?: string;
  inReplyTo?: string; // Message-ID this is a reply to
  references?: string[]; // full reference chain, root-first (threading)
  occurredAt?: string;
  headers?: Record<string, string>;
}

const stripReply = (s?: string): string => (s ?? '').replace(/^(re|fwd|fw)\s*:\s*/i, '').trim();
const stripHtml = (h?: string): string => (h ?? '').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();

/**
 * Email channel adapter. Turns a provider's inbound email into a normalized
 * interaction with email-native threading: the whole reply chain collapses to one
 * IIOS thread (rooted at `references[0]`/`inReplyTo`), and `messageId` dedups
 * redelivery. HMAC signature for now (per-vendor schemes are a follow-up).
 */
export class EmailAdapter implements ChannelAdapter {
  readonly channelType = 'EMAIL';
  readonly capability: AdapterCapability = {
    canReceive: true,
    canSend: true,
    supportsThreads: true,
    requiresFollowupFetch: false,
  };

  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 EmailInboundPayload)?.messageId;
    return typeof id === 'string' ? id : undefined;
  }

  normalize(payload: unknown, ctx: NormalizeContext): IngestInteractionRequest {
    const p = payload as EmailInboundPayload;
    // A reply's thread root is the first Message-ID in its chain → same IIOS thread as the original.
    const threadRoot = p.references?.[0] ?? p.inReplyTo ?? p.messageId;
    return {
      scope: ctx.scope,
      channel: { type: 'EMAIL', externalChannelId: 'email' },
      source: { handleKind: 'EXTERNAL_VISITOR', externalId: p.from, displayName: p.fromName ?? p.from },
      kind: 'MESSAGE',
      thread: { externalThreadId: `email:${threadRoot}`, subject: stripReply(p.subject) },
      parts: [{ kind: 'TEXT', bodyText: p.text ?? stripHtml(p.html), mimeType: 'text/plain' }],
      occurredAt: p.occurredAt ?? new Date().toISOString(),
      providerEventId: p.messageId,
      metadata: { to: p.to, cc: p.cc, messageId: p.messageId, inReplyTo: p.inReplyTo, references: p.references, html: p.html },
    };
  }

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