import { levenshtein } from './fuzzy';

const normalize = value => (value || '').toString().toLowerCase().trim();

const compact = value => value.replace(/[^a-z0-9]/g, '');

const wordTails = value => {
  const words = value.split(/[^a-z0-9]+/).filter(Boolean);
  return words.map((_, index) => words.slice(index).join(''));
};

const tokenScore = (item, token) => {
  const name = normalize(item.title || item.name);
  const category = normalize(item.categoryLabel || item.category);
  const tags = (item.tags || []).map(normalize);
  const description = normalize(item.description);
  const flatToken = compact(token);
  const tails = flatToken ? wordTails(name) : [];
  const tagTails = flatToken ? tags.map(wordTails) : [];

  if (name === token || tails[0] === flatToken) return { score: 100, type: 'name' };
  if (name.startsWith(token) || tails[0]?.startsWith(flatToken)) return { score: 90, type: 'name' };
  if (name.includes(token) || tails.some(tail => tail.startsWith(flatToken))) return { score: 80, type: 'name' };
  if (tags.some(tag => tag === token) || tagTails.some(tail => tail[0] === flatToken))
    return { score: 72, type: 'tag' };
  if (
    tags.some(tag => tag.startsWith(token) || tag.includes(token)) ||
    tagTails.some(tail => tail.some(part => part.startsWith(flatToken)))
  )
    return { score: 62, type: 'tag' };
  if (category.includes(token)) return { score: 50, type: 'category' };
  if (description.includes(token)) return { score: 38, type: 'description' };

  if (flatToken.length >= 4) {
    const maxDistance = flatToken.length <= 5 ? 1 : 2;
    const words = name.split(/\s+/);
    const similar = [...words, compact(name)].some(word => levenshtein(word, flatToken) <= maxDistance);
    if (similar) return { score: 20, type: 'similar' };
  }

  return null;
};

export const rankComponentSearch = (item, query) => {
  const normalizedQuery = normalize(query);
  if (!normalizedQuery) return { score: 1, type: 'name' };

  const tokens = normalizedQuery.split(/\s+/).filter(Boolean);
  const matches = tokens.map(token => tokenScore(item, token));
  if (matches.some(match => !match)) return null;

  const score = matches.reduce((total, match) => total + match.score, 0) / matches.length;
  const weakestMatch = [...matches].sort((a, b) => a.score - b.score)[0];
  return { score, type: weakestMatch.type };
};

export const getComponentMatchLabel = (category, type) => {
  if (type === 'tag') return `${category} · tag match`;
  if (type === 'description') return `${category} · description match`;
  if (type === 'category') return `${category} · category match`;
  if (type === 'similar') return `${category} · similar name`;
  return `in ${category}`;
};