const STOP_WORDS = new Set([
  'the', 'is', 'a', 'an', 'of', 'for', 'to', 'and', 'what', 'how', 'do', 'does'
]);

/**
 * Normalize text by lowercasing and removing punctuation.
 * @param {string} text
 * @returns {string}
 */
function normalizeText(text) {
  if (typeof text !== 'string') return '';
  return text
    .toLowerCase()

    .replace(/[^a-z0-9\s]/g, ' ')
    .replace(/\s+/g, ' ')
    .trim();
}

/**
 * Convert a text string into keyword tokens while filtering stop words.
 * @param {string} text
 * @returns {string[]}
 */
function tokenize(text) {
  return normalizeText(text)
    .split(' ')
    .filter(Boolean)
    .filter((term) => !STOP_WORDS.has(term));
}

/**
 * Build a simple frequency map from an array of tokens.
 * @param {string[]} tokens
 * @returns {Object<string, number>}
 */
function buildFrequencyMap(tokens) {
  return tokens.reduce((acc, token) => {
    acc[token] = (acc[token] || 0) + 1;
    return acc;
  }, {});
}

/**
 * Calculate a relevance score for a single document.
 * Exact query term matches in the title and content are weighted higher.
 * @param {string[]} queryTerms
 * @param {{ name?: string, content: string }} document
 * @returns {number}
 */
function scoreDocument(queryTerms, document) {
  const contentTokens = tokenize(document.content);
  const contentFreq = buildFrequencyMap(contentTokens);

  const titleText = typeof document.name === 'string'
    ? document.name.replace(/\.md$/i, '')
    : '';
  const titleTokens = tokenize(titleText);

  let score = 0;

  queryTerms.forEach((term) => {
    if (titleTokens.includes(term)) {
      score += 5;
    }

    if (contentFreq[term]) {
      score += contentFreq[term] * 3;
    }
  });

  return score;
}

/**
 * Search the markdown knowledge base and return the top 3 matches.
 * @param {string} userQuestion
 * @param {Array<{ name: string, content: string }>} knowledgeBase
 * @returns {Array<{ score: number, content: string }>}
 */
function searchKnowledge(userQuestion, knowledgeBase) {
  if (typeof userQuestion !== 'string') {
    throw new TypeError('userQuestion must be a string');
  }

  if (!Array.isArray(knowledgeBase)) {
    throw new TypeError('knowledgeBase must be an array');
  }

  const queryTerms = tokenize(userQuestion);
  if (queryTerms.length === 0) {
    return [];
  }

  const scoredResults = knowledgeBase
    .map((document) => {
      if (!document || typeof document.content !== 'string') {
        throw new TypeError('Each knowledge document must include a string content field');
      }

      const score = scoreDocument(queryTerms, document);
      return {
        score,
        content: document.content
      };
    })
    .filter((result) => result.score > 0)
    .sort((a, b) => b.score - a.score)
    .slice(0, 3);

  return scoredResults;
}

module.exports = {
  searchKnowledge
};
