import { createHash } from 'crypto';

/** Inbound OpenWA `message.received` / `message.sent` data (subset used by the mapper). */
export interface OpenWaMessageData {
  id?: string;
  from?: string;
  to?: string;
  chatId?: string;
  body?: string;
  type?: string;
  timestamp?: number;
  fromMe?: boolean;
  isGroup?: boolean;
  author?: string;
  mentionedIds?: string[];
  senderPhone?: string | null;
  contact?: { pushName?: string; name?: string; number?: string };
  media?: { mimetype?: string; filename?: string; data?: string; omitted?: boolean };
  quotedMessage?: { id?: string; body?: string };
}

export interface UltramsgMessageData {
  id: string;
  sid: string;
  from: string;
  to: string;
  author: string;
  pushname: string;
  ack: string;
  type: string;
  body: string;
  media: string;
  fromMe: boolean;
  self: boolean;
  isForwarded: boolean;
  isMentioned: boolean;
  quotedMsg: UltramsgQuotedMsg | [];
  mentionedIds: string[];
  time: number;
}

export interface UltramsgQuotedMsg {
  id: string;
  sid: string;
  body: string;
  fromMe: boolean;
}

export interface UltramsgCompatEnvelope {
  event_type: 'message_received' | 'message_create';
  instanceId: string;
  id: string;
  referenceId: string;
  hash: string;
  data: UltramsgMessageData;
}

const TYPE_MAP: Record<string, string> = {
  text: 'chat',
  chat: 'chat',
  image: 'image',
  video: 'video',
  audio: 'audio',
  voice: 'ptt',
  ptt: 'ptt',
  document: 'document',
};

/** UltraMsg `data.type` values that list/webhook_instant.php understands. */
export function mapUltramsgType(openWaType: string | undefined): string {
  if (!openWaType) return 'chat';
  return TYPE_MAP[openWaType] ?? 'chat';
}

/** Prefer a resolved phone JID; fold `@s.whatsapp.net` into `@c.us`. */
export function toPhoneJid(jid: string | undefined, senderPhone?: string | null): string {
  if (senderPhone) {
    const digits = String(senderPhone).replace(/\D/g, '');
    if (digits) return `${digits}@c.us`;
  }
  if (!jid) return '';
  if (jid.endsWith('@s.whatsapp.net')) {
    return jid.replace(/@s\.whatsapp\.net$/, '@c.us');
  }
  return jid;
}

/** Extract the WhatsApp message hash UltraMsg stores as `sid`. */
export function extractSid(id: string | undefined): string {
  if (!id) return '';
  const hashed = id.match(/@(?:g\.us|c\.us|lid|s\.whatsapp\.net)_([A-Za-z0-9]{12,64})/);
  if (hashed) return hashed[1];
  const parts = id.split('_').filter(Boolean);
  return parts[parts.length - 1] || id;
}

export function buildUltramsgId(opts: {
  fromMe: boolean;
  remoteJid: string;
  sid: string;
  authorJid?: string;
  isGroup: boolean;
}): string {
  const flag = opts.fromMe ? 'true' : 'false';
  const sid = opts.sid || 'unknown';
  const remote = opts.remoteJid || 'unknown';
  if (opts.isGroup && opts.authorJid) {
    return `${flag}_${remote}_${sid}_${opts.authorJid}`;
  }
  return `${flag}_${remote}_${sid}`;
}

/**
 * Reconstruct a wwebjs-style serialized id from an UltraMsg mid so quoted sends work.
 * `false_{jid}_{sid}_{authorLid}` → `false_{jid}_{sid}`
 */
export function engineQuotedId(msgId: string | undefined): string | undefined {
  if (!msgId) return undefined;
  const parsed = msgId.match(
    /^(true|false)_([^_]+@(?:g\.us|c\.us|lid|s\.whatsapp\.net))_([A-Za-z0-9]{12,64})(?:_.*)?$/,
  );
  if (parsed) {
    return `${parsed[1]}_${parsed[2]}_${parsed[3]}`;
  }
  return msgId;
}

