"""
cortex/hippo/db/schema.py — SQL DDL constants for Hippo LCM tables.

Issue: #801

All tables use:
- owner_id TEXT NOT NULL DEFAULT 'default' for multi-tenant isolation
- ISO 8601 UTC timestamps as TEXT
- Foreign key enforcement
- WAL journal mode for concurrent reads
"""

HIPPO_SCHEMA_VERSION = 1

# ═══════════════════════════════════════════════════════════════════════════
# Table DDL
# ═══════════════════════════════════════════════════════════════════════════

CONVERSATIONS_DDL = """
CREATE TABLE IF NOT EXISTS hippo_conversations (
    conversation_id         INTEGER PRIMARY KEY AUTOINCREMENT,
    owner_id                TEXT NOT NULL DEFAULT 'default',
    session_id              TEXT NOT NULL,
    session_key             TEXT,
    title                   TEXT,
    bootstrapped_at         TEXT,
    compaction_in_progress  INTEGER NOT NULL DEFAULT 0,
    compaction_started_at   TEXT,
    created_at              TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
    updated_at              TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
);
"""

MESSAGES_DDL = """
CREATE TABLE IF NOT EXISTS hippo_messages (
    message_id      INTEGER PRIMARY KEY AUTOINCREMENT,
    conversation_id INTEGER NOT NULL REFERENCES hippo_conversations(conversation_id),
    owner_id        TEXT NOT NULL DEFAULT 'default',
    seq             INTEGER NOT NULL,
    role            TEXT NOT NULL CHECK(role IN ('user','assistant','system','tool')),
    content         TEXT NOT NULL DEFAULT '',
    token_count     INTEGER NOT NULL DEFAULT 0,
    metadata        TEXT,
    created_at      TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
);
"""

SUMMARIES_DDL = """
CREATE TABLE IF NOT EXISTS hippo_summaries (
    summary_id              TEXT PRIMARY KEY,
    conversation_id         INTEGER NOT NULL REFERENCES hippo_conversations(conversation_id),
    owner_id                TEXT NOT NULL DEFAULT 'default',
    kind                    TEXT NOT NULL CHECK(kind IN ('leaf','condensed')),
    depth                   INTEGER NOT NULL DEFAULT 0,
    content                 TEXT NOT NULL,
    token_count             INTEGER NOT NULL DEFAULT 0,
    file_ids                TEXT NOT NULL DEFAULT '[]',
    earliest_at             TEXT,
    latest_at               TEXT,
    descendant_count        INTEGER NOT NULL DEFAULT 0,
    descendant_token_count  INTEGER NOT NULL DEFAULT 0,
    source_message_token_count INTEGER NOT NULL DEFAULT 0,
    quality_score           REAL,
    embedding               BLOB,
    signal_metadata         TEXT,
    created_at              TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
);
"""

SUMMARY_PARENTS_DDL = """
CREATE TABLE IF NOT EXISTS hippo_summary_parents (
    summary_id        TEXT NOT NULL REFERENCES hippo_summaries(summary_id),
    parent_summary_id TEXT NOT NULL REFERENCES hippo_summaries(summary_id),
    PRIMARY KEY (summary_id, parent_summary_id)
);
"""

SUMMARY_MESSAGES_DDL = """
CREATE TABLE IF NOT EXISTS hippo_summary_messages (
    summary_id TEXT NOT NULL REFERENCES hippo_summaries(summary_id),
    message_id INTEGER NOT NULL REFERENCES hippo_messages(message_id),
    PRIMARY KEY (summary_id, message_id)
);
"""

CONTEXT_ITEMS_DDL = """
CREATE TABLE IF NOT EXISTS hippo_context_items (
    conversation_id INTEGER NOT NULL REFERENCES hippo_conversations(conversation_id),
    ordinal         INTEGER NOT NULL,
    item_type       TEXT NOT NULL CHECK(item_type IN ('message','summary')),
    message_id      INTEGER REFERENCES hippo_messages(message_id),
    summary_id      TEXT REFERENCES hippo_summaries(summary_id),
    created_at      TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
    PRIMARY KEY (conversation_id, ordinal)
);
"""

