"""Memory retrieval — keyword search and daily log loading."""

import re
from dataclasses import dataclass
from datetime import date, timedelta

from fliiq.runtime.memory.manager import MemoryManager


@dataclass
class SearchResult:
    file_name: str
    line_number: int
    line: str
    context: str  # 1 line above/below


def search_memories(
    manager: MemoryManager,
    query: str,
    ignore_case: bool = True,
    max_results: int = 50,
) -> list[SearchResult]:
    """Regex search across all .md files in the memory directory."""
    flags = re.IGNORECASE if ignore_case else 0
    try:
        regex = re.compile(query, flags)
    except re.error as e:
        raise ValueError(f"Invalid search pattern: {e}")

    results = []
    memory_dir = manager.memory_dir
    if not memory_dir.is_dir():
        return results

    for md_file in sorted(memory_dir.rglob("*.md")):
        if len(results) >= max_results:
            break
        try:
            lines = md_file.read_text(encoding="utf-8").splitlines()
        except (OSError, UnicodeDecodeError):
            continue

        rel_name = str(md_file.relative_to(memory_dir))
        for i, line in enumerate(lines):
            if len(results) >= max_results:
                break
            if regex.search(line):
                # Build context: 1 line above/below
                ctx_lines = []
                if i > 0:
                    ctx_lines.append(lines[i - 1])
                ctx_lines.append(line)
                if i < len(lines) - 1:
                    ctx_lines.append(lines[i + 1])
                context = "\n".join(ctx_lines)

                results.append(SearchResult(
                    file_name=rel_name,
                    line_number=i + 1,
                    line=line,
                    context=context,
                ))

    return results


def load_recent_daily(manager: MemoryManager, days: int = 3) -> str:
    """Concatenate the last N days of daily logs."""
    today = date.today()
    parts = []
    for offset in range(days):
        d = today - timedelta(days=offset)
        date_str = d.isoformat()
        content = manager.load_memory(date_str)
        if content:
            parts.append(f"### {date_str}\n{content}")

    return "\n\n".join(parts)


def load_relevant_entities(
    manager: MemoryManager,
    prompt: str,
    max_chunks: int = 5,
    max_chars: int = 3000,
) -> str:
    """Load people/ and topics/ files relevant to the prompt.

    Phase 1: keyword overlap scoring with name-match boost.
    """
    prompt_words = set(re.findall(r"\b\w{3,}\b", prompt.lower()))
    if not prompt_words:
        return ""

    candidates: list[tuple[int, str, str, str]] = []  # (score, category, stem, content)

    for category in ("people", "topics"):
        cat_dir = manager.memory_dir / category
        if not cat_dir.is_dir():
            continue
        for md_file in cat_dir.glob("*.md"):
            try:
                content = md_file.read_text(encoding="utf-8")
            except (OSError, UnicodeDecodeError):
                continue

            content_words = set(re.findall(r"\b\w{3,}\b", content.lower()))
            overlap = len(prompt_words & content_words)

            # Strong boost if the entity name appears in the prompt
            name_words = md_file.stem.replace("-", " ").lower().split()
            if any(w in prompt.lower() for w in name_words if len(w) >= 3):
                overlap += 10

            if overlap > 0:
                candidates.append((overlap, category, md_file.stem, content))

    candidates.sort(key=lambda x: -x[0])

    parts: list[str] = []
    total_chars = 0
    for _score, category, name, content in candidates[:max_chunks]:
        chunk = f"### {category}/{name}\n{content[:800]}"
        if total_chars + len(chunk) > max_chars:
            break
        parts.append(chunk)
        total_chars += len(chunk)

    return "\n\n".join(parts)
