import * as fs from 'node:fs/promises';
import * as os from 'node:os';
import * as path from 'node:path';
import { color } from '@wrongstack/core';
import { API_VERSION } from '../../version.js';
import type { SubcommandHandler } from '../index.js';

export const diagCmd: SubcommandHandler = async (_args, deps) => {
  const cfg = deps.config;
  const age = await deps.modelsRegistry.ageSeconds();
  const lines = [
    color.bold('WrongStack diagnostics'),
    `  apiVersion:    ${API_VERSION}`,
    `  cwd:           ${deps.cwd}`,
    `  projectRoot:   ${deps.projectRoot}`,
    `  projectHash:   ${deps.paths.projectHash}`,
    `  projectDir:    ${deps.paths.projectDir}`,
    `  globalRoot:    ${deps.paths.globalRoot}`,
    `  modelsCache:   ${deps.paths.modelsCache}`,
    `  cacheAge:      ${isFinite(age) ? `${Math.round(age / 60)}m` : 'never'}`,
    `  node:          ${process.version}`,
    `  os:            ${os.platform()} ${os.release()}`,
    `  provider:      ${cfg.provider ?? '<unset>'}`,
    `  model:         ${cfg.model ?? '<unset>'}`,
    `  tools:         ${deps.toolRegistry?.list().length ?? 0}`,
    `  plugins:       ${cfg.plugins?.length ?? 0}`,
    `  mcpServers:    ${Object.keys(cfg.mcpServers ?? {}).length}`,
  ];
  deps.renderer.write(lines.join('\n') + '\n');
  return 0;
};

export const doctorCmd: SubcommandHandler = async (_args, deps) => {
  type CheckResult = { name: string; status: 'ok' | 'warn' | 'fail'; detail: string };
  const checks: CheckResult[] = [];
  const cfg = deps.config;
  if (!cfg.provider)
    checks.push({
      name: 'provider',
      status: 'fail',
      detail: 'no provider configured — run `wstack init` or `wstack auth`',
    });
  else checks.push({ name: 'provider', status: 'ok', detail: cfg.provider });
  if (!cfg.model)
    checks.push({
      name: 'model',
      status: 'fail',
      detail: 'no model configured — run `wstack init`',
    });
  else checks.push({ name: 'model', status: 'ok', detail: cfg.model });
  if (cfg.provider) {
    const providerCfg = (
      cfg.providers as Record<string, { apiKey?: string | undefined; envVars?: string[] | undefined }> | undefined
    )?.[cfg.provider];
    const hasVaultKey = typeof providerCfg?.apiKey === 'string' && providerCfg.apiKey.length > 0;
    const envHit = providerCfg?.envVars?.some((v) => process.env[v]) ?? false;
    if (hasVaultKey || envHit)
      checks.push({
        name: 'api key',
        status: 'ok',
        detail: hasVaultKey ? 'found in vault' : 'found in env',
      });
    else
      checks.push({
        name: 'api key',
        status: 'fail',
        detail: `no key for "${cfg.provider}" in vault or env — run \`wstack auth ${cfg.provider}\``,
      });
  }
  try {
    const age = await deps.modelsRegistry.ageSeconds();
    if (!isFinite(age))
      checks.push({
        name: 'models cache',
        status: 'warn',
        detail: 'never fetched — run `wstack models refresh`',
      });
    else if (age > 7 * 24 * 3600)
      checks.push({
        name: 'models cache',
        status: 'warn',
        detail: `${Math.round(age / 86400)} days old — run \`wstack models refresh\``,
      });
    else
      checks.push({ name: 'models cache', status: 'ok', detail: `${Math.round(age / 60)}m old` });
  } catch (err) {
    checks.push({
      name: 'models cache',
      status: 'warn',
      detail: `read failed: ${err instanceof Error ? err.message : String(err)}`,
    });
  }
  try {
    await fs.access(deps.paths.secretsKey);
    checks.push({ name: 'secret vault', status: 'ok', detail: deps.paths.secretsKey });
  } catch {
    checks.push({
      name: 'secret vault',
      status: 'warn',
      detail: 'not yet initialized (created lazily on first encrypt)',
    });
  }
  try {
    await fs.mkdir(deps.paths.projectSessions, { recursive: true });
    const probe = path.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
    await fs.writeFile(probe, '');
    await fs.unlink(probe);
    checks.push({ name: 'sessions writable', status: 'ok', detail: deps.paths.projectSessions });
  } catch (err) {
    checks.push({
      name: 'sessions writable',
      status: 'fail',
      detail: `cannot write to ${deps.paths.projectSessions}: ${err instanceof Error ? err.message : String(err)}`,
    });
  }
  const mcpEntries = Object.entries(cfg.mcpServers ?? {}) as [
    string,
    { enabled?: boolean | undefined; transport?: string | undefined; command?: string | undefined; url?: string | undefined },
  ][];
  for (const [name, srv] of mcpEntries) {
    if (!srv.enabled) continue;
    if ((srv.transport === 'sse' || srv.transport === 'streamable-http') && !srv.url)
      checks.push({ name: `mcp:${name}`, status: 'fail', detail: 'transport requires url' });
    else if (srv.transport === 'stdio' && !srv.command)
      checks.push({
        name: `mcp:${name}`,
        status: 'fail',
        detail: 'stdio transport requires command',
      });
    else
      checks.push({
        name: `mcp:${name}`,
        status: 'ok',
        detail: `${srv.transport} ${srv.command ?? srv.url ?? ''}`.trim(),
      });
  }
  const major = Number.parseInt(process.version.replace(/^v/, '').split('.')[0] ?? '0', 10);
  if (major < 22)
    checks.push({ name: 'node', status: 'fail', detail: `${process.version} (need ≥22)` });
  else checks.push({ name: 'node', status: 'ok', detail: process.version });
  deps.renderer.write(color.bold('WrongStack doctor\n\n'));
  let failed = 0;
  let warned = 0;
  for (const c of checks) {
    const icon =
      c.status === 'ok'
        ? color.green('✓')
        : c.status === 'warn'
          ? color.amber('●')
          : color.red('✗');
    deps.renderer.write(`  ${icon} ${c.name.padEnd(20)} ${color.dim(c.detail)}\n`);
    if (c.status === 'fail') failed++;
    if (c.status === 'warn') warned++;
  }
  deps.renderer.write('\n');
  if (failed > 0) {
    deps.renderer.write(
      color.red(`${failed} failed, ${warned} warning${warned === 1 ? '' : 's'}\n`),
    );
    return 1;
  }
  if (warned > 0) {
    deps.renderer.write(
      color.amber(`All checks passed (${warned} warning${warned === 1 ? '' : 's'})\n`),
    );
    return 0;
  }
  deps.renderer.write(color.green('All checks passed.\n'));
  return 0;
};
