"""
Database schema initialization for Lattice.

This module implements schema creation for:
- store.db: Project-level episodic logs
- global.db: Cross-project evidence summaries

All functions are Shell layer — they perform I/O and return Result[T, E].

Reference: RFC-002 §5.3 Schema
"""

from __future__ import annotations

import sqlite3
from pathlib import Path
from typing import TYPE_CHECKING

from deal import pre, post
from returns.result import Failure, Result, Success

if TYPE_CHECKING:
    pass  # No typing imports needed

# SQL statements for store.db (project-level)
STORE_DB_SCHEMA = """
-- Episodic Logs (legacy, kept for migration)
CREATE TABLE IF NOT EXISTS logs (
    id INTEGER PRIMARY KEY,
    external_id TEXT UNIQUE NOT NULL,
    session_id TEXT NOT NULL,
    timestamp TEXT NOT NULL,
    role TEXT NOT NULL,
    content TEXT NOT NULL,
    metadata TEXT
);

CREATE INDEX IF NOT EXISTS idx_logs_session ON logs(session_id);
CREATE INDEX IF NOT EXISTS idx_logs_timestamp ON logs(timestamp);

-- Events (new schema for session compression)
CREATE TABLE IF NOT EXISTS events (
    id INTEGER PRIMARY KEY,
    external_id TEXT UNIQUE NOT NULL,
    session_id TEXT NOT NULL,
    timestamp TEXT NOT NULL,
    type TEXT NOT NULL,
    content TEXT NOT NULL,
    tool_input TEXT,
    tool_status TEXT,
    tool_error TEXT,
    message_id TEXT
);

CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);
CREATE INDEX IF NOT EXISTS idx_events_type ON events(type);
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
CREATE INDEX IF NOT EXISTS idx_events_message_id ON events(message_id);

-- Evolution State
CREATE TABLE IF NOT EXISTS metadata (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL
);

-- Full-Text Search Index for logs (legacy)
CREATE VIRTUAL TABLE IF NOT EXISTS logs_fts USING fts5(
    content,
    content=logs,
    content_rowid=id
);

-- Full-Text Search Index for events
CREATE VIRTUAL TABLE IF NOT EXISTS events_fts USING fts5(
    content,
    content=events,
    content_rowid=id
);

-- FTS Sync Triggers for logs (legacy)
CREATE TRIGGER IF NOT EXISTS logs_ai AFTER INSERT ON logs BEGIN
    INSERT INTO logs_fts(rowid, content) VALUES (new.id, new.content);
END;

CREATE TRIGGER IF NOT EXISTS logs_ad AFTER DELETE ON logs BEGIN
    INSERT INTO logs_fts(logs_fts, rowid, content) VALUES ('delete', old.id, old.content);
END;

CREATE TRIGGER IF NOT EXISTS logs_au AFTER UPDATE ON logs BEGIN
    INSERT INTO logs_fts(logs_fts, rowid, content) VALUES ('delete', old.id, old.content);
    INSERT INTO logs_fts(rowid, content) VALUES (new.id, new.content);
END;

-- FTS Sync Triggers for events
CREATE TRIGGER IF NOT EXISTS events_ai AFTER INSERT ON events BEGIN
    INSERT INTO events_fts(rowid, content) VALUES (new.id, new.content);
END;

CREATE TRIGGER IF NOT EXISTS events_ad AFTER DELETE ON events BEGIN
    INSERT INTO events_fts(events_fts, rowid, content) VALUES ('delete', old.id, old.content);
END;

CREATE TRIGGER IF NOT EXISTS events_au AFTER UPDATE ON events BEGIN
    INSERT INTO events_fts(events_fts, rowid, content) VALUES ('delete', old.id, old.content);
    INSERT INTO events_fts(rowid, content) VALUES (new.id, new.content);
END;
"""

# SQL statements for logs_vec (sqlite-vec extension)
LOGS_VEC_CREATE = """
CREATE VIRTUAL TABLE IF NOT EXISTS logs_vec USING vec0(embedding float[768]);
"""

# SQL statements for events_vec (sqlite-vec extension)
EVENTS_VEC_CREATE = """
CREATE VIRTUAL TABLE IF NOT EXISTS events_vec USING vec0(embedding float[768]);
"""

# SQL statements for global.db
GLOBAL_DB_SCHEMA = """
-- Cross-project evidence summaries
CREATE TABLE IF NOT EXISTS evidence (
    id INTEGER PRIMARY KEY,
    source_project TEXT NOT NULL,
    source_session_id TEXT NOT NULL,
    source_rule_file TEXT,
    pattern TEXT NOT NULL,
    summary TEXT NOT NULL,
    original_snippet TEXT,
    extracted_at TEXT NOT NULL,
    compiler_trace TEXT,
    confidence REAL DEFAULT 0.0
);

-- Links global rules to their supporting evidence
CREATE TABLE IF NOT EXISTS rule_evidence (
    rule_file TEXT NOT NULL,
    evidence_id TEXT NOT NULL,
    PRIMARY KEY (rule_file, evidence_id)
);

-- Full-Text Search Index
CREATE VIRTUAL TABLE IF NOT EXISTS evidence_fts USING fts5(
    pattern, summary, original_snippet,
    content=evidence, content_rowid=id
);

-- FTS Sync Triggers
CREATE TRIGGER IF NOT EXISTS evidence_ai AFTER INSERT ON evidence BEGIN
    INSERT INTO evidence_fts(rowid, pattern, summary, original_snippet)
    VALUES (new.id, new.pattern, new.summary, new.original_snippet);
END;

CREATE TRIGGER IF NOT EXISTS evidence_ad AFTER DELETE ON evidence BEGIN
    INSERT INTO evidence_fts(evidence_fts, rowid, pattern, summary, original_snippet)
    VALUES ('delete', old.id, old.pattern, old.summary, old.original_snippet);
END;

CREATE TRIGGER IF NOT EXISTS evidence_au AFTER UPDATE ON evidence BEGIN
    INSERT INTO evidence_fts(evidence_fts, rowid, pattern, summary, original_snippet)
    VALUES ('delete', old.id, old.pattern, old.summary, old.original_snippet);
    INSERT INTO evidence_fts(rowid, pattern, summary, original_snippet)
    VALUES (new.id, new.pattern, new.summary, new.original_snippet);
END;
"""

