import type { Channel, WhatsAppProvider } from '@wa-ticketing/shared';
import { BaileysWhatsAppProvider } from './baileysWhatsAppProvider.js';
import { createAfricasTalkingWhatsAppProvider } from './africasTalkingWhatsAppProvider.js';

export { WhatsAppProviderError } from './africasTalkingWhatsAppProvider.js';

export type WhatsAppMediaPayload = {
  kind: 'image' | 'voice_note' | 'document';
  mediaBase64: string;
  /** Externally reachable (signed) URL; null when the caller only has the bytes. */
  mediaUrl: string | null;
  mediaMime: string;
  caption?: string;
  fileName?: string;
};

export type GroupMetadata = {
  groupJid: string;
  subject: string;
  phones: string[];
  participantCount: number;
};

export interface WhatsAppProviderAdapter {
  sendText(channel: Channel, recipient: string, text: string): Promise<string | null>;
  sendMedia(channel: Channel, recipient: string, media: WhatsAppMediaPayload): Promise<string | null>;
  startSession?(channel: Channel, clearAuth: boolean): Promise<void>;
  terminateSession?(channel: Channel): Promise<void>;
  createGroup?(channel: Channel, subject: string, phones: string[]): Promise<{ groupJid: string; subject: string }>;
  getGroupMetadata?(channel: Channel, groupJid: string): Promise<GroupMetadata>;
  updateGroupParticipants?(channel: Channel, groupJid: string, action: 'add' | 'remove', phones: string[]): Promise<void>;
}

export type OptionalProviderMethod =
  | 'startSession' | 'terminateSession' | 'createGroup' | 'getGroupMetadata' | 'updateGroupParticipants';

/** Lazily built so provider secrets are only required when a channel actually uses that provider. */
export type WhatsAppProviderRegistry = Record<WhatsAppProvider, () => WhatsAppProviderAdapter>;

const defaultRegistry: WhatsAppProviderRegistry = {
  baileys: () => new BaileysWhatsAppProvider(),
  africas_talking: () => createAfricasTalkingWhatsAppProvider(),
};

export function resolveWhatsAppProvider(
  channel: Channel,
  registry: WhatsAppProviderRegistry = defaultRegistry,
): WhatsAppProviderAdapter {
  const provider = channel.provider;
  if (channel.kind !== 'whatsapp' || !provider || !Object.hasOwn(registry, provider)) {
    throw Object.assign(new Error('Channel does not have a WhatsApp provider'), { statusCode: 400 });
  }
  return registry[provider]();
}

/** Returns the adapter method bound to the adapter, or throws a 400 capability error. */
export function requireWhatsAppProviderMethod<K extends OptionalProviderMethod>(
  channel: Channel,
  adapter: WhatsAppProviderAdapter,
  method: K,
): NonNullable<WhatsAppProviderAdapter[K]> {
  const fn = adapter[method];
  if (typeof fn !== 'function') {
    throw Object.assign(
      new Error(`WhatsApp provider ${channel.provider ?? 'unknown'} does not support ${method}`),
      { statusCode: 400, code: 'unsupported_capability' },
    );
  }
  return (fn as (...args: unknown[]) => unknown).bind(adapter) as NonNullable<WhatsAppProviderAdapter[K]>;
}
