CREATE TABLE IF NOT EXISTS "entity_changes" (
                                                `id`	INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
                                                `entityName`	TEXT NOT NULL,
                                                `entityId`	TEXT NOT NULL,
                                                `hash`	TEXT NOT NULL,
                                                `isErased` INT NOT NULL,
                                                `changeId` TEXT NOT NULL,
                                                `componentId` TEXT NOT NULL,
                                                `instanceId` TEXT NOT NULL,
                                                `isSynced` INTEGER NOT NULL,
                                                `utcDateChanged` TEXT NOT NULL
                                                );
CREATE TABLE IF NOT EXISTS "etapi_tokens"
(
    etapiTokenId TEXT PRIMARY KEY NOT NULL,
    name TEXT NOT NULL,
    tokenHash TEXT NOT NULL,
    utcDateCreated TEXT NOT NULL,
    utcDateModified TEXT NOT NULL,
    isDeleted INT NOT NULL DEFAULT 0);
CREATE TABLE IF NOT EXISTS "branches" (
                                          `branchId`	TEXT NOT NULL,
                                          `noteId`	TEXT NOT NULL,
                                          `parentNoteId`	TEXT NOT NULL,
                                          `notePosition`	INTEGER NOT NULL,
                                          `prefix`	TEXT,
                                          `isExpanded`	INTEGER NOT NULL DEFAULT 0,
                                          `isDeleted`	INTEGER NOT NULL DEFAULT 0,
                                          `deleteId`    TEXT DEFAULT NULL,
                                          `utcDateModified`	TEXT NOT NULL,
                                          PRIMARY KEY(`branchId`));
CREATE TABLE IF NOT EXISTS "notes" (
                                       `noteId`	TEXT NOT NULL,
                                       `title`	TEXT NOT NULL DEFAULT "note",
                                       `isProtected`	INT NOT NULL DEFAULT 0,
                                       `type` TEXT NOT NULL DEFAULT 'text',
                                       `mime` TEXT NOT NULL DEFAULT 'text/html',
                                       blobId TEXT DEFAULT NULL,
                                       `isDeleted`	INT NOT NULL DEFAULT 0,
                                       `deleteId`   TEXT DEFAULT NULL,
                                       `dateCreated`	TEXT NOT NULL,
                                       `dateModified`	TEXT NOT NULL,
                                       `utcDateCreated`	TEXT NOT NULL,
                                       `utcDateModified`	TEXT NOT NULL,
                                       PRIMARY KEY(`noteId`));