# SQL statements for evidence_vec (sqlite-vec extension)
EVIDENCE_VEC_CREATE = """
CREATE VIRTUAL TABLE IF NOT EXISTS evidence_vec USING vec0(embedding float[768]);
"""


def create_store(path: Path) -> Result[sqlite3.Connection, str]:
    """Create or open store.db with full schema.

    Creates all tables, indexes, FTS5 virtual tables, and triggers.
    Loads sqlite-vec extension for vector search.

    Args:
        path: Path to store.db file (parent directories will be created).

    Returns:
        Success with Connection if successful, Failure with error message otherwise.

    Example:
        >>> from pathlib import Path
        >>> import tempfile
        >>> with tempfile.TemporaryDirectory() as tmpdir:
        ...     result = create_store(Path(tmpdir) / "store.db")
        ...     isinstance(result, Success)
        True
    """
    try:
        # Ensure parent directory exists
        path.parent.mkdir(parents=True, exist_ok=True)

        # Connect to database
        conn = sqlite3.connect(str(path))
        conn.row_factory = sqlite3.Row

        # Load sqlite-vec extension
        try:
            import sqlite_vec

            conn.enable_load_extension(True)
            sqlite_vec.load(conn)
            conn.enable_load_extension(False)
        except ImportError:
            conn.close()
            return Failure("sqlite-vec extension not available")

        # Create main schema
        conn.executescript(STORE_DB_SCHEMA)

        # Create vector tables (separate due to extension dependency)
        conn.execute(LOGS_VEC_CREATE)
        conn.execute(EVENTS_VEC_CREATE)

        # Run migrations for existing databases
        add_message_id_column(conn)

        conn.commit()
        return Success(conn)

    except sqlite3.Error as e:
        return Failure(f"Database error: {e}")
    except Exception as e:
        return Failure(f"Unexpected error: {e}")


def create_global_store(path: Path) -> Result[sqlite3.Connection, str]:
    """Create or open global.db with full schema.

    Creates all tables, indexes, FTS5 virtual tables, and triggers.
    Loads sqlite-vec extension for vector search.

    Args:
        path: Path to global.db file (parent directories will be created).

    Returns:
        Success with Connection if successful, Failure with error message otherwise.

    Example:
        >>> from pathlib import Path
        >>> import tempfile
        >>> with tempfile.TemporaryDirectory() as tmpdir:
        ...     result = create_global_store(Path(tmpdir) / "global.db")
        ...     isinstance(result, Success)
        True
    """
    try:
        # Ensure parent directory exists
        path.parent.mkdir(parents=True, exist_ok=True)

        # Connect to database
        conn = sqlite3.connect(str(path))
        conn.row_factory = sqlite3.Row

        # Load sqlite-vec extension
        try:
            import sqlite_vec

            conn.enable_load_extension(True)
            sqlite_vec.load(conn)
            conn.enable_load_extension(False)
        except ImportError:
            conn.close()
            return Failure("sqlite-vec extension not available")

        # Create main schema
        conn.executescript(GLOBAL_DB_SCHEMA)

        # Create vector table (separate due to extension dependency)
        conn.execute(EVIDENCE_VEC_CREATE)

        conn.commit()
        return Success(conn)

    except sqlite3.Error as e:
        return Failure(f"Database error: {e}")
    except Exception as e:
        return Failure(f"Unexpected error: {e}")


@pre(lambda conn: isinstance(conn, sqlite3.Connection))
@post(lambda result: isinstance(result, (Success, Failure)))
def add_message_id_column(conn: sqlite3.Connection) -> Result[None, str]:
    """Add message_id column to events table if not exists.

    This is an idempotent migration - safe to call multiple times.

    Creates:
        - message_id TEXT column on events table (nullable)
        - idx_events_message_id index on message_id

    Args:
        conn: Active SQLite connection to store.db.

    Returns:
        Success(None) if migration succeeded or column already exists.
        Failure with error message if migration failed.

    Contract:
        >>> # Pre-condition: conn must be a sqlite3.Connection
        >>> import sqlite3
        >>> isinstance(sqlite3.Connection, type)
        True
        >>> # Post-condition: result is Success or Failure
        >>> from returns.result import Success, Failure
        >>> isinstance(Success(None), (Success, Failure))
        True
        >>> isinstance(Failure("error"), (Success, Failure))
        True
    """
    try:
        cursor = conn.cursor()

        # Check if message_id column already exists
        cursor.execute(
            "SELECT name FROM pragma_table_info('events') WHERE name = ?",
            ("message_id",),
        )
        column_exists = cursor.fetchone() is not None

        # Add column if not exists
        if not column_exists:
            cursor.execute("ALTER TABLE events ADD COLUMN message_id TEXT")

        # Create index if not exists (idempotent)
        cursor.execute(
            "CREATE INDEX IF NOT EXISTS idx_events_message_id ON events(message_id)"
        )

        conn.commit()
        return Success(None)

    except sqlite3.Error as e:
        return Failure(str(e))
