"""Search functionality for shell history.""" from datetime import datetime from typing import Optional from fuzzywuzzy import fuzz # type: ignore from shellhist.core import HistoryEntry, HistoryStore def fuzzy_search( store: HistoryStore, query: str, threshold: int = 70, limit: int = 20, reverse: bool = False, recent: bool = False, ) -> list[tuple[HistoryEntry, int]]: """Search history with fuzzy matching. Args: store: HistoryStore to search. query: Search query string. threshold: Minimum similarity score (0-100). limit: Maximum number of results to return. reverse: If True, sort by recency (newest first). recent: If True, boost scores for recent commands. Returns: List of (HistoryEntry, score) tuples sorted by score. """ commands = store.get_unique_commands() if not commands: return [] results = [] now = datetime.now() for command in commands: score = fuzz.token_sort_ratio(query.lower(), command.lower()) if score >= threshold: entry = _get_first_entry(store, command) if entry: if recent and entry.timestamp: hours_old = (now - entry.timestamp).total_seconds() / 3600 if hours_old < 24: score = min(100, score + 10) results.append((entry, score)) results.sort(key=lambda x: x[1], reverse=not reverse) return results[:limit] def _get_first_entry(store: HistoryStore, command: str) -> Optional[HistoryEntry]: """Get the first entry for a command from the store.""" for entry in store.entries: if entry.command == command: return entry return None def rank_by_frequency( store: HistoryStore, results: list[tuple[HistoryEntry, int]], boost_recent: bool = False, ) -> list[tuple[HistoryEntry, int, int]]: """Rank search results by frequency. Args: store: HistoryStore for frequency lookups. results: List of (HistoryEntry, score) tuples. boost_recent: If True, boost scores for recent commands. Returns: List of (HistoryEntry, score, frequency) tuples. """ ranked = [] now = datetime.now() for entry, score in results: freq = store.get_frequency(entry.command) if boost_recent and entry.timestamp: hours_old = (now - entry.timestamp).total_seconds() / 3600 if hours_old < 24: freq = freq * 2 ranked.append((entry, score, freq)) ranked.sort(key=lambda x: (x[1], x[2]), reverse=True) return ranked