CREATE TABLE IF NOT EXISTS "revisions" (`revisionId`	TEXT NOT NULL PRIMARY KEY,
                                             `noteId`	TEXT NOT NULL,
                                             type TEXT DEFAULT '' NOT NULL,
                                             mime TEXT DEFAULT '' NOT NULL,
                                             `title`	TEXT NOT NULL,
                                             `isProtected`	INT NOT NULL DEFAULT 0,
                                            blobId TEXT DEFAULT NULL,
                                             `utcDateLastEdited` TEXT NOT NULL,
                                             `utcDateCreated` TEXT NOT NULL,
                                             `utcDateModified` TEXT NOT NULL,
                                             `dateLastEdited` TEXT NOT NULL,
                                             `dateCreated` TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS "options"
(
    name TEXT not null PRIMARY KEY,
    value TEXT not null,
    isSynced INTEGER default 0 not null,
    utcDateModified TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS "attributes"
(
    attributeId      TEXT not null primary key,
    noteId       TEXT not null,
    type         TEXT not null,
    name         TEXT not null,
    value        TEXT default '' not null,
    position     INT  default 0 not null,
    utcDateModified TEXT not null,
    isDeleted    INT  not null,
    `deleteId`    TEXT DEFAULT NULL,
    isInheritable int DEFAULT 0 NULL);
CREATE UNIQUE INDEX `IDX_entityChanges_entityName_entityId` ON "entity_changes" (
                                                                                 `entityName`,
                                                                                 `entityId`
    );
CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (`noteId`,`parentNoteId`);
CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId);
CREATE INDEX `IDX_notes_title` ON `notes` (`title`);
CREATE INDEX `IDX_notes_type` ON `notes` (`type`);
CREATE INDEX `IDX_notes_dateCreated` ON `notes` (`dateCreated`);
CREATE INDEX `IDX_notes_dateModified` ON `notes` (`dateModified`);
CREATE INDEX `IDX_notes_utcDateModified` ON `notes` (`utcDateModified`);
CREATE INDEX `IDX_notes_utcDateCreated` ON `notes` (`utcDateCreated`);
CREATE INDEX `IDX_revisions_noteId` ON `revisions` (`noteId`);
CREATE INDEX `IDX_revisions_utcDateCreated` ON `revisions` (`utcDateCreated`);
CREATE INDEX `IDX_revisions_utcDateLastEdited` ON `revisions` (`utcDateLastEdited`);
CREATE INDEX `IDX_revisions_dateCreated` ON `revisions` (`dateCreated`);
CREATE INDEX `IDX_revisions_dateLastEdited` ON `revisions` (`dateLastEdited`);
CREATE INDEX `IDX_entity_changes_changeId` ON `entity_changes` (`changeId`);
CREATE INDEX IDX_attributes_name_value
    on attributes (name, value);
CREATE INDEX IDX_attributes_noteId_index
    on attributes (noteId);
CREATE INDEX IDX_attributes_value_index
    on attributes (value);
CREATE TABLE IF NOT EXISTS "recent_notes"
(
    noteId TEXT not null primary key,
    notePath TEXT not null,
    utcDateCreated TEXT not null
);
CREATE TABLE IF NOT EXISTS "blobs" (
                                               `blobId`	TEXT NOT NULL,
                                               `content`	TEXT NULL DEFAULT NULL,
                                               `dateModified` TEXT NOT NULL,
                                               `utcDateModified` TEXT NOT NULL,
                                               PRIMARY KEY(`blobId`)
);
CREATE TABLE IF NOT EXISTS "attachments"
(
    attachmentId      TEXT not null primary key,
    ownerId       TEXT not null,
    role         TEXT not null,
    mime         TEXT not null,
    title         TEXT not null,
    isProtected    INT  not null DEFAULT 0,
    position     INT  default 0 not null,
    blobId    TEXT DEFAULT null,
    dateModified TEXT NOT NULL,
    utcDateModified TEXT not null,
    utcDateScheduledForErasureSince TEXT DEFAULT NULL,
    isDeleted    INT  not null,
    deleteId    TEXT DEFAULT NULL);
CREATE TABLE IF NOT EXISTS "user_data"
(
    tmpID INT,
    username TEXT,
    email TEXT,
    userIDEncryptedDataKey TEXT,
    userIDVerificationHash TEXT,
    salt TEXT,
    derivedKey TEXT,
    isSetup TEXT DEFAULT "false",
    UNIQUE (tmpID),
    PRIMARY KEY (tmpID)
);
CREATE INDEX IDX_attachments_ownerId_role
    on attachments (ownerId, role);

CREATE INDEX IDX_notes_blobId on notes (blobId);
CREATE INDEX IDX_revisions_blobId on revisions (blobId);
CREATE INDEX IDX_attachments_blobId on attachments (blobId);

-- Strategic Performance Indexes from migration 234
-- NOTES TABLE INDEXES
CREATE INDEX IDX_notes_search_composite 
ON notes (isDeleted, type, mime, dateModified DESC);

CREATE INDEX IDX_notes_metadata_covering 
ON notes (noteId, isDeleted, type, mime, title, dateModified, isProtected);

CREATE INDEX IDX_notes_protected_deleted 
ON notes (isProtected, isDeleted) 
WHERE isProtected = 1;

-- BRANCHES TABLE INDEXES  
CREATE INDEX IDX_branches_tree_traversal 
ON branches (parentNoteId, isDeleted, notePosition);

CREATE INDEX IDX_branches_covering 
ON branches (noteId, parentNoteId, isDeleted, notePosition, prefix);

CREATE INDEX IDX_branches_note_parents 
ON branches (noteId, isDeleted) 
WHERE isDeleted = 0;

-- ATTRIBUTES TABLE INDEXES
CREATE INDEX IDX_attributes_search_composite 
ON attributes (name, value, isDeleted);

CREATE INDEX IDX_attributes_covering 
ON attributes (noteId, name, value, type, isDeleted, position);

CREATE INDEX IDX_attributes_inheritable 
ON attributes (isInheritable, isDeleted) 
WHERE isInheritable = 1 AND isDeleted = 0;

CREATE INDEX IDX_attributes_labels 
ON attributes (type, name, value) 
WHERE type = 'label' AND isDeleted = 0;

CREATE INDEX IDX_attributes_relations 
ON attributes (type, name, value) 
WHERE type = 'relation' AND isDeleted = 0;

-- BLOBS TABLE INDEXES
CREATE INDEX IDX_blobs_content_size 
ON blobs (blobId, LENGTH(content));

-- ATTACHMENTS TABLE INDEXES
CREATE INDEX IDX_attachments_composite 
ON attachments (ownerId, role, isDeleted, position);

-- REVISIONS TABLE INDEXES
CREATE INDEX IDX_revisions_note_date 
ON revisions (noteId, utcDateCreated DESC);

-- ENTITY_CHANGES TABLE INDEXES
CREATE INDEX IDX_entity_changes_sync 
ON entity_changes (isSynced, utcDateChanged);

CREATE INDEX IDX_entity_changes_component 
ON entity_changes (componentId, utcDateChanged DESC);

-- RECENT_NOTES TABLE INDEXES
CREATE INDEX IDX_recent_notes_date 
ON recent_notes (utcDateCreated DESC);


CREATE TABLE IF NOT EXISTS sessions (
    id TEXT PRIMARY KEY,
    data TEXT,
    expires INTEGER
);

-- FTS5 Full-Text Search Support
-- Create FTS5 virtual table with porter stemming for word-based searches
CREATE VIRTUAL TABLE notes_fts USING fts5(
    noteId UNINDEXED,
    title,
    content,
    tokenize = 'porter unicode61'
);

-- Create FTS5 virtual table with trigram tokenizer for substring searches
CREATE VIRTUAL TABLE notes_fts_trigram USING fts5(
    noteId UNINDEXED,
    title,
    content,
    tokenize = 'trigram',
    detail = 'none'
);

-- Triggers to keep FTS table synchronized with notes
-- IMPORTANT: These triggers must handle all SQL operations including:
-- - Regular INSERT/UPDATE/DELETE
-- - INSERT OR REPLACE
-- - INSERT ... ON CONFLICT ... DO UPDATE (upsert)
-- - Cases where notes are created before blobs (import scenarios)

-- Trigger for INSERT operations on notes
-- Handles: INSERT, INSERT OR REPLACE, INSERT OR IGNORE, and the INSERT part of upsert
CREATE TRIGGER notes_fts_insert 
AFTER INSERT ON notes
WHEN NEW.type IN ('text', 'code', 'mermaid', 'canvas', 'mindMap') 
    AND NEW.isDeleted = 0
    AND NEW.isProtected = 0
BEGIN
    -- First delete any existing FTS entries (in case of INSERT OR REPLACE)
    DELETE FROM notes_fts WHERE noteId = NEW.noteId;
    DELETE FROM notes_fts_trigram WHERE noteId = NEW.noteId;
    
    -- Then insert the new entry into both FTS tables
    INSERT INTO notes_fts (noteId, title, content)
    SELECT 
        NEW.noteId,
        NEW.title,
        COALESCE(b.content, '')  -- Use empty string if blob doesn't exist yet
    FROM (SELECT NEW.noteId) AS note_select
    LEFT JOIN blobs b ON b.blobId = NEW.blobId;
    
    INSERT INTO notes_fts_trigram (noteId, title, content)
    SELECT 
        NEW.noteId,
        NEW.title,
        COALESCE(b.content, '')
    FROM (SELECT NEW.noteId) AS note_select
    LEFT JOIN blobs b ON b.blobId = NEW.blobId;
END;

-- Trigger for UPDATE operations on notes table
-- Handles: Regular UPDATE and the UPDATE part of upsert (ON CONFLICT DO UPDATE)
-- Fires for ANY update to searchable notes to ensure FTS stays in sync
CREATE TRIGGER notes_fts_update 
AFTER UPDATE ON notes
WHEN NEW.type IN ('text', 'code', 'mermaid', 'canvas', 'mindMap')
    -- Fire on any change, not just specific columns, to handle all upsert scenarios
BEGIN
    -- Always delete the old entries from both FTS tables
    DELETE FROM notes_fts WHERE noteId = NEW.noteId;
    DELETE FROM notes_fts_trigram WHERE noteId = NEW.noteId;
    
    -- Insert new entries into both FTS tables if note is not deleted and not protected
    INSERT INTO notes_fts (noteId, title, content)
    SELECT 
        NEW.noteId,
        NEW.title,
        COALESCE(b.content, '')  -- Use empty string if blob doesn't exist yet
    FROM (SELECT NEW.noteId) AS note_select
    LEFT JOIN blobs b ON b.blobId = NEW.blobId
    WHERE NEW.isDeleted = 0
        AND NEW.isProtected = 0;
    
    INSERT INTO notes_fts_trigram (noteId, title, content)
    SELECT 
        NEW.noteId,
        NEW.title,
        COALESCE(b.content, '')
    FROM (SELECT NEW.noteId) AS note_select
    LEFT JOIN blobs b ON b.blobId = NEW.blobId
    WHERE NEW.isDeleted = 0
        AND NEW.isProtected = 0;
END;

-- Trigger for UPDATE operations on blobs
-- Handles: Regular UPDATE and the UPDATE part of upsert (ON CONFLICT DO UPDATE)
-- IMPORTANT: Uses INSERT OR REPLACE for efficiency with deduplicated blobs
CREATE TRIGGER notes_fts_blob_update 
AFTER UPDATE ON blobs
BEGIN
    -- Update both FTS tables for all notes sharing this blob
    INSERT OR REPLACE INTO notes_fts (noteId, title, content)
    SELECT 
        n.noteId,
        n.title,
        NEW.content
    FROM notes n
    WHERE n.blobId = NEW.blobId
        AND n.type IN ('text', 'code', 'mermaid', 'canvas', 'mindMap')
        AND n.isDeleted = 0
        AND n.isProtected = 0;
    
    INSERT OR REPLACE INTO notes_fts_trigram (noteId, title, content)
    SELECT 
        n.noteId,
        n.title,
        NEW.content
    FROM notes n
    WHERE n.blobId = NEW.blobId
        AND n.type IN ('text', 'code', 'mermaid', 'canvas', 'mindMap')
        AND n.isDeleted = 0
        AND n.isProtected = 0;
END;

-- Trigger for DELETE operations
CREATE TRIGGER notes_fts_delete 
AFTER DELETE ON notes
BEGIN
    DELETE FROM notes_fts WHERE noteId = OLD.noteId;
    DELETE FROM notes_fts_trigram WHERE noteId = OLD.noteId;
END;

-- Trigger for soft delete (isDeleted = 1)
CREATE TRIGGER notes_fts_soft_delete 
AFTER UPDATE ON notes
WHEN OLD.isDeleted = 0 AND NEW.isDeleted = 1
BEGIN
    DELETE FROM notes_fts WHERE noteId = NEW.noteId;
    DELETE FROM notes_fts_trigram WHERE noteId = NEW.noteId;
END;

-- Trigger for notes becoming protected
-- Remove from FTS when a note becomes protected
CREATE TRIGGER notes_fts_protect 
AFTER UPDATE ON notes
WHEN OLD.isProtected = 0 AND NEW.isProtected = 1
BEGIN
    DELETE FROM notes_fts WHERE noteId = NEW.noteId;
    DELETE FROM notes_fts_trigram WHERE noteId = NEW.noteId;
END;

-- Trigger for notes becoming unprotected
-- Add to FTS when a note becomes unprotected (if eligible)
CREATE TRIGGER notes_fts_unprotect 
AFTER UPDATE ON notes
WHEN OLD.isProtected = 1 AND NEW.isProtected = 0
    AND NEW.type IN ('text', 'code', 'mermaid', 'canvas', 'mindMap')
    AND NEW.isDeleted = 0
BEGIN
    DELETE FROM notes_fts WHERE noteId = NEW.noteId;
    DELETE FROM notes_fts_trigram WHERE noteId = NEW.noteId;
    
    INSERT INTO notes_fts (noteId, title, content)
    SELECT 
        NEW.noteId,
        NEW.title,
        COALESCE(b.content, '')
    FROM (SELECT NEW.noteId) AS note_select
    LEFT JOIN blobs b ON b.blobId = NEW.blobId;
    
    INSERT INTO notes_fts_trigram (noteId, title, content)
    SELECT 
        NEW.noteId,
        NEW.title,
        COALESCE(b.content, '')
    FROM (SELECT NEW.noteId) AS note_select
    LEFT JOIN blobs b ON b.blobId = NEW.blobId;
END;

-- Trigger for INSERT operations on blobs
-- Handles: INSERT, INSERT OR REPLACE, and the INSERT part of upsert
-- Updates all notes that reference this blob (common during import and deduplication)
CREATE TRIGGER notes_fts_blob_insert 
AFTER INSERT ON blobs
BEGIN
    -- Update both FTS tables for all notes that reference this blob
    INSERT OR REPLACE INTO notes_fts (noteId, title, content)
    SELECT 
        n.noteId,
        n.title,
        NEW.content
    FROM notes n
    WHERE n.blobId = NEW.blobId
        AND n.type IN ('text', 'code', 'mermaid', 'canvas', 'mindMap')
        AND n.isDeleted = 0
        AND n.isProtected = 0;
    
    INSERT OR REPLACE INTO notes_fts_trigram (noteId, title, content)
    SELECT 
        n.noteId,
        n.title,
        NEW.content
    FROM notes n
    WHERE n.blobId = NEW.blobId
        AND n.type IN ('text', 'code', 'mermaid', 'canvas', 'mindMap')
        AND n.isDeleted = 0
        AND n.isProtected = 0;
END;
