-- NEUROVAULT Knowledge Graph Schema
-- Includes a lightweight local CHRONOS-compatible memory table for standalone mode.
PRAGMA foreign_keys = ON;

CREATE TABLE IF NOT EXISTS memories (
    id              BLOB PRIMARY KEY,
    persona_id      BLOB NOT NULL,
    content         TEXT NOT NULL,
    source          TEXT NOT NULL,
    importance      REAL NOT NULL DEFAULT 0.5,
    metadata        TEXT NOT NULL DEFAULT '{}',
    embedding       BLOB,
    content_hash    TEXT DEFAULT '',
    device_origin   TEXT DEFAULT 'local',
    created_at      INTEGER NOT NULL,
    is_active       INTEGER NOT NULL DEFAULT 1
);

CREATE INDEX IF NOT EXISTS idx_memories_persona ON memories(persona_id);
CREATE INDEX IF NOT EXISTS idx_memories_created_at ON memories(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(is_active);
CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash);

CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
    content,
    content='memories',
    content_rowid='rowid'
);

CREATE TRIGGER IF NOT EXISTS memories_ai_fts AFTER INSERT ON memories BEGIN
    INSERT INTO memories_fts(rowid, content)
    VALUES (new.rowid, new.content);
END;

CREATE TRIGGER IF NOT EXISTS memories_ad_fts AFTER DELETE ON memories BEGIN
    INSERT INTO memories_fts(memories_fts, rowid, content)
    VALUES ('delete', old.rowid, old.content);
END;

CREATE TRIGGER IF NOT EXISTS memories_au_fts AFTER UPDATE ON memories BEGIN
    INSERT INTO memories_fts(memories_fts, rowid, content)
    VALUES ('delete', old.rowid, old.content);
    INSERT INTO memories_fts(rowid, content)
    VALUES (new.rowid, new.content);
END;

