import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import Database from 'better-sqlite3';
import { mkdtempSync, rmSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';
import { runMigrations } from './migrate.js';
import { Repository } from './repository.js';
import { createRegistry } from '../mcp/registry.js';
import { createConnectionRepo, type CreateConnectionInput } from '../ssh/connection-repo.js';

/**
 * Spaces foundation (Phase 1): mcp_servers / ssh_connections gain a nullable
 * `space_id` column via the triple-path (schema.sql / migrate.ts /
 * Repository.initSchema). Phase 1 is a no-op — nobody resolves by space_id yet,
 * so these tests assert column existence + row mapping only.
 */

function hasColumn(db: Database.Database, table: string, column: string): boolean {
  const cols = db.prepare(`PRAGMA table_info('${table}')`).all() as Array<{ name: string }>;
  return cols.some((c) => c.name === column);
}

describe('space_id triple-path: migrate.ts', () => {
  let db: Database.Database;

  beforeEach(() => {
    process.env.MCP_ENCRYPTION_KEY = 'a'.repeat(64);
    db = new Database(':memory:');
    db.pragma('foreign_keys = ON');
    db.exec(`CREATE TABLE users (id TEXT PRIMARY KEY);`);
    db.exec(`CREATE TABLE jobs (id TEXT PRIMARY KEY, wait_reason TEXT);`);
    db.exec(`CREATE TABLE local_tasks (id INTEGER PRIMARY KEY AUTOINCREMENT);`);
    runMigrations(db);
  });

  afterEach(() => {
    db.close();
  });

  it('adds space_id to mcp_servers', () => {
    expect(hasColumn(db, 'mcp_servers', 'space_id')).toBe(true);
  });

  it('adds space_id to ssh_connections', () => {
    expect(hasColumn(db, 'ssh_connections', 'space_id')).toBe(true);
  });

  it('is idempotent — running twice does not error and keeps the columns', () => {
    expect(() => runMigrations(db)).not.toThrow();
    expect(hasColumn(db, 'mcp_servers', 'space_id')).toBe(true);
    expect(hasColumn(db, 'ssh_connections', 'space_id')).toBe(true);
  });

  it('does NOT add space_id to user_mcp_tokens or ssh_connection_grants', () => {
    expect(hasColumn(db, 'user_mcp_tokens', 'space_id')).toBe(false);
    expect(hasColumn(db, 'ssh_connection_grants', 'space_id')).toBe(false);
  });
});

describe('space_id triple-path: Repository.initSchema (schema.sql)', () => {
  let tempDir = '';

  afterEach(() => {
    if (tempDir) {
      rmSync(tempDir, { recursive: true, force: true });
      tempDir = '';
    }
  });

  function makeRepo(): Repository {
    tempDir = mkdtempSync(join(tmpdir(), 'maestro-spaceid-'));
    return new Repository(join(tempDir, 'orchestrator.db'));
  }

  it('mcp_servers has space_id on a fresh Repository', () => {
    const repo = makeRepo();
    expect(hasColumn(repo.getDb(), 'mcp_servers', 'space_id')).toBe(true);
  });

  it('ssh_connections has space_id on a fresh Repository', () => {
    const repo = makeRepo();
    expect(hasColumn(repo.getDb(), 'ssh_connections', 'space_id')).toBe(true);
  });
});

describe('space_id row mapping', () => {
  let db: Database.Database;

  beforeEach(() => {
    process.env.MCP_ENCRYPTION_KEY = 'a'.repeat(64);
    db = new Database(':memory:');
    db.pragma('foreign_keys = ON');
    db.exec(`CREATE TABLE users (id TEXT PRIMARY KEY);`);
    db.exec(`CREATE TABLE jobs (id TEXT PRIMARY KEY, wait_reason TEXT);`);
    db.exec(`CREATE TABLE local_tasks (id INTEGER PRIMARY KEY AUTOINCREMENT);`);
    runMigrations(db);
    db.prepare('INSERT INTO users(id) VALUES (?)').run('alice');
  });

  afterEach(() => {
    db.close();
  });

  it('McpServerPublic exposes spaceId (null when unset)', () => {
    const reg = createRegistry(db);
    reg.upsert({
      id: 'srv1',
      name: 'Srv',
      url: 'https://mcp.example.com/mcp',
      authKind: 'oauth',
      ownerId: null,
      oauthClientId: 'cid',
      oauthClientSecret: 'cs',
      oauthScopes: null,
    });
    const [pub] = reg.listPublic();
    expect(pub).toBeTruthy();
    expect(pub.spaceId).toBeNull();
  });

  it('SshConnection exposes spaceId (null when unset)', () => {
    const repo = createConnectionRepo(db);
    const input: CreateConnectionInput = {
      ownerId: 'alice',
      label: 'prod',
      host: 'srv.example.com',
      port: 22,
      username: 'deploy',
      privateKeyEnc: Buffer.from([1, 2, 3]),
      keyFingerprint: 'SHA256:abc',
      remotePathPrefix: '/home/deploy',
    };
    const created = repo.create(input);
    expect(created.spaceId).toBeNull();
  });
});
