#!/usr/bin/env python3
"""
Temporal Window Search Source — surfaces all knowledge from the last 72 hours.

This source exists because recency-based queries ("what did we just do?",
"what happened today?") fail with keyword-only relevance scoring. This source
bypasses relevance entirely and returns the most recent items, scored 70%
recency / 30% keyword relevance.

Expert 4 (Pattern Recognition) + Expert 2 (Memory Systems) recommendation.
"""
import logging
from datetime import datetime, timezone, timedelta

log = logging.getLogger("pulse.brain_search.temporal")

WINDOW_HOURS = 72
MAX_PER_TABLE = 5


def search_temporal_window(query: str, max_results: int = 10) -> list:
    """Return the most recent knowledge items from the last 72 hours.

    Scored by recency (70%) and keyword relevance (30%).
    Always returns results if any exist in the window, regardless of query match.
    """
    try:
        from pulse.core.brain_db import get_conn
    except ImportError:
        return []

    conn = get_conn()
    cutoff = (datetime.now(timezone.utc) - timedelta(hours=WINDOW_HOURS)).isoformat()
    tables = ["lessons", "failures", "patterns"]

    all_hits = []
    for table in tables:
        try:
            rows = conn.execute(
                f"SELECT id, title, content, confidence, salience, agent, domain, timestamp "
                f"FROM {table} WHERE timestamp > ? AND validity = 'valid' "
                f"ORDER BY timestamp DESC LIMIT ?",
                (cutoff, MAX_PER_TABLE)
            ).fetchall()
            for r in rows:
                title = r[1] or ""
                content = r[2] or ""
                ts = r[7] or ""
                # Compute recency score: 1.0 for just now, 0.0 for 72h ago
                recency = _recency_score(ts)
                # Compute keyword relevance
                relevance = _keyword_relevance(query, title + " " + content)
                # Combined: 70% recency, 30% relevance
                combined = recency * 0.7 + relevance * 0.3

                all_hits.append({
                    "id": r[0],
                    "title": title,
                    "text": title or content[:200],
                    "content": content[:500],
                    "confidence": max(combined, r[3] or 0.5),
                    "salience": r[4] or 1.0,
                    "agent": r[5] or "",
                    "domain": r[6] or "general",
                    "timestamp": ts,
                    "source": table,
                    "type": f"recent_{table[:-1]}",
                    "_temporal_recency": round(recency, 3),
                    "_temporal_relevance": round(relevance, 3),
                })
        except Exception as e:
            log.debug("Temporal search on %s failed: %s", table, e)

    # Pull recent session_turns — the write-only table that was never searchable
    try:
        turns = conn.execute(
            "SELECT st.session_id, st.role, st.content, st.timestamp, st.domain, "
            "       s.agent "
            "FROM session_turns st "
            "LEFT JOIN sessions s ON st.session_id = s.id "
            "WHERE st.timestamp > ? AND st.content IS NOT NULL AND LENGTH(st.content) > 20 "
            "ORDER BY st.timestamp DESC LIMIT 10",
            (cutoff,)
        ).fetchall()
        for t in turns:
            content = t[2] or ""
            recency = _recency_score(t[3] or "")
            relevance = _keyword_relevance(query, content)
            combined = recency * 0.7 + relevance * 0.3
            agent = t[5] or "unknown"
            role = t[1] or "unknown"
            all_hits.append({
                "id": f"turn_{t[0]}_{t[3]}",
                "title": f"[{agent}/{role}] {content[:80]}",
                "text": content[:200],
                "content": content[:500],
                "confidence": max(combined, 0.45),
                "agent": agent,
                "domain": t[4] or "general",
                "timestamp": t[3] or "",
                "source": "session_turns",
                "type": "recent_turn",
                "_temporal_recency": round(recency, 3),
            })
    except Exception as e:
        log.debug("Session turns search failed: %s", e)

    # Also pull recent session summaries
    try:
        sessions = conn.execute(
            "SELECT id, agent, started_at, summary, domain FROM sessions "
            "WHERE started_at > ? AND summary IS NOT NULL AND summary != '' "
            "ORDER BY started_at DESC LIMIT 5",
            (cutoff,)
        ).fetchall()
        for s in sessions:
            recency = _recency_score(s[2] or "")
            relevance = _keyword_relevance(query, s[3] or "")
            combined = recency * 0.7 + relevance * 0.3
            all_hits.append({
                "id": s[0],
                "title": f"[{s[1] or 'agent'}] session",
                "text": s[3][:200] if s[3] else "",
                "content": s[3] or "",
                "confidence": max(combined, 0.5),
                "agent": s[1] or "",
                "domain": s[4] or "general",
                "timestamp": s[2] or "",
                "source": "sessions",
                "type": "recent_session",
                "_temporal_recency": round(recency, 3),
            })
    except Exception as e:
        log.debug("Temporal session search failed: %s", e)

    # Sort by combined score, return top N
    all_hits.sort(key=lambda x: x.get("confidence", 0), reverse=True)
    return all_hits[:max_results]


def _recency_score(timestamp: str) -> float:
    """Score from 1.0 (just now) to 0.0 (72h+ ago)."""
    if not timestamp:
        return 0.0
    try:
        ts = datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
        now = datetime.now(timezone.utc)
        age_hours = (now - ts).total_seconds() / 3600
        if age_hours <= 0:
            return 1.0
        if age_hours >= WINDOW_HOURS:
            return 0.0
        return 1.0 - (age_hours / WINDOW_HOURS)
    except Exception:
        return 0.0


def _keyword_relevance(query: str, text: str) -> float:
    """Simple keyword overlap for temporal results."""
    if not query or not text:
        return 0.0
    qwords = set(query.lower().split())
    twords = set(text.lower().split())
    if not qwords:
        return 0.0
    return len(qwords & twords) / len(qwords)
