"""MemoryIndex — SQLite + FTS5 structured memory store."""

import json
import sqlite3
from typing import Optional

from neo_cortex.models import CompactMemory, MemoryRecord, StructuredFields


_SCHEMA = """
CREATE TABLE IF NOT EXISTS memories (
    id TEXT PRIMARY KEY,
    session_id TEXT NOT NULL,
    timestamp REAL NOT NULL,
    turn_number INTEGER DEFAULT 0,
    project TEXT DEFAULT 'general',
    topic TEXT DEFAULT 'unknown',
    activity TEXT DEFAULT 'discussion',
    memory_type TEXT DEFAULT 'episodic',
    energy REAL DEFAULT 1.0,
    model TEXT DEFAULT 'opus',
    source TEXT DEFAULT 'neo-cortex',
    title TEXT,
    summary TEXT,
    facts TEXT,
    concepts TEXT,
    files_touched TEXT,
    question TEXT,
    answer_preview TEXT,
    document TEXT
);

CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
    title, summary, facts, question, answer_preview,
    content=memories, content_rowid=rowid
);

CREATE INDEX IF NOT EXISTS idx_mem_timestamp ON memories(timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_mem_project ON memories(project);
CREATE INDEX IF NOT EXISTS idx_mem_activity ON memories(activity);
CREATE INDEX IF NOT EXISTS idx_mem_session ON memories(session_id);

CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
    INSERT INTO memories_fts(rowid, title, summary, facts, question, answer_preview)
    VALUES (new.rowid, new.title, new.summary, new.facts, new.question, new.answer_preview);
END;

CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
    INSERT INTO memories_fts(memories_fts, rowid, title, summary, facts, question, answer_preview)
    VALUES ('delete', old.rowid, old.title, old.summary, old.facts, old.question, old.answer_preview);
END;

CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
    INSERT INTO memories_fts(memories_fts, rowid, title, summary, facts, question, answer_preview)
    VALUES ('delete', old.rowid, old.title, old.summary, old.facts, old.question, old.answer_preview);
    INSERT INTO memories_fts(rowid, title, summary, facts, question, answer_preview)
    VALUES (new.rowid, new.title, new.summary, new.facts, new.question, new.answer_preview);
END;
"""


