#!/usr/bin/env python3
"""
rlm_adapter.py -- Genesis RLM (Recursive Language Models) PostgreSQL Adapter
Phase 1 Foundation: Relational Semantic Memory for Genesis

Connects to Elestio PostgreSQL genesis_rlm schema.
Provides upsert, query, session, and event operations.

Entity types: CLIENT, INTEGRATION, FEATURE, TASK, BLOCKER,
              SOLUTION, PATTERN, DECISION, LESSON, RESOURCE
"""

import json
import uuid
from datetime import datetime, timezone

import psycopg2
import psycopg2.extras

_DB_CONFIG = {
    "host": "postgresql-genesis-u50607.vm.elestio.app",
    "port": 25432,
    "user": "postgres",
    "password": "CiBjh6LM7Yuqkq-jo2r7eQDw",
    "database": "postgres",
    "connect_timeout": 10,
    "keepalives": 1,
    "keepalives_idle": 30,
}

VALID_ENTITY_TYPES = {
    "CLIENT", "INTEGRATION", "FEATURE", "TASK", "BLOCKER",
    "SOLUTION", "PATTERN", "DECISION", "LESSON", "RESOURCE",
    "GENERIC", "SESSION", "OTHER",
}

def _connect():
    return psycopg2.connect(**_DB_CONFIG)


def _now():
    return datetime.now(timezone.utc)


def upsert_entity(
    entity_type,
    name,
    description="",
    properties=None,
    tags=None,
    surprise_score=0.5,
    source="rlm_adapter",
):
    """Insert or update entity. Returns UUID string."""
    if properties is None:
        properties = {}
    if tags is None:
        tags = []
    entity_type = entity_type.upper()
    if entity_type not in VALID_ENTITY_TYPES:
        entity_type = "GENERIC"
    eid = str(uuid.uuid4())
    now = _now()
    conn = _connect()
    try:
        cur = conn.cursor()
        cur.execute(
            """
            INSERT INTO genesis_rlm.rlm_entities
                (id, entity_type, name, description, properties, tags,
                 surprise_score, created_at, updated_at, last_seen,
                 mention_count, source)
            VALUES (%s, %s, %s, %s, %s::jsonb, %s, %s, %s, %s, %s, 1, %s)
            ON CONFLICT (entity_type, name) DO UPDATE SET
                description     = EXCLUDED.description,
                properties      = genesis_rlm.rlm_entities.properties || EXCLUDED.properties,
                tags            = EXCLUDED.tags,
                surprise_score  = EXCLUDED.surprise_score,
                updated_at      = EXCLUDED.updated_at,
                last_seen       = EXCLUDED.last_seen,
                mention_count   = genesis_rlm.rlm_entities.mention_count + 1
            RETURNING id
            """,
            (eid, entity_type, name[:500], description or "",
             json.dumps(properties), tags,
             surprise_score, now, now, now, source[:100]),
        )
        row = cur.fetchone()
        returned_id = str(row[0]) if row else eid
        conn.commit()
        return returned_id
    finally:
        conn.close()

def add_relationship(from_id, to_id, rel_type, strength=0.5, properties=None):
    """Create typed edge between entities. Returns relationship UUID."""
    if properties is None:
        properties = {}
    rid = str(uuid.uuid4())
    conn = _connect()
    try:
        cur = conn.cursor()
        cur.execute(
            """
            INSERT INTO genesis_rlm.rlm_relationships
                (id, from_entity_id, to_entity_id, relationship_type,
                 strength, properties, created_at)
            VALUES (%s, %s::uuid, %s::uuid, %s, %s, %s::jsonb, %s)
            ON CONFLICT (from_entity_id, to_entity_id, relationship_type)
            DO NOTHING
            RETURNING id
            """,
            (rid, from_id, to_id, rel_type.upper(),
             strength, json.dumps(properties), _now()),
        )
        row = cur.fetchone()
        conn.commit()
        return str(row[0]) if row else rid
    finally:
        conn.close()


def query_entities(entity_type=None, name_like=None, limit=20):
    """Query entities with optional filters. Returns list of dicts."""
    conditions = []
    params = []
    if entity_type:
        conditions.append("entity_type = %s")
        params.append(entity_type.upper())
    if name_like:
        conditions.append("name ILIKE %s")
        params.append(f"%{name_like}%")
    where = ("WHERE " + " AND ".join(conditions)) if conditions else ""
    params.append(limit)
    conn = _connect()
    try:
        cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
        cur.execute(
            f"""
            SELECT id, entity_type, name, description, properties, tags,
                   surprise_score, created_at, updated_at, last_seen,
                   mention_count, source
            FROM genesis_rlm.rlm_entities
            {where}
            ORDER BY last_seen DESC
            LIMIT %s
            """,
            params,
        )
        return [dict(r) for r in cur.fetchall()]
    finally:
        conn.close()


