/**
 * Unit test for MigrationRunner — fresh DB → all tables present.
 *
 * Run with: node --test packages/runtime/src/storage/MigrationRunner.test.mjs
 *   (after compiling, or directly via tsx)
 *
 * Uses Node 22 built-in node:test + node:sqlite (no external deps).
 */

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { DatabaseSync } from 'node:sqlite';

import {
  MigrationRunner,
} from './MigrationRunner.js';

// ---------------------------------------------------------------------------
// Adapter: node:sqlite DatabaseSync → DatabaseHandle
// ---------------------------------------------------------------------------

function asHandle(db) {
  return {
    exec(sql) {
      db.exec(sql);
    },
    prepare(sql) {
      const stmt = db.prepare(sql);
      return {
        run(...params) {
          stmt.run(...params);
          return { changes: db.changes };
        },
        get(...params) {
          return stmt.get(...params);
        },
      };
    },
    query(sql, ...params) {
      return db.prepare(sql).all(...params);
    },
  };
}

// ---------------------------------------------------------------------------
// Expected tables (db-schema-v1 §2–§18)
// ---------------------------------------------------------------------------

const EXPECTED_TABLES = [
  'schema_meta',        // §2
  'sessions',           // §3
  'messages',           // §4
  'message_drafts',     // §5
  'events',             // §6
  'tasks',              // §7
  'task_dependencies',  // §8
  'task_attempts',      // §9
  'agents',             // §10
  'tool_runs',          // §11
  'command_runs',       // §12
  'artifacts',          // §13
  'diagnostics',        // §14
  'evidence_refs',      // §15
  'workspaces',         // §16
  'summaries',          // §17
  'ui_state',           // §18
];

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

describe('MigrationRunner', () => {
  it('creates all 17 session tables on a fresh DB', async () => {
    const raw = new DatabaseSync(':memory:');
    const db = asHandle(raw);

    try {
      const runner = new MigrationRunner();
      await runner.migrate(db);

      const rows = db.query(
        "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name",
      );
      const tableNames = rows.map((r) => r.name);

      for (const expected of EXPECTED_TABLES) {
        assert.ok(
          tableNames.includes(expected),
          `Missing table: ${expected}. Found: ${tableNames.join(', ')}`,
        );
      }
      assert.equal(tableNames.length, EXPECTED_TABLES.length, 'Unexpected extra tables present');
    } finally {
      raw.close();
    }
  });

  it('seeds schema_meta with schema_version=1', async () => {
    const raw = new DatabaseSync(':memory:');
    const db = asHandle(raw);

    try {
      const runner = new MigrationRunner();
      await runner.migrate(db);

      const rows = db.query(
        "SELECT value FROM schema_meta WHERE key = 'schema_version'",
      );
      assert.ok(rows.length > 0, 'schema_version row missing');
      assert.equal(rows[0].value, '1');
    } finally {
      raw.close();
    }
  });

  it('creates all expected indexes', async () => {
    const raw = new DatabaseSync(':memory:');
    const db = asHandle(raw);

    try {
      const runner = new MigrationRunner();
      await runner.migrate(db);

      const rows = db.query(
        "SELECT name FROM sqlite_master WHERE type='index' AND name LIKE 'idx_%' ORDER BY name",
      );
      const indexNames = rows.map((r) => r.name);

      const expectedIndexes = [
        'idx_messages_session_created',         // §4
        'idx_drafts_session_status',            // §5
        'idx_events_session_type_time',         // §6
        'idx_events_route_text',                // §6
        'idx_tasks_session_status',             // §7
        'idx_task_deps_task',                   // §8
        'idx_task_deps_depends_on',             // §8
        'idx_task_deps_session_type',           // §8
        'idx_task_attempts_task',               // §9
        'idx_task_attempts_failure_signature',  // §9
        'idx_agents_session_status',            // §10
        'idx_agents_task',                      // §10
        'idx_tool_runs_origin_message',         // §11
        'idx_tool_runs_session_time',           // §11
        'idx_tool_runs_task_time',              // §11
        'idx_tool_runs_agent_time',             // §11
        'idx_command_runs_origin_message',      // §12
        'idx_command_runs_session_time',        // §12
        'idx_command_runs_task_time',           // §12
        'idx_command_runs_agent_time',          // §12
        'idx_artifacts_session_type_time',      // §13
        'idx_artifacts_task',                   // §13
        'idx_artifacts_agent',                  // §13
        'idx_artifacts_tool_run',               // §13
        'idx_artifacts_command_run',            // §13
        'idx_diagnostics_signature',            // §14
        'idx_diagnostics_file',                 // §14
        'idx_diagnostics_command',              // §14
        'idx_diagnostics_task',                 // §14
        'idx_evidence_task',                    // §15
        'idx_evidence_artifact',                // §15
        'idx_evidence_diagnostic',              // §15
        'idx_workspaces_task',                  // §16
        'idx_workspaces_status',                // §16
        'idx_ui_state_session_scope_key',       // §18
      ];

      for (const idx of expectedIndexes) {
        assert.ok(
          indexNames.includes(idx),
          `Missing index: ${idx}. Found: ${indexNames.join(', ')}`,
        );
      }
    } finally {
      raw.close();
    }
  });

  it('is idempotent — safe to run migrate twice', async () => {
    const raw = new DatabaseSync(':memory:');
    const db = asHandle(raw);

    try {
      const runner = new MigrationRunner();
      await runner.migrate(db);
      await runner.migrate(db); // second call must not throw

      const rows = db.query(
        "SELECT value FROM schema_meta WHERE key = 'schema_version'",
      );
      assert.ok(rows.length > 0, 'schema_version row missing after double migrate');
      assert.equal(rows[0].value, '1');
    } finally {
      raw.close();
    }
  });

  it('currentVersion returns 0 on empty DB and 1 after migration', async () => {
    const raw = new DatabaseSync(':memory:');
    const db = asHandle(raw);

    try {
      const runner = new MigrationRunner();
      assert.equal(runner.currentVersion(db), 0, 'should be 0 before migration');

      await runner.migrate(db);
      assert.equal(runner.currentVersion(db), 1, 'should be 1 after migration');
    } finally {
      raw.close();
    }
  });

  it('targetVersion returns 1', () => {
    const runner = new MigrationRunner();
    assert.equal(runner.targetVersion(), 1);
  });
});