class MemoryIndex:
    def __init__(self, db_path: str):
        self._conn = sqlite3.connect(db_path, check_same_thread=False)
        self._conn.row_factory = sqlite3.Row
        self._conn.executescript(_SCHEMA)

    def insert(self, record: MemoryRecord, structured: Optional[StructuredFields] = None):
        self._conn.execute(
            """INSERT OR REPLACE INTO memories
            (id, session_id, timestamp, turn_number, project, topic, activity,
             memory_type, energy, model, source, title, summary, facts, concepts,
             files_touched, question, answer_preview, document)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
            (
                record.id,
                record.session_id,
                record.timestamp,
                record.turn_number,
                record.project,
                record.topic,
                record.activity.value if hasattr(record.activity, "value") else record.activity,
                record.memory_type.value if hasattr(record.memory_type, "value") else record.memory_type,
                record.energy,
                record.model,
                record.source,
                structured.title if structured else None,
                structured.summary if structured else None,
                json.dumps(structured.facts) if structured else None,
                json.dumps(structured.concepts) if structured else None,
                json.dumps(structured.files_touched) if structured else None,
                record.question,
                record.answer_preview,
                record.document,
            ),
        )
        self._conn.commit()

    def get_by_id(self, id: str) -> Optional[MemoryRecord]:
        row = self._conn.execute("SELECT * FROM memories WHERE id = ?", (id,)).fetchone()
        if not row:
            return None
        return self._row_to_record(row)

    def get_by_ids(self, ids: list[str]) -> list[MemoryRecord]:
        if not ids:
            return []
        placeholders = ",".join("?" for _ in ids)
        rows = self._conn.execute(
            f"SELECT * FROM memories WHERE id IN ({placeholders})", ids
        ).fetchall()
        return [self._row_to_record(r) for r in rows]

    def search_fts(self, query: str, project: Optional[str] = None,
                   activity: Optional[str] = None, n: int = 10) -> list[CompactMemory]:
        if query.strip():
            safe_query = query.replace('"', '""')
            sql = """SELECT m.id, m.title, m.project, m.topic, m.activity, m.timestamp, m.energy
                     FROM memories_fts f
                     JOIN memories m ON f.rowid = m.rowid
                     WHERE memories_fts MATCH ?"""
            params: list = [f'"{safe_query}"']
            if project:
                sql += " AND m.project = ?"
                params.append(project)
            if activity:
                sql += " AND m.activity = ?"
                params.append(activity)
            sql += " ORDER BY rank LIMIT ?"
            params.append(n)
        else:
            sql = """SELECT id, title, project, topic, activity, timestamp, energy
                     FROM memories WHERE 1=1"""
            params = []
            if project:
                sql += " AND project = ?"
                params.append(project)
            if activity:
                sql += " AND activity = ?"
                params.append(activity)
            sql += " ORDER BY timestamp DESC LIMIT ?"
            params.append(n)

        rows = self._conn.execute(sql, params).fetchall()
        return [
            CompactMemory(
                id=r["id"],
                title=r["title"],
                project=r["project"],
                topic=r["topic"],
                activity=r["activity"],
                timestamp=r["timestamp"],
                energy=r["energy"],
            )
            for r in rows
        ]

    def timeline(self, n: int = 10, project: Optional[str] = None) -> list[CompactMemory]:
        sql = """SELECT id, title, project, topic, activity, timestamp, energy
                 FROM memories WHERE 1=1"""
        params: list = []
        if project:
            sql += " AND project = ?"
            params.append(project)
        sql += " ORDER BY timestamp DESC LIMIT ?"
        params.append(n)
        rows = self._conn.execute(sql, params).fetchall()
        return [
            CompactMemory(
                id=r["id"],
                title=r["title"],
                project=r["project"],
                topic=r["topic"],
                activity=r["activity"],
                timestamp=r["timestamp"],
                energy=r["energy"],
            )
            for r in rows
        ]

    def stats(self, dream_count: int = 0) -> dict:
        row = self._conn.execute(
            """SELECT COUNT(*) as total, COUNT(DISTINCT session_id) as sessions,
                      COALESCE(AVG(energy), 0) as avg_energy
               FROM memories"""
        ).fetchone()
        return {
            "total_memories": row["total"],
            "sessions": row["sessions"],
            "avg_energy": row["avg_energy"],
            "dream_count": dream_count,
        }

    def update_energy(self, id: str, energy: float):
        self._conn.execute("UPDATE memories SET energy = ? WHERE id = ?", (energy, id))
        self._conn.commit()

    def count(self) -> int:
        return self._conn.execute("SELECT COUNT(*) FROM memories").fetchone()[0]

    def get_structured(self, id: str) -> Optional[StructuredFields]:
        row = self._conn.execute(
            "SELECT title, summary, facts, concepts, files_touched FROM memories WHERE id = ?",
            (id,),
        ).fetchone()
        if not row:
            return None
        return StructuredFields(
            title=row["title"],
            summary=row["summary"],
            facts=json.loads(row["facts"]) if row["facts"] else [],
            concepts=json.loads(row["concepts"]) if row["concepts"] else [],
            files_touched=json.loads(row["files_touched"]) if row["files_touched"] else [],
        )

    def _row_to_record(self, row: sqlite3.Row) -> MemoryRecord:
        return MemoryRecord(
            id=row["id"],
            session_id=row["session_id"],
            timestamp=row["timestamp"],
            turn_number=row["turn_number"],
            question=row["question"],
            answer_preview=row["answer_preview"],
            document=row["document"] or "",
            project=row["project"],
            topic=row["topic"],
            activity=row["activity"],
            memory_type=row["memory_type"],
            energy=row["energy"],
            model=row["model"],
            source=row["source"],
        )
