/**
 * v0.30.1 integration smoke test — PGLite path.
 *
 * Exercises the Lane A-E surfaces together against an in-memory PGLite
 * brain to prove the new modules integrate. No DATABASE_URL required.
 *
 * What this proves:
 *   Lane A: ConnectionManager constructed; doctor diagnostic shape
 *           (single-mode for non-Supabase URL).
 *   Lane B: Migration runner applies pending migrations cleanly via the
 *           new retry wrapper. v44 (emotional_weight_recomputed_at)
 *           lands on PGLite.
 *   Lane C: Backfill registry resolves all 3 entries; running
 *           emotional_weight backfill on an empty brain returns
 *           examined=0 (no work).
 *   Lane D: dropZombieIndexes on PGLite returns dropped=[] (no-op).
 *   Lane E: upgrade-checkpoint round-trips with a brain_id derived from
 *           the engine config.
 *
 * Postgres-only e2es (connection-routing, hnsw-lifecycle, migrate-supabase
 * timeout/wedge recovery) live in their own DATABASE_URL-gated files;
 * those verify behaviors that PGLite can't exercise (pooler timeout,
 * CONCURRENTLY index, multi-tenant lock, etc.).
 */

import { describe, expect, test, beforeEach, afterEach } from 'bun:test';
import { mkdtempSync, rmSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { PGLiteEngine } from '../../src/core/pglite-engine.ts';
import { listBackfills, getBackfill } from '../../src/core/backfill-registry.ts';
import { runBackfill } from '../../src/core/backfill-base.ts';
import { dropZombieIndexes, checkActiveBuild } from '../../src/core/vector-index.ts';
import {
  computeBrainId,
  writeCheckpoint,
  loadCheckpoint,
  validateCheckpoint,
  markStepComplete,
  type UpgradeCheckpoint,
} from '../../src/core/upgrade-checkpoint.ts';
import { LATEST_VERSION } from '../../src/core/migrate.ts';

let tmpHome: string;
let originalHome: string | undefined;
let engine: PGLiteEngine;

beforeEach(async () => {
  tmpHome = mkdtempSync(join(tmpdir(), 'v030_1-int-'));
  originalHome = process.env.GBRAIN_HOME;
  process.env.GBRAIN_HOME = tmpHome;
  engine = new PGLiteEngine();
  await engine.connect({});
  await engine.initSchema();
});

afterEach(async () => {
  await engine.disconnect();
  if (originalHome === undefined) delete process.env.GBRAIN_HOME;
  else process.env.GBRAIN_HOME = originalHome;
  if (existsSync(tmpHome)) rmSync(tmpHome, { recursive: true, force: true });
});

describe('Lane B — migration runner applies cleanly through retry wrapper', () => {
  test('after initSchema, config.version is at LATEST_VERSION', async () => {
    const ver = await engine.getConfig('version');
    expect(parseInt(ver || '1', 10)).toBe(LATEST_VERSION);
  });

  test('v44 emotional_weight_recomputed_at column exists on pages', async () => {
    // PGLite supports information_schema.columns. ALTER TABLE ADD COLUMN
    // is idempotent, so v44 should have applied even on a freshly-created
    // PGLite brain.
    const rows = await engine.executeRaw<{ column_name: string }>(
      `SELECT column_name FROM information_schema.columns
       WHERE table_name = 'pages' AND column_name = 'emotional_weight_recomputed_at'`,
    );
    expect(rows.length).toBe(1);
  });
});

describe('Lane C — backfill registry on empty brain', () => {
  test('listBackfills returns the canonical registry entries', () => {
    // v0.30.1 shipped 3 entries (effective_date, embedding_voyage,
    // emotional_weight). v0.36 cross-modal wave adds `modality` for
    // historical image-asset chunks. Extend this assertion as new
    // backfills land.
    const list = listBackfills();
    const names = list.map(e => e.spec.name).sort();
    expect(names).toEqual(['effective_date', 'embedding_voyage', 'emotional_weight', 'modality']);
  });

  test('embedding_voyage is declared-only in v0.30.1', () => {
    const reg = getBackfill('embedding_voyage');
    expect(reg).toBeDefined();
    expect(reg!.v030_1_status).toBe('declared-only');
  });

  test('emotional_weight backfill on empty brain: examined=0', async () => {
    const reg = getBackfill('emotional_weight');
    expect(reg).toBeDefined();
    const result = await runBackfill(engine, reg!.spec, { batchSize: 100 });
    expect(result.examined).toBe(0);
    expect(result.errors).toBe(0);
  });

  test('effective_date backfill on empty brain: examined=0', async () => {
    const reg = getBackfill('effective_date');
    expect(reg).toBeDefined();
    const result = await runBackfill(engine, reg!.spec, { batchSize: 100 });
    expect(result.examined).toBe(0);
  });
});

describe('Lane D — vector-index lifecycle on PGLite', () => {
  test('dropZombieIndexes on PGLite is a no-op', async () => {
    const r = await dropZombieIndexes(engine);
    expect(r.dropped).toEqual([]);
  });

  test('checkActiveBuild on PGLite returns active: false', async () => {
    const r = await checkActiveBuild(engine, 'idx_chunks_embedding');
    expect(r.active).toBe(false);
  });
});

describe('Lane E — upgrade-checkpoint with brain identity', () => {
  test('round-trips a checkpoint with brain_id', async () => {
    const brainId = computeBrainId(undefined); // PGLite path
    const cp: UpgradeCheckpoint = {
      brain_id: brainId,
      started_at: new Date().toISOString(),
      from_version: '0.30.0',
      to_version: '0.30.1',
      completed_steps: ['pull', 'install'],
    };
    writeCheckpoint(cp);
    const loaded = loadCheckpoint();
    expect(loaded?.brain_id).toBe(brainId);
    expect(loaded?.completed_steps).toEqual(['pull', 'install']);
  });

  test('validateCheckpoint detects partial completion → resumeAt', async () => {
    const brainId = computeBrainId(undefined);
    const cp: UpgradeCheckpoint = {
      brain_id: brainId,
      started_at: new Date().toISOString(),
      from_version: '0.30.0',
      to_version: '0.30.1',
      completed_steps: ['pull', 'install', 'schema'],
    };
    writeCheckpoint(cp);
    const r = validateCheckpoint(brainId);
    expect(r.valid).toBe(true);
    expect(r.resumeAt).toBe('features');
  });

  test('cross-brain checkpoint mismatch (X2) refuses', async () => {
    const brainA = computeBrainId('postgresql://u:p@host:5432/db_a');
    const brainB = computeBrainId('postgresql://u:p@host:5432/db_b');
    const cp: UpgradeCheckpoint = {
      brain_id: brainA,
      started_at: new Date().toISOString(),
      from_version: '0.30.0',
      to_version: '0.30.1',
      completed_steps: ['pull'],
    };
    writeCheckpoint(cp);
    const r = validateCheckpoint(brainB);
    expect(r.valid).toBe(false);
    expect(r.reason).toBe('brain_mismatch');
  });

  test('full step progression: pull → install → schema → features → backfills → verify', async () => {
    const brainId = computeBrainId(undefined);
    let cp: UpgradeCheckpoint = {
      brain_id: brainId,
      started_at: new Date().toISOString(),
      from_version: '0.30.0',
      to_version: '0.30.1',
      completed_steps: [],
    };
    writeCheckpoint(cp);

    const steps = ['pull', 'install', 'schema', 'features', 'backfills'] as const;
    for (const s of steps) {
      cp = markStepComplete(cp, s);
      writeCheckpoint(cp);
      const v = validateCheckpoint(brainId);
      expect(v.valid).toBe(true);
    }
    cp = markStepComplete(cp, 'verify');
    writeCheckpoint(cp);
    const final = validateCheckpoint(brainId);
    expect(final.valid).toBe(false);
    expect(final.reason).toBe('all_complete');
  });
});

describe('Cross-lane integration', () => {
  test('PostgresEngine.connectionManager is null on PGLite (engine kind branch)', () => {
    // PGLite engines don't get a ConnectionManager — that's a Postgres-only
    // concern. PGLiteEngine doesn't have the property at all.
    const hasManager = 'connectionManager' in engine;
    expect(hasManager).toBe(false);
  });

  test('schema_version on BrainHealth is the optional v0.30.1 marker', async () => {
    // Engines don't yet populate this field; it's an optional contract.
    // The SHAPE compiles, the runtime read returns undefined.
    const health = await engine.getHealth();
    // schema_version is OPTIONAL (v0.30.1 declares the contract; engines
    // populate in v0.30.2). undefined is a valid v0.30.1 state.
    expect(health.schema_version === undefined || health.schema_version === '1').toBe(true);
  });
});
