#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""Session Lifecycle — cross-session memory for PULSE agents.

P47: Every conversation gets a session record in SQLite. When a new agent
boots, it sees the last 3 sessions from ANY agent — not just its own.

Tables: sessions, session_turns (created by brain_db.init_db)
"""

import json
import hashlib
from datetime import datetime, timezone
from typing import Optional


def _now() -> str:
    return datetime.now(timezone.utc).isoformat()


def _session_id(agent: str) -> str:
    ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
    h = hashlib.md5(f"{agent}:{ts}".encode()).hexdigest()[:6]
    return f"ses_{agent}_{ts}_{h}"


def start_session(agent: str, task: str = "", domain: str = "general") -> str:
    """Create a new session row. Returns session_id.

    Also detects orphaned 'active' sessions from the same agent and
    marks them as 'abandoned' (crash recovery).
    """
    from pulse.core.brain_db import get_conn

    conn = get_conn()
    sid = _session_id(agent)
    now = _now()

    # Orphan recovery: close any stale 'active' sessions from this agent
    try:
        with conn:
            conn.execute(
                "UPDATE sessions SET status='abandoned', ended_at=? "
                "WHERE agent=? AND status='active'",
                (now, agent),
            )
    except Exception:
        pass

    # Find predecessor (most recent completed session from any agent)
    predecessor = None
    try:
        row = conn.execute(
            "SELECT id FROM sessions WHERE status IN ('completed','abandoned') "
            "ORDER BY started_at DESC LIMIT 1"
        ).fetchone()
        if row:
            predecessor = row[0]
    except Exception:
        pass

    with conn:
        conn.execute(
            "INSERT OR IGNORE INTO sessions "
            "(id, agent, started_at, domain, status, predecessor_session, metadata) "
            "VALUES (?, ?, ?, ?, 'active', ?, '{}')",
            (sid, agent, now, domain, predecessor),
        )
    return sid


def record_turn(
    session_id: str,
    role: str,
    text: str,
    domain: str = "general",
    timestamp: str = "",
) -> None:
    """Record a conversation turn. Called by the bridge on each flush."""
    if not session_id or not text:
        return
    from pulse.core.brain_db import get_conn

    conn = get_conn()
    ts = timestamp or _now()

    # Get next turn index
    row = conn.execute(
        "SELECT MAX(turn_index) FROM session_turns WHERE session_id=?",
        (session_id,),
    ).fetchone()
    next_idx = (row[0] or 0) + 1 if row else 1

    # Truncate to 5000 chars to keep DB manageable
    trimmed = text[:5000]

    with conn:
        conn.execute(
            "INSERT INTO session_turns "
            "(session_id, turn_index, role, content, content_len, timestamp, domain) "
            "VALUES (?, ?, ?, ?, ?, ?, ?)",
            (session_id, next_idx, role.lower(), trimmed, len(text), ts, domain),
        )
        # Update turn count on session
        conn.execute(
            "UPDATE sessions SET turn_count=turn_count+1 WHERE id=?", (session_id,)
        )


def end_session(
    session_id: str,
    summary: str = "",
    user_intent: str = "",
    key_decisions: list = None,
    key_discoveries: list = None,
    files_touched: list = None,
) -> None:
    """Mark session as completed with summary data."""
    if not session_id:
        return
    from pulse.core.brain_db import get_conn

    conn = get_conn()
    now = _now()

    with conn:
        conn.execute(
            "UPDATE sessions SET "
            "ended_at=?, summary=?, user_intent=?, "
            "key_decisions=?, key_discoveries=?, files_touched=?, "
            "status='completed' "
            "WHERE id=?",
            (
                now,
                (summary or "")[:2000],
                (user_intent or "")[:500],
                json.dumps((key_decisions or [])[:20]),
                json.dumps((key_discoveries or [])[:20]),
                json.dumps((files_touched or [])[:30]),
                session_id,
            ),
        )


def get_recent_sessions(limit: int = 5, agent: str = None) -> list:
    """Get most recent completed/abandoned sessions across ALL agents.

    This is the core cross-session query. Returns dicts with:
    id, agent, started_at, ended_at, summary, user_intent, turn_count, domain, status
    """
    from pulse.core.brain_db import get_conn

    conn = get_conn()
    if agent:
        rows = conn.execute(
            "SELECT id, agent, started_at, ended_at, summary, user_intent, "
            "turn_count, domain, status FROM sessions "
            "WHERE agent=? AND status IN ('completed','abandoned') "
            "ORDER BY started_at DESC LIMIT ?",
            (agent, limit),
        ).fetchall()
    else:
        rows = conn.execute(
            "SELECT id, agent, started_at, ended_at, summary, user_intent, "
            "turn_count, domain, status FROM sessions "
            "WHERE status IN ('completed','abandoned') "
            "ORDER BY started_at DESC LIMIT ?",
            (limit,),
        ).fetchall()
    return [dict(r) for r in rows]


def get_active_session(agent: str) -> Optional[str]:
    """Get the active session ID for an agent, if any.

    Uses a prefix LIKE match so "claude" finds both "claude" and
    "claude_interactive_20020", "claude_daemon", etc.  This resolves
    the mismatch where pulse_boot.py creates sessions with a full
    qualified name while the bridge looks them up by bare agent prefix.
    """
    from pulse.core.brain_db import get_conn

    conn = get_conn()
    row = conn.execute(
        "SELECT id FROM sessions WHERE (agent = ? OR agent LIKE ? || '_%') "
        "AND status='active' ORDER BY started_at DESC LIMIT 1",
        (agent, agent),
    ).fetchone()
    return row[0] if row else None


def _get_session_turns(session_id: str, limit: int = 20) -> list:
    """Get the most recent turns from a session."""
    from pulse.core.brain_db import get_conn

    conn = get_conn()
    rows = conn.execute(
        "SELECT turn_index, role, content, timestamp FROM session_turns "
        "WHERE session_id=? ORDER BY turn_index DESC LIMIT ?",
        (session_id, limit),
    ).fetchall()
    return [dict(r) for r in reversed(rows)]


def _record_interaction(agent: str, user_text: str, agent_text: str = "",
                       domain: str = "general", timestamp: str = "") -> None:
    """Record a full interaction (user+agent turns) for the given agent.
    Called from bridge._flush_interaction. Auto-finds active session."""
    sid = get_active_session(agent)
    if not sid:
        return
    record_turn(sid, "user", user_text, domain=domain, timestamp=timestamp)
    if agent_text:
        record_turn(sid, "agent", agent_text, domain=domain, timestamp=timestamp)
