***fix the bugs in the code below***
// search-core.js - with improved partial matching
(function() {
  const DEFAULT_OPTIONS = {
    highlightPreviewLength: 100,
    fuzzyMatchThreshold: 0.7,
    maxFuzzyResults: 5,
    maxResultsPerPage: 10,
    partialMatchEnabled: true,     // Control for partial matching
    minPartialMatchLength: 3,      // Minimum characters for partial matching
    weightFactors: {
      title: 2.0,
      heading: 1.5,
      description: 1.3,
      content: 1.0,
      exactMatch: 1.5,           // Boost for exact matches
      partialMatch: 0.8          // Lower weight for partial matches
    },
    stopWords: ['the', 'and', 'a', 'an', 'in', 'on', 'at', 'for', 'to', 'of', 'with']
  };

  class SearchCore {
    constructor(options = {}) {
      this.options = { ...DEFAULT_OPTIONS, ...options };
      this.wordsData = null;
      this.allTextData = null;
      this.wordKeys = []; // For efficient partial matching
    }

    init() {
      try {
        if (window.siteSearch) {
          this.wordsData = window.siteSearch.getWordsData();
          this.allTextData = window.siteSearch.getAllTextData();
          
          // Create an array of all word keys for efficient partial matching
          this.wordKeys = Object.keys(this.wordsData);
          return true;
        } else {
          console.error('Search Core: siteSearch object not found.');
          return false;
        }
      } catch (error) {
        console.error('Search Core: Failed to initialize search data', error);
        return false;
      }
    }

    search(query) {
      if (!this.wordsData || !this.allTextData) {
        if (!this.init()) {
          return { success: false, error: 'Search data not available' };
        }
      }

      const searchTerms = this._normalizeAndTokenize(query);
      
      if (searchTerms.length === 0) {
        return { success: true, results: [], message: 'No valid search terms provided' };
      }

      const termResults = searchTerms.map(term => this._processTerm(term));
      const combinedResults = this._combineResults(termResults);

      return {
        success: true,
        results: combinedResults,
        query: query,
        totalResults: combinedResults.length
      };
    }

    _processTerm(term) {
      // Check if the term exists exactly
      const exactMatch = this.wordsData[term] !== undefined;
      
      // Find partial matches if enabled
      let partialMatches = [];
      if (this.options.partialMatchEnabled && term.length >= this.options.minPartialMatchLength) {
        partialMatches = this._findPartialMatches(term);
      }
      
      // Find fuzzy matches only if we don't have exact or good partial matches
      let fuzzyMatches = [];
      if (!exactMatch && partialMatches.length < 2) {
        fuzzyMatches = this._findSimilarTerms(term);
      }
      
      // Process all match types
      const allPages = [];
      
      // Add exact match pages
      if (exactMatch) {
        const exactPages = this._findTermInPages(term, 'exact');
        allPages.push(...exactPages);
      }
      
      // Add partial match pages
      partialMatches.forEach(match => {
        const partialPages = this._findTermInPages(match.term, 'partial', match.confidence);
        partialPages.forEach(page => {
          page.matchType = 'partial';
          page.matchedTerm = match.term;
          allPages.push(page);
        });
      });
      
      // Add fuzzy match pages only if needed
      if (!exactMatch && partialMatches.length === 0) {
        fuzzyMatches.forEach(match => {
          const fuzzyPages = this._findTermInPages(match.term, 'fuzzy', match.confidence);
          fuzzyPages.forEach(page => {
            page.matchType = 'fuzzy';
            page.matchedTerm = match.term;
            allPages.push(page);
          });
        });
      }
      
      return {
        term: term,
        exact: exactMatch,
        pages: allPages,
        suggestions: [
          ...partialMatches.map(m => ({ ...m, type: 'partial' })),
          ...fuzzyMatches.map(m => ({ ...m, type: 'fuzzy' }))
        ]
      };
    }

    // Efficiently find partial matches (words containing the search term)
    _findPartialMatches(term) {
      const results = [];
      
      // Binary search wouldn't work here since we need substring matching
      // Use efficient filtering with early returns
      for (let i = 0; i < this.wordKeys.length; i++) {
        const word = this.wordKeys[i];
        
        // Skip words that are shorter than our search term
        if (word.length < term.length) continue;
        
        // Check if word contains our term
        if (word.includes(term)) {
          // Calculate a confidence score based on how much of the word is matched
          const confidence = term.length / word.length;
          
          results.push({
            term: word,
            confidence: confidence
          });
          
          // Early return if we have enough good matches
          if (results.length >= 10 && results.some(r => r.confidence > 0.7)) {
            break;
          }
        }
      }
      
      // Sort by confidence (higher is better)
      return results
        .sort((a, b) => b.confidence - a.confidence)
        .slice(0, this.options.maxFuzzyResults);
    }

    // Find term in pages with match type information
    _findTermInPages(term, matchType = 'exact', confidence = 1.0) {
      const results = [];
      const wordInfo = this.wordsData[term];
      
      if (!wordInfo || !wordInfo.pages || !this.allTextData.pages) {
        return results;
      }
      
      // Adjust match weight based on match type
      let matchWeight = 1.0;
      if (matchType === 'exact') {
        matchWeight = this.options.weightFactors.exactMatch;
      } else if (matchType === 'partial') {
        matchWeight = this.options.weightFactors.partialMatch;
      } else if (matchType === 'fuzzy') {
        matchWeight = confidence * this.options.weightFactors.partialMatch;
      }
      
      wordInfo.pages.forEach(pagePath => {
        const pageData = this.allTextData.pages[pagePath];
        if (!pageData) return;
        
        const matchingSegments = this._findMatchingSegments(pageData, term, wordInfo.variations);
        
        if (matchingSegments.length > 0) {
          results.push({
            page: pagePath,
            title: pageData.title || this._getFilenameFromPath(pagePath),
            description: pageData.description || this._createDescriptionFromSegments(matchingSegments),
            matches: matchingSegments,
            matchType: matchType,
            confidence: confidence,
            matchWeight: matchWeight,
            relevanceScore: this._calculateRelevanceScore(matchingSegments, matchWeight),
            highlightRanges: this._getHighlightRanges(matchingSegments, term)
          });
        }
      });
      
      return results;
    }

    // Calculate relevance score with match type weight
    _calculateRelevanceScore(matches, matchWeight = 1.0) {
      let score = 0;
      
      matches.forEach(match => {
        // Base score is 1 for each occurrence
        let matchScore = 1;
        
        // Apply weight factors based on segment type
        if (match.type === 'title') {
          matchScore *= this.options.weightFactors.title;
        } else if (match.type === 'heading') {
          matchScore *= this.options.weightFactors.heading;
        } else if (match.type === 'description') {
          matchScore *= this.options.weightFactors.description;
        } else {
          matchScore *= this.options.weightFactors.content;
        }
        
        // Apply weight from segment if available
        if (match.weight) {
          matchScore *= match.weight;
        }
        
        // Apply match type weight
        matchScore *= matchWeight;
        
        score += matchScore;
      });
      
      return score;
    }

    // The rest of the methods remain similar, with minor adjustments for efficiency

    _findMatchingSegments(pageData, term, variations) {
      const matchingSegments = [];
      
      if (!pageData.segments) return matchingSegments;
      
      pageData.segments.forEach(segment => {
        // Check if segment contains term or any of its variations
        const containsTerm = this._segmentContainsTerm(segment, term, variations);
        if (containsTerm) {
          matchingSegments.push({
            ...segment,
            term: term,
            preview: this._createPreview(segment.text, term)
          });
        }
      });
      
      return matchingSegments;
    }

    _segmentContainsTerm(segment, term, variations) {
      const text = segment.text.toLowerCase();
      if (text.includes(term)) return true;
      
      if (variations && Array.isArray(variations)) {
        // Use some() for early return
        return variations.some(variation => 
          text.includes(variation.toLowerCase())
        );
      }
      
      return false;
    }

    _createPreview(text, term) {
      const lowerText = text.toLowerCase();
      const termIndex = lowerText.indexOf(term.toLowerCase());
      
      if (termIndex === -1) return text;
      
      const halfLength = this.options.highlightPreviewLength;
      let start = Math.max(0, termIndex - halfLength);
      let end = Math.min(text.length, termIndex + term.length + halfLength);
      
      // Try to start and end at word boundaries
      while (start > 0 && text[start] !== ' ' && text[start] !== '.') start--;
      while (end < text.length && text[end] !== ' ' && text[end] !== '.') end++;
      
      let preview = text.substring(start, end).trim();
      
      // Add ellipsis if we're not at the beginning or end
      if (start > 0) preview = '...' + preview;
      if (end < text.length) preview = preview + '...';
      
      return preview;
    }

    // Levenshtein distance for fuzzy matching remains unchanged
    _findSimilarTerms(term) {
      const results = [];
      const termLength = term.length;
      
      if (termLength < 3) return results;
      
      // Use a more efficient approach by limiting comparisons
      // Only check words with similar length for efficiency
      const candidateWords = this.wordKeys.filter(word => 
        Math.abs(word.length - termLength) <= 2
      );
      
      // If we have too many candidates, take a sample
      const wordsToCheck = candidateWords.length > 500 
        ? candidateWords.slice(0, 500) 
        : candidateWords;
      
      for (const word of wordsToCheck) {
        const distance = this._levenshteinDistance(term, word);
        const similarity = 1 - distance / Math.max(term.length, word.length);
        
        if (similarity >= this.options.fuzzyMatchThreshold) {
          results.push({
            term: word,
            confidence: similarity
          });
        }
      }
      
      return results
        .sort((a, b) => b.confidence - a.confidence)
        .slice(0, this.options.maxFuzzyResults);
    }

    _levenshteinDistance(a, b) {
      // Optimize for common cases
      if (a === b) return 0;
      if (a.length === 0) return b.length;
      if (b.length === 0) return a.length;
      
      // Use matrix calculation for Levenshtein distance
      const matrix = [];
      
      // Initialize matrix
      for (let i = 0; i <= b.length; i++) {
        matrix[i] = [i];
      }
      
      for (let j = 0; j <= a.length; j++) {
        matrix[0][j] = j;
      }
      
      // Fill matrix
      for (let i = 1; i <= b.length; i++) {
        for (let j = 1; j <= a.length; j++) {
          const cost = a[j - 1] === b[i - 1] ? 0 : 1;
          matrix[i][j] = Math.min(
            matrix[i - 1][j] + 1,      // deletion
            matrix[i][j - 1] + 1,      // insertion
            matrix[i - 1][j - 1] + cost // substitution
          );
        }
      }
      
      return matrix[b.length][a.length];
    }

// Update to the _combineResults method to preserve match type information
_combineResults(termResults) {
  const pageMap = new Map();
  
  termResults.forEach(termResult => {
    const searchTerm = termResult.term;
    
    termResult.pages.forEach(page => {
      const existingPage = pageMap.get(page.page);
      
      // Create match info object
      const matchInfo = {
        term: searchTerm,
        matchedTerm: page.matchedTerm || searchTerm,
        type: page.matchType || 'exact', // 'exact', 'partial', or 'fuzzy'
        confidence: page.confidence || 1.0
      };
      
      if (existingPage) {
        // Merge matches and update relevance score
        existingPage.matches = [...existingPage.matches, ...page.matches];
        existingPage.relevanceScore += page.relevanceScore;
        
        // Track all match types and terms for this page
        existingPage.matchInfo.push(matchInfo);
        
        // Merge highlight ranges
        if (page.highlightRanges) {
          existingPage.highlightRanges = [
            ...(existingPage.highlightRanges || []),
            ...(page.highlightRanges || [])
          ];
        }
      } else {
        // New page with initial match info
        const newPage = {...page};
        newPage.matchInfo = [matchInfo];
        pageMap.set(page.page, newPage);
      }
    });
  });
  
  // Convert map to array and sort by relevance
  const results = Array.from(pageMap.values())
    .sort((a, b) => b.relevanceScore - a.relevanceScore);
  
  // Add best match type as a top-level property for easy UI access
  results.forEach(result => {
    // Find the "best" match type (exact > partial > fuzzy)
    if (result.matchInfo.some(m => m.type === 'exact')) {
      result.bestMatchType = 'exact';
    } else if (result.matchInfo.some(m => m.type === 'partial')) {
      result.bestMatchType = 'partial';
    } else {
      result.bestMatchType = 'fuzzy';
    }
    
    // Add a user-friendly match description
    switch(result.bestMatchType) {
      case 'exact':
        result.matchDescription = 'Exact match';
        break;
      case 'partial':
        result.matchDescription = 'Contains your search';
        break;
      case 'fuzzy':
        result.matchDescription = 'Similar to your search';
        break;
    }
  });
  
  return results;
}

    _getFilenameFromPath(path) {
      const parts = path.split('/');
      let filename = parts[parts.length - 1];
      return filename.replace(/\.(html|htm)$/, '').replace(/-/g, ' ');
    }

    _createDescriptionFromSegments(segments) {
      if (segments.length === 0) return '';
      
      // Use the first segment's preview as description
      if (segments[0].preview) {
        return segments[0].preview;
      }
      
      return segments[0].text.substring(0, 150) + '...';
    }

    _getHighlightRanges(segments, term) {
      return segments.map(segment => {
        if (segment.position) {
          return {
            start: segment.position.start,
            end: segment.position.end,
            term: term
          };
        }
        return null;
      }).filter(range => range !== null);
    }

    // Pagination and URL generation methods
    getPaginationInfo(results, currentPage = 1) {
      const totalResults = results.length;
      const totalPages = Math.ceil(totalResults / this.options.maxResultsPerPage);
      
      currentPage = Math.max(1, Math.min(currentPage, totalPages || 1));
      
      const startIndex = (currentPage - 1) * this.options.maxResultsPerPage;
      const endIndex = Math.min(startIndex + this.options.maxResultsPerPage, totalResults);
      
      return {
        currentPage,
        totalPages,
        totalResults,
        resultsPerPage: this.options.maxResultsPerPage,
        startIndex,
        endIndex,
        pageResults: results.slice(startIndex, endIndex)
      };
    }

    generateHighlightUrl(pageUrl, highlightRanges) {
      if (!highlightRanges || highlightRanges.length === 0) {
        return pageUrl;
      }
      
      const highlightParam = encodeURIComponent(JSON.stringify(highlightRanges));
      const hasParams = pageUrl.includes('?');
      return `${pageUrl}${hasParams ? '&' : '?'}highlight=${highlightParam}`;
    }
  }

  // Expose the class to the global scope
  window.SearchCore = SearchCore;
})();