def get_entity_by_name(name):
    """Return first entity matching name (case-insensitive). None if not found."""
    conn = _connect()
    try:
        cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
        cur.execute(
            """
            SELECT id, entity_type, name, description, properties, tags,
                   surprise_score, created_at, updated_at, last_seen,
                   mention_count, source
            FROM genesis_rlm.rlm_entities
            WHERE name ILIKE %s
            ORDER BY mention_count DESC
            LIMIT 1
            """,
            (name,),
        )
        row = cur.fetchone()
        return dict(row) if row else None
    finally:
        conn.close()

def record_session(
    session_id,
    summary="",
    entities_touched=0,
    decisions_made=0,
    cost_usd=0.0,
    handoff_file="",
    ended=True,
):
    """Upsert session record. Returns session UUID."""
    sid = str(uuid.uuid4())
    now = _now()
    conn = _connect()
    try:
        cur = conn.cursor()
        cur.execute(
            """
            INSERT INTO genesis_rlm.rlm_sessions
                (id, session_id, started_at, ended_at, summary,
                 entities_touched, decisions_made, cost_usd, handoff_file)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
            ON CONFLICT (session_id) DO UPDATE SET
                ended_at         = EXCLUDED.ended_at,
                summary          = EXCLUDED.summary,
                entities_touched = EXCLUDED.entities_touched,
                decisions_made   = EXCLUDED.decisions_made,
                cost_usd         = EXCLUDED.cost_usd,
                handoff_file     = EXCLUDED.handoff_file
            RETURNING id
            """,
            (sid, session_id[:100], now,
             now if ended else None,
             summary, entities_touched, decisions_made,
             cost_usd, handoff_file or ""),
        )
        row = cur.fetchone()
        conn.commit()
        return str(row[0]) if row else sid
    finally:
        conn.close()


def add_event(event_type, entity_id=None, session_id=None, payload=None):
    """Append raw event to rlm_events stream. Returns event UUID."""
    if payload is None:
        payload = {}
    eid_val = str(uuid.uuid4())
    conn = _connect()
    try:
        cur = conn.cursor()
        cur.execute(
            """
            INSERT INTO genesis_rlm.rlm_events
                (id, event_type, entity_id, session_id, payload, created_at)
            VALUES (%s, %s, %s::uuid, %s, %s::jsonb, %s)
            RETURNING id
            """,
            (eid_val, event_type[:100],
             entity_id,
             session_id[:100] if session_id else None,
             json.dumps(payload), _now()),
        )
        row = cur.fetchone()
        conn.commit()
        return str(row[0]) if row else eid_val
    finally:
        conn.close()


def get_stats():
    """Return entity counts by type and totals."""
    conn = _connect()
    try:
        cur = conn.cursor()
        cur.execute(
            """
            SELECT entity_type, COUNT(*) AS cnt
            FROM genesis_rlm.rlm_entities
            GROUP BY entity_type ORDER BY cnt DESC
            """
        )
        by_type = {r[0]: r[1] for r in cur.fetchall()}
        cur.execute("SELECT COUNT(*) FROM genesis_rlm.rlm_entities")
        total_entities = cur.fetchone()[0]
        cur.execute("SELECT COUNT(*) FROM genesis_rlm.rlm_relationships")
        total_relationships = cur.fetchone()[0]
        cur.execute("SELECT COUNT(*) FROM genesis_rlm.rlm_sessions")
        total_sessions = cur.fetchone()[0]
        cur.execute("SELECT COUNT(*) FROM genesis_rlm.rlm_events")
        total_events = cur.fetchone()[0]
        return {
            "total_entities": total_entities,
            "total_relationships": total_relationships,
            "total_sessions": total_sessions,
            "total_events": total_events,
            "by_type": by_type,
        }
    finally:
        conn.close()


def get_recent_entities(limit=10):
    """Return N most recently seen entities."""
    return query_entities(limit=limit)


def test_connection():
    """Test DB connectivity. Returns True on success."""
    try:
        conn = _connect()
        cur = conn.cursor()
        cur.execute("SELECT 1")
        conn.close()
        print("[rlm_adapter] Connection OK -- genesis_rlm schema ready")
        return True
    except Exception as e:
        print(f"[rlm_adapter] Connection FAILED: {e}")
        return False


if __name__ == "__main__":
    test_connection()
    stats = get_stats()
    print(f"[rlm_adapter] Stats: {json.dumps(stats, indent=2, default=str)}")