MESSAGE_PARTS_DDL = """
CREATE TABLE IF NOT EXISTS hippo_message_parts (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    message_id  INTEGER NOT NULL REFERENCES hippo_messages(message_id),
    ordinal     INTEGER NOT NULL DEFAULT 0,
    part_type   TEXT NOT NULL DEFAULT 'text',
    text_content TEXT,
    tool_name   TEXT,
    tool_call_id TEXT,
    tool_input  TEXT,
    tool_output TEXT,
    metadata    TEXT,
    created_at  TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
);
"""

LARGE_FILES_DDL = """
CREATE TABLE IF NOT EXISTS hippo_large_files (
    id                  INTEGER PRIMARY KEY AUTOINCREMENT,
    file_id             TEXT NOT NULL UNIQUE,
    conversation_id     INTEGER NOT NULL REFERENCES hippo_conversations(conversation_id),
    owner_id            TEXT NOT NULL DEFAULT 'default',
    file_name           TEXT,
    mime_type           TEXT,
    byte_size           INTEGER NOT NULL DEFAULT 0,
    content             TEXT,
    exploration_summary TEXT,
    created_at          TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
);
"""

SIGNALS_DDL = """
CREATE TABLE IF NOT EXISTS hippo_signals (
    signal_id       INTEGER PRIMARY KEY AUTOINCREMENT,
    conversation_id INTEGER NOT NULL REFERENCES hippo_conversations(conversation_id),
    owner_id        TEXT NOT NULL DEFAULT 'default',
    summary_id      TEXT REFERENCES hippo_summaries(summary_id),
    message_id      INTEGER REFERENCES hippo_messages(message_id),
    signal_type     TEXT NOT NULL,
    attributes      TEXT NOT NULL DEFAULT '{}',
    created_at      TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
);
"""

# ═══════════════════════════════════════════════════════════════════════════
# Index DDL
# ═══════════════════════════════════════════════════════════════════════════

INDEXES_DDL = """
CREATE INDEX IF NOT EXISTS idx_hippo_conversations_owner_session
    ON hippo_conversations(owner_id, session_id);
CREATE INDEX IF NOT EXISTS idx_hippo_conversations_owner
    ON hippo_conversations(owner_id);
CREATE INDEX IF NOT EXISTS idx_hippo_conversations_owner_session_key
    ON hippo_conversations(owner_id, session_key);

CREATE INDEX IF NOT EXISTS idx_hippo_messages_conversation_seq
    ON hippo_messages(conversation_id, seq);
CREATE INDEX IF NOT EXISTS idx_hippo_messages_owner
    ON hippo_messages(owner_id);

CREATE INDEX IF NOT EXISTS idx_hippo_summaries_conversation_depth
    ON hippo_summaries(conversation_id, depth);
CREATE INDEX IF NOT EXISTS idx_hippo_summaries_owner
    ON hippo_summaries(owner_id);
CREATE INDEX IF NOT EXISTS idx_hippo_summaries_quality
    ON hippo_summaries(quality_score) WHERE quality_score IS NOT NULL;

CREATE INDEX IF NOT EXISTS idx_hippo_summary_parents_parent
    ON hippo_summary_parents(parent_summary_id);

CREATE INDEX IF NOT EXISTS idx_hippo_summary_messages_message
    ON hippo_summary_messages(message_id);

CREATE INDEX IF NOT EXISTS idx_hippo_signals_conversation
    ON hippo_signals(conversation_id);
CREATE INDEX IF NOT EXISTS idx_hippo_signals_owner
    ON hippo_signals(owner_id);

CREATE INDEX IF NOT EXISTS idx_hippo_message_parts_message_id
    ON hippo_message_parts(message_id);

CREATE INDEX IF NOT EXISTS idx_hippo_large_files_conversation
    ON hippo_large_files(conversation_id);
CREATE INDEX IF NOT EXISTS idx_hippo_large_files_file_id
    ON hippo_large_files(file_id);

CREATE INDEX IF NOT EXISTS idx_hippo_messages_created
    ON hippo_messages(created_at);
CREATE INDEX IF NOT EXISTS idx_hippo_messages_conv_created
    ON hippo_messages(conversation_id, created_at);
CREATE INDEX IF NOT EXISTS idx_hippo_summaries_created
    ON hippo_summaries(created_at);
CREATE INDEX IF NOT EXISTS idx_hippo_summaries_conv_created
    ON hippo_summaries(conversation_id, created_at);
"""

