/**
 * Tests for `doctorReportRemote()` — the focused thin-client doctor that
 * powers the run_doctor MCP op.
 *
 * Strategy: build a fresh PGLite engine + initSchema, run the report, assert
 * all 5 checks present + healthy. Uses the canonical PGLite test pattern
 * (beforeAll + afterAll, not beforeEach) per CLAUDE.md test-isolation rules.
 */

import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
import { mkdtempSync, rmSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { PGLiteEngine } from '../src/core/pglite-engine.ts';
import { doctorReportRemote, computeDoctorReport, type DoctorReport, type Check } from '../src/commands/doctor.ts';

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

beforeAll(async () => {
  // v0.37.10.0: doctorReportRemote reads from ~/.gbrain audit files
  // (reranker_health, sync_failures, etc.). Without isolation, host state
  // leaks into the test and makes the assertion non-deterministic. Pin
  // GBRAIN_HOME to a tempdir so audit reads return empty.
  tmpHome = mkdtempSync(join(tmpdir(), 'gbrain-doctor-remote-'));
  priorHome = process.env.GBRAIN_HOME;
  process.env.GBRAIN_HOME = tmpHome;
  engine = new PGLiteEngine();
  await engine.connect({});
  await engine.initSchema();
});

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

describe('doctorReportRemote', () => {
  test('runs all 5 checks on a fresh PGLite brain', async () => {
    const report = await doctorReportRemote(engine);
    expect(report.schema_version).toBe(2);
    expect(report.checks.length).toBeGreaterThanOrEqual(5);
    const names = report.checks.map(c => c.name);
    expect(names).toContain('connection');
    expect(names).toContain('schema_version');
    expect(names).toContain('brain_score');
    expect(names).toContain('sync_failures');
    expect(names).toContain('queue_health');
  });

  test('connection check passes against a healthy engine', async () => {
    const report = await doctorReportRemote(engine);
    const conn = report.checks.find(c => c.name === 'connection');
    expect(conn).toBeDefined();
    expect(conn!.status).toBe('ok');
    expect(conn!.message).toContain('Connected');
  });

  test('schema_version check shows the latest version', async () => {
    const report = await doctorReportRemote(engine);
    const sv = report.checks.find(c => c.name === 'schema_version');
    expect(sv).toBeDefined();
    // Fresh PGLite at LATEST_VERSION → status ok with "(latest)"
    expect(sv!.status).toBe('ok');
    expect(sv!.message.toLowerCase()).toContain('latest');
  });

  test('queue_health is informational on PGLite', async () => {
    const report = await doctorReportRemote(engine);
    const q = report.checks.find(c => c.name === 'queue_health');
    expect(q).toBeDefined();
    expect(q!.status).toBe('ok');
    // PGLite-specific message
    expect(q!.message).toContain('PGLite');
  });

  test('full report on healthy brain is "healthy" status', async () => {
    const report = await doctorReportRemote(engine);
    expect(report.status).toMatch(/healthy|warnings/);
    expect(report.health_score).toBeGreaterThanOrEqual(70);
  });
});

describe('computeDoctorReport — score + status math', () => {
  function check(status: Check['status']): Check {
    return { name: `check-${status}`, status, message: '' };
  }

  test('all-ok → healthy + 100', () => {
    const r = computeDoctorReport([check('ok'), check('ok'), check('ok')]);
    expect(r.status).toBe('healthy');
    expect(r.health_score).toBe(100);
  });

  test('one warn → warnings + score - 5', () => {
    const r = computeDoctorReport([check('ok'), check('warn'), check('ok')]);
    expect(r.status).toBe('warnings');
    expect(r.health_score).toBe(95);
  });

  test('one fail → unhealthy + score - 20', () => {
    const r = computeDoctorReport([check('ok'), check('fail'), check('ok')]);
    expect(r.status).toBe('unhealthy');
    expect(r.health_score).toBe(80);
  });

  test('mix of fail + warn → unhealthy (fail dominates)', () => {
    const r = computeDoctorReport([check('warn'), check('fail'), check('warn')]);
    expect(r.status).toBe('unhealthy');
    expect(r.health_score).toBe(70);
  });

  test('score floor at 0', () => {
    const fails: Check[] = [];
    for (let i = 0; i < 10; i++) fails.push(check('fail'));
    const r = computeDoctorReport(fails);
    expect(r.health_score).toBe(0);
  });

  test('schema_version is always 2', () => {
    const r: DoctorReport = computeDoctorReport([check('ok')]);
    expect(r.schema_version).toBe(2);
  });
});