/** UltraMsg `to` may be digits only; OpenWA send-text wants a JID. */
export function toChatId(to: string): string {
  const trimmed = to.trim();
  if (!trimmed) return trimmed;
  if (trimmed.includes('@')) return trimmed.replace(/@s\.whatsapp\.net$/, '@c.us');
  const digits = trimmed.replace(/\D/g, '');
  return digits ? `${digits}@c.us` : trimmed;
}

export function mapMediaUrl(media: OpenWaMessageData['media'], savedUrl: string): string {
  if (savedUrl) return savedUrl;
  if (!media) return '';
  if (typeof media.data === 'string' && /^https?:\/\//i.test(media.data)) {
    return media.data;
  }
  return '';
}

const ULTRAMSG_ID_RE = /^(true|false)_.+@(?:g\.us|c\.us|lid|s\.whatsapp\.net)_/;

function looksLikeUltramsgId(id: string): boolean {
  return ULTRAMSG_ID_RE.test(id);
}

function mapQuotedMsg(
  quoted: NonNullable<OpenWaMessageData['quotedMessage']>,
  isGroup: boolean,
  remoteForId: string,
  authorForId?: string,
): UltramsgQuotedMsg {
  const qSid = extractSid(quoted.id);
  const id =
    quoted.id && looksLikeUltramsgId(quoted.id)
      ? quoted.id
      : buildUltramsgId({
          fromMe: false,
          remoteJid: remoteForId,
          sid: qSid,
          authorJid: isGroup ? authorForId : undefined,
          isGroup,
        });
  return {
    id,
    sid: qSid,
    body: quoted.body ?? '',
    fromMe: false,
  };
}

export function mapToUltramsgData(src: OpenWaMessageData, mediaUrl = ''): UltramsgMessageData {
  const fromMe = Boolean(src.fromMe);
  const isGroup =
    Boolean(src.isGroup) || Boolean(src.chatId?.endsWith('@g.us')) || Boolean(src.from?.endsWith('@g.us'));
  const sid = extractSid(src.id);
  // UltraMsg keeps the conversation JID (often `@lid`) inside `data.id`, while `from`/`author`
  // prefer a resolved phone `@c.us` when one is available.
  const remoteForId = src.chatId || (fromMe ? src.to : src.from) || '';
  const authorForId = isGroup ? src.author : undefined;
  const id = buildUltramsgId({ fromMe, remoteJid: remoteForId, sid, authorJid: authorForId, isGroup });

  const quotedMsg: UltramsgQuotedMsg | [] = src.quotedMessage?.id
    ? mapQuotedMsg(src.quotedMessage, isGroup, remoteForId, authorForId)
    : [];

  const ultramsgType = mapUltramsgType(src.type);
  const media = mapMediaUrl(src.media, mediaUrl);
  const body = ultramsgType === 'chat' ? (src.body ?? '') : src.body || media;
  const authorPhone = isGroup ? toPhoneJid(src.author, src.senderPhone) : '';

  return {
    id,
    sid,
    from: isGroup ? remoteForId : toPhoneJid(src.from || src.chatId, src.senderPhone),
    to: toPhoneJid(src.to, null),
    author: authorPhone,
    pushname: src.contact?.pushName || src.contact?.name || '',
    ack: '',
    type: ultramsgType,
    body,
    media,
    fromMe,
    self: fromMe,
    isForwarded: false,
    isMentioned: (src.mentionedIds?.length ?? 0) > 0,
    quotedMsg,
    mentionedIds: src.mentionedIds ?? [],
    time: typeof src.timestamp === 'number' ? src.timestamp : Math.floor(Date.now() / 1000),
  };
}

export function wrapUltramsgPayload(
  event: string,
  instanceId: string,
  data: UltramsgMessageData,
): UltramsgCompatEnvelope {
  const event_type: UltramsgCompatEnvelope['event_type'] =
    event === 'message.sent' ? 'message_create' : 'message_received';
  return {
    event_type,
    instanceId,
    id: event_type === 'message_create' ? data.sid : '',
    referenceId: '',
    hash: createHash('md5')
      .update(data.sid || data.id)
      .digest('hex'),
    data,
  };
}