# ═══════════════════════════════════════════════════════════════════════════
# FTS5 Virtual Tables
# ═══════════════════════════════════════════════════════════════════════════

FTS_DDL = """
CREATE VIRTUAL TABLE IF NOT EXISTS hippo_fts_messages USING fts5(
    content,
    content='hippo_messages',
    content_rowid='message_id'
);

CREATE VIRTUAL TABLE IF NOT EXISTS hippo_fts_summaries USING fts5(
    content,
    content='hippo_summaries',
    content_rowid='rowid'
);
"""

FTS_TRIGGERS_DDL = """
CREATE TRIGGER IF NOT EXISTS hippo_messages_ai AFTER INSERT ON hippo_messages BEGIN
    INSERT INTO hippo_fts_messages(rowid, content) VALUES (new.message_id, new.content);
END;

CREATE TRIGGER IF NOT EXISTS hippo_messages_ad AFTER DELETE ON hippo_messages BEGIN
    INSERT INTO hippo_fts_messages(hippo_fts_messages, rowid, content)
        VALUES ('delete', old.message_id, old.content);
END;

CREATE TRIGGER IF NOT EXISTS hippo_summaries_ai AFTER INSERT ON hippo_summaries BEGIN
    INSERT INTO hippo_fts_summaries(rowid, content) VALUES (new.rowid, new.content);
END;

CREATE TRIGGER IF NOT EXISTS hippo_summaries_ad AFTER DELETE ON hippo_summaries BEGIN
    INSERT INTO hippo_fts_summaries(hippo_fts_summaries, rowid, content)
        VALUES ('delete', old.rowid, old.content);
END;
"""

# ═══════════════════════════════════════════════════════════════════════════
# sqlite-vec Virtual Table (ANN vector search)
# ═══════════════════════════════════════════════════════════════════════════

# Template: dimension is filled in dynamically from config.embedding_dim.
# owner_id is a partition key for efficient per-tenant ANN queries.
# distance_metric=cosine matches Voyage embedding space.
VEC_DDL_TEMPLATE = """
CREATE VIRTUAL TABLE IF NOT EXISTS hippo_vec_summaries USING vec0(
    summary_id TEXT PRIMARY KEY,
    owner_id TEXT partition key,
    embedding float[{dim}] distance_metric=cosine
);
"""


def get_vec_ddl(embedding_dim: int = 1024) -> str:
    """Return the vec0 virtual table DDL for the given embedding dimension."""
    return VEC_DDL_TEMPLATE.format(dim=embedding_dim)


# ── Config table for hippo-specific KV ─────────────────────────────────

HIPPO_CONFIG_DDL = """
CREATE TABLE IF NOT EXISTS hippo_config (
    key       TEXT PRIMARY KEY,
    value     TEXT,
    updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
);
"""

# ═══════════════════════════════════════════════════════════════════════════
# Aggregated DDL for full schema creation
# ═══════════════════════════════════════════════════════════════════════════

ALL_DDL = [
    CONVERSATIONS_DDL,
    MESSAGES_DDL,
    SUMMARIES_DDL,
    SUMMARY_PARENTS_DDL,
    SUMMARY_MESSAGES_DDL,
    CONTEXT_ITEMS_DDL,
    MESSAGE_PARTS_DDL,
    LARGE_FILES_DDL,
    SIGNALS_DDL,
    INDEXES_DDL,
    FTS_DDL,
    FTS_TRIGGERS_DDL,
    HIPPO_CONFIG_DDL,
]


def get_full_schema_sql() -> str:
    """Return the complete Hippo schema as a single SQL string."""
    pragma = "PRAGMA journal_mode = WAL;\nPRAGMA foreign_keys = ON;\n\n"
    return pragma + "\n".join(ALL_DDL)
