/**
 * gateway-doctor.ts — `mosaic gateway doctor` implementation.
 *
 * Reports current tier and per-service health (PG, Valkey, pgvector) for the
 * Mosaic gateway. Supports machine-readable JSON output for CI.
 *
 * Exit codes:
 *   0 — overall green or yellow
 *   1 — overall red (at least one required service failed)
 */

import { existsSync } from 'node:fs';
import { resolve, join } from 'node:path';
import { homedir } from 'node:os';
import { loadConfig } from '@mosaicstack/config';
import { probeServiceHealth } from '@mosaicstack/storage';
import type { TierHealthReport, ServiceCheck } from '@mosaicstack/storage';

/* ------------------------------------------------------------------ */
/*  Config resolution                                                   */
/* ------------------------------------------------------------------ */

const CONFIG_CANDIDATES = [
  resolve(process.cwd(), 'mosaic.config.json'),
  join(homedir(), '.mosaic', 'mosaic.config.json'),
];

/**
 * Resolve the config path to report in output.
 *
 * Priority:
 *   1. Explicit `--config <path>` flag
 *   2. `./mosaic.config.json` (cwd)
 *   3. `~/.mosaic/mosaic.config.json`
 *   4. undefined — `loadConfig()` falls back to env-var detection
 *
 * `loadConfig()` itself already handles priority 1-3 when passed an explicit
 * path, and falls back to env-detection when none exists.  We resolve here
 * only so we can surface the path in the health report.
 */
function resolveConfigPath(explicit?: string): string | undefined {
  if (explicit) return resolve(explicit);
  for (const candidate of CONFIG_CANDIDATES) {
    if (existsSync(candidate)) return candidate;
  }
  return undefined;
}

/* ------------------------------------------------------------------ */
/*  Output helpers                                                      */
/* ------------------------------------------------------------------ */

const TICK = '\u2713'; // ✓
const CROSS = '\u2717'; // ✗
const SKIP = '-';

function padRight(s: string, n: number): string {
  return s + ' '.repeat(Math.max(0, n - s.length));
}

function serviceLabel(svc: ServiceCheck): string {
  const hostPort =
    svc.host !== undefined && svc.port !== undefined ? `${svc.host}:${svc.port.toString()}` : '';
  const duration = `(${svc.durationMs.toString()}ms)`;

  switch (svc.status) {
    case 'ok':
      return `  ${TICK} ${padRight(svc.name, 10)} ${padRight(hostPort, 22)} ${duration}`;
    case 'fail': {
      const errMsg = svc.error?.message ?? 'unknown error';
      return `  ${CROSS} ${padRight(svc.name, 10)} ${padRight(hostPort, 22)} ${duration}  \u2192 ${errMsg}`;
    }
    case 'skipped':
      return `  ${SKIP} ${padRight(svc.name, 10)} (skipped)`;
  }
}

function printReport(report: TierHealthReport): void {
  const configDisplay = report.configPath ?? '(auto-detected)';
  console.log(`Tier: ${report.tier}  Config: ${configDisplay}`);
  console.log('');

  for (const svc of report.services) {
    console.log(serviceLabel(svc));
  }

  console.log('');

  // Print remediations for failed services.
  const failed = report.services.filter((s) => s.status === 'fail' && s.error);
  if (failed.length > 0) {
    console.log('Remediations:');
    for (const svc of failed) {
      if (svc.error) {
        console.log(`  ${svc.name}: ${svc.error.remediation}`);
      }
    }
    console.log('');
  }

  console.log(`Overall: ${report.overall.toUpperCase()}`);
}

/* ------------------------------------------------------------------ */
/*  Main runner                                                         */
/* ------------------------------------------------------------------ */

export interface GatewayDoctorOptions {
  json?: boolean;
  config?: string;
}

export async function runGatewayDoctor(opts: GatewayDoctorOptions): Promise<void> {
  const configPath = resolveConfigPath(opts.config);

  let mosaicConfig;
  try {
    mosaicConfig = loadConfig(configPath);
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    if (opts.json) {
      process.stdout.write(
        JSON.stringify({ error: `Failed to load config: ${msg}` }, null, 2) + '\n',
      );
    } else {
      process.stderr.write(`Error: Failed to load config: ${msg}\n`);
    }
    process.exit(1);
  }

  const report = await probeServiceHealth(mosaicConfig, configPath);

  if (opts.json) {
    process.stdout.write(JSON.stringify(report, null, 2) + '\n');
  } else {
    printReport(report);
  }

  // Exit 1 if overall is red.
  if (report.overall === 'red') {
    process.exit(1);
  }
}