CREATE TABLE IF NOT EXISTS checkpoints (
    id              BLOB PRIMARY KEY,
    message         TEXT NOT NULL,
    created_at      INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS entities (
    id              BLOB PRIMARY KEY,
    name            TEXT NOT NULL,
    canonical_name  TEXT NOT NULL,
    entity_type     TEXT NOT NULL,
    description     TEXT DEFAULT '',
    embedding       BLOB,
    mention_count   INTEGER DEFAULT 1,
    first_seen      INTEGER NOT NULL,
    last_seen       INTEGER NOT NULL,
    importance      REAL DEFAULT 0.5,
    metadata        TEXT DEFAULT '{}',
    UNIQUE(canonical_name, entity_type)
);

CREATE TABLE IF NOT EXISTS relationships (
    id              BLOB PRIMARY KEY,
    source_entity_id BLOB NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
    target_entity_id BLOB NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
    relation_type   TEXT NOT NULL,
    strength        REAL DEFAULT 0.5,
    evidence_count  INTEGER DEFAULT 1,
    first_seen      INTEGER NOT NULL,
    last_seen       INTEGER NOT NULL,
    metadata        TEXT DEFAULT '{}',
    UNIQUE(source_entity_id, target_entity_id, relation_type)
);

CREATE TABLE IF NOT EXISTS memory_entities (
    memory_id       BLOB NOT NULL,
    entity_id       BLOB NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
    mention_offset  INTEGER,
    confidence      REAL DEFAULT 0.8,
    PRIMARY KEY (memory_id, entity_id)
);

CREATE TABLE IF NOT EXISTS clusters (
    id              BLOB PRIMARY KEY,
    centroid_embedding BLOB,
    insight_id      BLOB,
    memory_count    INTEGER DEFAULT 0,
    created_at      INTEGER NOT NULL,
    updated_at      INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS cluster_memories (
    cluster_id      BLOB NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
    memory_id       BLOB NOT NULL,
    PRIMARY KEY (cluster_id, memory_id)
);

CREATE TABLE IF NOT EXISTS processing_log (
    memory_id       BLOB NOT NULL,
    phase           TEXT NOT NULL,
    processed_at    INTEGER NOT NULL,
    PRIMARY KEY (memory_id, phase)
);

CREATE TABLE IF NOT EXISTS concept_hierarchy (
    id               BLOB PRIMARY KEY,
    child_entity_id  BLOB NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
    parent_entity_id BLOB NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
    relation_type    TEXT NOT NULL,
    confidence       REAL DEFAULT 0.8,
    source           TEXT DEFAULT 'inferred',
    created_at       INTEGER NOT NULL,
    UNIQUE(child_entity_id, parent_entity_id, relation_type)
);

CREATE TABLE IF NOT EXISTS concept_properties (
    id               BLOB PRIMARY KEY,
    entity_id        BLOB NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
    property_key     TEXT NOT NULL,
    property_value   TEXT NOT NULL,
    confidence       REAL DEFAULT 0.7,
    source_memory_id BLOB,
    created_at       INTEGER NOT NULL,
    UNIQUE(entity_id, property_key)
);

CREATE TABLE IF NOT EXISTS plugin_state (
    plugin_id    TEXT PRIMARY KEY,
    enabled      INTEGER DEFAULT 1,
    config       TEXT DEFAULT '{}',
    last_poll    INTEGER DEFAULT 0,
    error_count  INTEGER DEFAULT 0,
    installed_at INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS hook_registry (
    id             INTEGER PRIMARY KEY AUTOINCREMENT,
    event          TEXT NOT NULL,
    handler_module TEXT NOT NULL,
    handler_func   TEXT NOT NULL,
    enabled        INTEGER DEFAULT 1,
    created_at     INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS sync_state (
    peer_id         TEXT PRIMARY KEY,
    last_sync_seq   INTEGER DEFAULT 0,
    last_sync_at    INTEGER DEFAULT 0,
    peer_public_key TEXT
);

CREATE TABLE IF NOT EXISTS sync_conflicts (
    id              INTEGER PRIMARY KEY AUTOINCREMENT,
    local_data      TEXT NOT NULL,
    remote_data     TEXT NOT NULL,
    conflict_type   TEXT NOT NULL,
    created_at      INTEGER NOT NULL,
    resolved        INTEGER DEFAULT 0,
    resolution      TEXT DEFAULT ''
);

CREATE TABLE IF NOT EXISTS users (
    username     TEXT PRIMARY KEY,
    role         TEXT NOT NULL DEFAULT 'reader',
    token_hash   TEXT UNIQUE NOT NULL,
    created_by   TEXT DEFAULT 'system',
    created_at   INTEGER NOT NULL,
    active       INTEGER DEFAULT 1
);

CREATE TABLE IF NOT EXISTS device_tokens (
    id           INTEGER PRIMARY KEY AUTOINCREMENT,
    device_name  TEXT NOT NULL,
    token_hash   TEXT UNIQUE NOT NULL,
    permissions  TEXT DEFAULT '["read"]',
    created_at   INTEGER NOT NULL,
    active       INTEGER DEFAULT 1
);

CREATE TABLE IF NOT EXISTS shared_spaces (
    id           BLOB PRIMARY KEY,
    name         TEXT UNIQUE NOT NULL,
    description  TEXT DEFAULT '',
    owner        TEXT NOT NULL,
    created_at   INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS space_members (
    space_id     BLOB NOT NULL REFERENCES shared_spaces(id),
    username     TEXT NOT NULL REFERENCES users(username),
    role         TEXT NOT NULL DEFAULT 'reader',
    joined_at    INTEGER NOT NULL,
    PRIMARY KEY (space_id, username)
);

CREATE TABLE IF NOT EXISTS audit_log (
    id           INTEGER PRIMARY KEY AUTOINCREMENT,
    timestamp    INTEGER NOT NULL,
    username     TEXT NOT NULL,
    action       TEXT NOT NULL,
    resource     TEXT NOT NULL,
    success      INTEGER DEFAULT 1,
    details      TEXT DEFAULT '{}'
);

-- Indices
CREATE INDEX IF NOT EXISTS idx_entities_type ON entities(entity_type);
CREATE INDEX IF NOT EXISTS idx_entities_canonical ON entities(canonical_name);
CREATE INDEX IF NOT EXISTS idx_entities_importance ON entities(importance DESC);
CREATE INDEX IF NOT EXISTS idx_rel_source ON relationships(source_entity_id);
CREATE INDEX IF NOT EXISTS idx_rel_target ON relationships(target_entity_id);
CREATE INDEX IF NOT EXISTS idx_rel_type ON relationships(relation_type);
CREATE INDEX IF NOT EXISTS idx_me_memory ON memory_entities(memory_id);
CREATE INDEX IF NOT EXISTS idx_me_entity ON memory_entities(entity_id);
CREATE INDEX IF NOT EXISTS idx_proc_phase ON processing_log(phase);
CREATE INDEX IF NOT EXISTS idx_ch_child ON concept_hierarchy(child_entity_id);
CREATE INDEX IF NOT EXISTS idx_ch_parent ON concept_hierarchy(parent_entity_id);
CREATE INDEX IF NOT EXISTS idx_cp_entity ON concept_properties(entity_id);

CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
    name, description, content='entities', content_rowid='rowid'
);

CREATE TRIGGER IF NOT EXISTS entities_ai_fts AFTER INSERT ON entities BEGIN
    INSERT INTO entities_fts(rowid, name, description)
    VALUES (new.rowid, new.name, new.description);
END;
