import type { Contact, Conversation } from '../../types';

export interface RankedConversation {
  readonly conv: Conversation;
  readonly label: string;
  readonly rank: number;
}

const MAX_DEFAULT = 5;

function nameRank(name: string, q: string): number | null {
  const n = name.toLowerCase();
  if (!n) return null;
  if (n === q) return 0;
  if (n.startsWith(q)) return 1;
  if (n.split(/\s+/).some((part) => part.startsWith(q))) return 2;
  if (n.includes(q)) return 3;
  return null;
}

export function normalizeHandle(raw: string): string {
  const trimmed = raw.trim().toLowerCase();
  if (trimmed.includes('@')) return trimmed;
  const digits = trimmed.replace(/\D+/g, '');
  return digits || trimmed;
}

export function rankConversationsByQuery(
  conversations: readonly Conversation[],
  contacts: readonly Contact[],
  query: string,
  max: number = MAX_DEFAULT,
): readonly RankedConversation[] {
  const q = query.trim().toLowerCase();
  if (q.length < 2) return [];

  const handleRank = new Map<string, { rank: number; label: string }>();
  for (const contact of contacts) {
    const rank = nameRank(contact.displayName, q);
    if (rank === null) continue;
    const handles = [contact.handle, ...contact.phoneNumbers, ...contact.emails]
      .filter(Boolean)
      .map(normalizeHandle);
    for (const h of handles) {
      const existing = handleRank.get(h);
      if (!existing || rank < existing.rank) {
        handleRank.set(h, { rank, label: contact.displayName });
      }
    }
  }

  const ranked: Array<RankedConversation & { index: number }> = [];
  conversations.forEach((conv, index) => {
    let bestRank: number | null = null;
    let bestLabel = conv.displayName || conv.participants[0] || 'Unknown';

    for (const p of conv.participants) {
      const hit = handleRank.get(normalizeHandle(p));
      if (hit && (bestRank === null || hit.rank < bestRank)) {
        bestRank = hit.rank;
        bestLabel = hit.label;
      }
    }

    const dnRank = nameRank(conv.displayName, q);
    if (dnRank !== null && (bestRank === null || dnRank < bestRank)) {
      bestRank = dnRank;
      bestLabel = conv.displayName;
    }

    if (bestRank === null && conv.participants.some((p) => p.toLowerCase().includes(q))) {
      bestRank = 4;
    }

    if (bestRank === null) return;
    ranked.push({ conv, label: bestLabel, rank: bestRank, index });
  });

  ranked.sort((a, b) => a.rank - b.rank || a.index - b.index);
  return ranked.slice(0, max).map(({ conv, label, rank }) => ({ conv, label, rank }));
}
