import { sql } from '@vercel/postgres';

const MATCH_THRESHOLD = 0.85;
const DISAMBIGUATION_THRESHOLD = 0.6;
const MIN_QUERY_LENGTH = 3;

function mapCardRow(card) {
  return {
    id: card.id,
    name: card.name,
    set_name: card.set_name,
    set_code: card.set_code,
    card_number: card.card_number,
    game: card.game,
    rarity: card.rarity,
    image_url: card.image_url,
    card_type: card.card_type,
    mana_cost: card.mana_cost,
    hp: card.power,
    similarity: card.sim,
  };
}

/**
 * Pick the most likely card name line from raw OCR output (name strip is top of card).
 */
export function extractNameCandidate(ocrText) {
  if (!ocrText || typeof ocrText !== 'string') return '';

  const lines = ocrText
    .split(/\r?\n/)
    .map((line) => line.replace(/\s+/g, ' ').trim())
    .filter((line) => line.length >= MIN_QUERY_LENGTH);

  if (lines.length === 0) {
    return ocrText.replace(/\s+/g, ' ').trim();
  }

  const scored = lines.slice(0, 5).map((line, index) => ({
    line,
    score: line.length - index * 2,
  }));
  scored.sort((a, b) => b.score - a.score);
  return scored[0].line;
}

async function querySimilarCards(query, game) {
  if (game) {
    return sql`
      SELECT *, similarity(name, ${query}) AS sim
      FROM cards
      WHERE similarity(name, ${query}) > ${DISAMBIGUATION_THRESHOLD - 0.05}
      ORDER BY sim DESC, CASE WHEN game = ${game} THEN 0 ELSE 1 END, LENGTH(name)
      LIMIT 8
    `;
  }

  return sql`
    SELECT *, similarity(name, ${query}) AS sim
    FROM cards
    WHERE similarity(name, ${query}) > ${DISAMBIGUATION_THRESHOLD - 0.05}
    ORDER BY sim DESC, LENGTH(name)
    LIMIT 8
  `;
}

/**
 * Fuzzy match OCR text against cards.name using pg_trgm similarity.
 */
export async function matchTextInCatalog({ ocrText, game = null, ocrConfidence = null }) {
  const query = extractNameCandidate(ocrText);

  if (!query || query.length < MIN_QUERY_LENGTH) {
    return {
      type: 'escalate',
      reason: 'OCR text too short for catalog match',
      query,
      ocrConfidence,
    };
  }

  const result = await querySimilarCards(query, game || null);
  const candidates = result.rows.filter((row) => row.sim >= DISAMBIGUATION_THRESHOLD);

  if (candidates.length === 0) {
    return {
      type: 'escalate',
      reason: `No catalog match above ${DISAMBIGUATION_THRESHOLD} similarity for "${query}"`,
      query,
      ocrConfidence,
    };
  }

  const normalizedQuery = query.toLowerCase();
  const exactNameMatches = candidates.filter(
    (row) => row.name?.toLowerCase() === normalizedQuery
  );

  // Same card name, multiple printings — always ask the user.
  if (exactNameMatches.length > 1) {
    return {
      type: 'disambiguation',
      matches: exactNameMatches.slice(0, 8).map(mapCardRow),
      query,
      message: `Found ${exactNameMatches.length} printings of "${query}". Select the correct one.`,
    };
  }

  const top = candidates[0];
  const runnerUp = candidates[1];
  const clearWinner =
    top.sim >= MATCH_THRESHOLD &&
    (!runnerUp || top.sim - runnerUp.sim >= 0.08);

  if (clearWinner) {
    return {
      type: 'matched',
      card: top,
      query,
      similarity: top.sim,
      message: `Matched "${top.name}" via text search (${Math.round(top.sim * 100)}% similar)`,
    };
  }

  return {
    type: 'disambiguation',
    matches: candidates.slice(0, 5).map(mapCardRow),
    query,
    message: `Found ${candidates.length} possible matches for "${query}". Select the correct card.`,
  };
}
