/**
 * Initial schema migration.
 * Creates all core tables for GNO.
 *
 * @module src/store/migrations/001_initial
 */

import type { Database } from "bun:sqlite";

import type { FtsTokenizer } from "../../config/types";
import type { Migration } from "./runner";

export const migration: Migration = {
  version: 1,
  name: "initial_schema",

  up(db: Database, ftsTokenizer: FtsTokenizer): void {
    // Collections (synced from YAML config)
    db.exec(`
      CREATE TABLE IF NOT EXISTS collections (
        name TEXT PRIMARY KEY,
        path TEXT NOT NULL,
        pattern TEXT NOT NULL DEFAULT '**/*',
        include TEXT,
        exclude TEXT,
        update_cmd TEXT,
        language_hint TEXT,
        synced_at TEXT NOT NULL DEFAULT (datetime('now'))
      )
    `);

    // Contexts (synced from YAML config)
    db.exec(`
      CREATE TABLE IF NOT EXISTS contexts (
        scope_type TEXT NOT NULL CHECK (scope_type IN ('global', 'collection', 'prefix')),
        scope_key TEXT NOT NULL,
        text TEXT NOT NULL,
        synced_at TEXT NOT NULL DEFAULT (datetime('now')),
        PRIMARY KEY (scope_type, scope_key)
      )
    `);

    // Documents (source file identity)
    db.exec(`
      CREATE TABLE IF NOT EXISTS documents (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        collection TEXT NOT NULL,
        rel_path TEXT NOT NULL,

        source_hash TEXT NOT NULL,
        source_mime TEXT NOT NULL,
        source_ext TEXT NOT NULL,
        source_size INTEGER NOT NULL,
        source_mtime TEXT NOT NULL,

        docid TEXT NOT NULL,
        uri TEXT NOT NULL,

        title TEXT,
        mirror_hash TEXT,
        converter_id TEXT,
        converter_version TEXT,
        language_hint TEXT,

        active INTEGER NOT NULL DEFAULT 1,

        last_error_code TEXT,
        last_error_message TEXT,
        last_error_at TEXT,

        created_at TEXT NOT NULL DEFAULT (datetime('now')),
        updated_at TEXT NOT NULL DEFAULT (datetime('now')),

        UNIQUE (collection, rel_path),
        FOREIGN KEY (collection) REFERENCES collections(name) ON DELETE CASCADE
        -- Note: mirror_hash is NOT an FK - documents are tracked before content exists
        -- Cleanup via cleanupOrphans() handles orphaned content
      )
    `);

    db.exec(
      "CREATE INDEX IF NOT EXISTS idx_documents_collection ON documents(collection)"
    );
    db.exec(
      "CREATE INDEX IF NOT EXISTS idx_documents_active ON documents(active)"
    );
    db.exec(
      "CREATE INDEX IF NOT EXISTS idx_documents_mirror_hash ON documents(mirror_hash)"
    );
    db.exec(
      "CREATE INDEX IF NOT EXISTS idx_documents_docid ON documents(docid)"
    );
    db.exec("CREATE INDEX IF NOT EXISTS idx_documents_uri ON documents(uri)");

    // Content (content-addressed markdown mirrors)
    db.exec(`
      CREATE TABLE IF NOT EXISTS content (
        mirror_hash TEXT PRIMARY KEY,
        markdown TEXT NOT NULL,
        created_at TEXT NOT NULL DEFAULT (datetime('now'))
      )
    `);

    // Content chunks (for FTS and vectors)
    db.exec(`
      CREATE TABLE IF NOT EXISTS content_chunks (
        mirror_hash TEXT NOT NULL,
        seq INTEGER NOT NULL,
        pos INTEGER NOT NULL,
        text TEXT NOT NULL,
        start_line INTEGER NOT NULL,
        end_line INTEGER NOT NULL,
        language TEXT,
        token_count INTEGER,
        created_at TEXT NOT NULL DEFAULT (datetime('now')),
        PRIMARY KEY (mirror_hash, seq),
        FOREIGN KEY (mirror_hash) REFERENCES content(mirror_hash) ON DELETE CASCADE
      )
    `);

    db.exec(
      "CREATE INDEX IF NOT EXISTS idx_chunks_mirror_hash ON content_chunks(mirror_hash)"
    );

    // FTS5 virtual table with configurable tokenizer
    db.exec(`
      CREATE VIRTUAL TABLE IF NOT EXISTS content_fts USING fts5(
        text,
        tokenize='${ftsTokenizer}'
      )
    `);

    // Content vectors (EPIC 7)
    db.exec(`
      CREATE TABLE IF NOT EXISTS content_vectors (
        mirror_hash TEXT NOT NULL,
        seq INTEGER NOT NULL,
        model TEXT NOT NULL,
        embedding BLOB NOT NULL,
        embedded_at TEXT NOT NULL DEFAULT (datetime('now')),
        PRIMARY KEY (mirror_hash, seq, model),
        FOREIGN KEY (mirror_hash, seq) REFERENCES content_chunks(mirror_hash, seq) ON DELETE CASCADE
      )
    `);

    db.exec(
      "CREATE INDEX IF NOT EXISTS idx_vectors_model ON content_vectors(model)"
    );

    // LLM cache (EPIC 6+)
    db.exec(`
      CREATE TABLE IF NOT EXISTS llm_cache (
        key TEXT PRIMARY KEY,
        value TEXT NOT NULL,
        created_at TEXT NOT NULL DEFAULT (datetime('now')),
        expires_at TEXT
      )
    `);

    db.exec(
      "CREATE INDEX IF NOT EXISTS idx_llm_cache_expires ON llm_cache(expires_at)"
    );

    // Ingest errors
    db.exec(`
      CREATE TABLE IF NOT EXISTS ingest_errors (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        collection TEXT NOT NULL,
        rel_path TEXT NOT NULL,
        occurred_at TEXT NOT NULL DEFAULT (datetime('now')),
        code TEXT NOT NULL,
        message TEXT NOT NULL,
        details_json TEXT
      )
    `);

    db.exec(
      "CREATE INDEX IF NOT EXISTS idx_ingest_errors_occurred ON ingest_errors(occurred_at DESC)"
    );
    db.exec(
      "CREATE INDEX IF NOT EXISTS idx_ingest_errors_collection ON ingest_errors(collection, rel_path)"
    );
  },

  down(db: Database): void {
    // Drop in reverse order of creation
    db.exec("DROP TABLE IF EXISTS ingest_errors");
    db.exec("DROP TABLE IF EXISTS llm_cache");
    db.exec("DROP TABLE IF EXISTS content_vectors");
    db.exec("DROP TABLE IF EXISTS content_fts");
    db.exec("DROP TABLE IF EXISTS content_chunks");
    db.exec("DROP TABLE IF EXISTS content");
    db.exec("DROP TABLE IF EXISTS documents");
    db.exec("DROP TABLE IF EXISTS contexts");
    db.exec("DROP TABLE IF EXISTS collections");
  },
};
