/**
 * `mcpctl provider <action> <name>` — managed LLM lifecycle control.
 *
 *   up      — warmup() now (next chat is fast)
 *   down    — dispose() now; auto-restarts on next chat
 *   status  — state, pid, uptime, disabled flag
 *   disable — dispose() AND set `disabled: true` in ~/.mcpctl/config.json;
 *             survives mcplocal restarts; complete()/ensureRunning() short-
 *             circuit so the GPU process doesn't spawn until you `enable`
 *   enable  — clear the disabled flag (live + on disk)
 *
 * Talks to mcplocal's `/llm/providers/:name/{status,start,stop,enable,disable}`
 * HTTP endpoints. Non-managed providers (anthropic, openai, gemini-cli)
 * get a clear 400 rather than a no-op for the lifecycle actions.
 *
 * Practical use:
 *   `mcpctl provider down vllm-local`    — release GPU memory now
 *   `mcpctl provider disable vllm-local` — release GPU AND prevent auto-start
 *                                          (e.g. when vLLM keeps crashing)
 */
import { Command } from 'commander';
import http from 'node:http';

export interface ProviderCommandDeps {
  log: (...args: string[]) => void;
  mcplocalUrl?: string;
}

interface ProviderStatusResponse {
  name: string;
  managed: boolean;
  state?: 'stopped' | 'starting' | 'running' | 'error';
  lastError?: string | null;
  pid?: number | null;
  uptime?: number | null;
  /** True when the persistent disable flag is set (config + live). */
  disabled?: boolean;
  /** Set by /enable + /disable — true if the config file was rewritten. */
  persisted?: boolean;
}

interface ErrorResponse {
  error: string;
}

function fetchJson<T>(url: string, method: 'GET' | 'POST'): Promise<{ status: number; body: T }> {
  return new Promise((resolve, reject) => {
    const req = http.request(url, { method, timeout: 10_000 }, (res) => {
      let data = '';
      res.on('data', (chunk: Buffer) => { data += chunk.toString(); });
      res.on('end', () => {
        try {
          resolve({ status: res.statusCode ?? 0, body: JSON.parse(data) as T });
        } catch {
          reject(new Error(`Invalid response from mcplocal: ${data.slice(0, 200)}`));
        }
      });
    });
    req.on('error', () => reject(new Error('Cannot connect to mcplocal. Is it running? (`systemctl --user status mcplocal`)')));
    req.on('timeout', () => { req.destroy(); reject(new Error('mcplocal request timed out')); });
    req.end();
  });
}

function formatStatus(s: ProviderStatusResponse): string {
  if (!s.managed) {
    return `${s.name}: unmanaged (no lifecycle — API-key or remote provider)`;
  }
  const stateLabel = s.disabled === true ? `${s.state ?? 'stopped'} (disabled)` : (s.state ?? 'unknown');
  const lines = [`${s.name}: ${stateLabel}`];
  if (s.pid !== null && s.pid !== undefined) lines.push(`  pid: ${String(s.pid)}`);
  if (s.uptime !== null && s.uptime !== undefined) {
    const sec = s.uptime;
    const fmt = sec < 60 ? `${String(sec)}s`
      : sec < 3600 ? `${String(Math.floor(sec / 60))}m`
        : `${String(Math.floor(sec / 3600))}h${String(Math.floor((sec % 3600) / 60))}m`;
    lines.push(`  uptime: ${fmt}`);
  }
  if (s.lastError !== null && s.lastError !== undefined) lines.push(`  lastError: ${s.lastError}`);
  return lines.join('\n');
}

export function createProviderCommand(deps: ProviderCommandDeps): Command {
  const cmd = new Command('provider')
    .description('Control local LLM providers (start/stop/status)');

  const mcplocalUrl = deps.mcplocalUrl ?? 'http://localhost:3200';

  cmd
    .command('status')
    .description('Show lifecycle state of a provider')
    .argument('<name>', 'Provider name (e.g. vllm-local)')
    .action(async (name: string) => {
      const res = await fetchJson<ProviderStatusResponse | ErrorResponse>(
        `${mcplocalUrl}/llm/providers/${encodeURIComponent(name)}/status`,
        'GET',
      );
      if (res.status !== 200) {
        deps.log(`error: ${(res.body as ErrorResponse).error}`);
        process.exitCode = 1;
        return;
      }
      deps.log(formatStatus(res.body as ProviderStatusResponse));
    });

  cmd
    .command('up')
    .description('Start a managed provider (warm up so first chat is fast)')
    .argument('<name>', 'Provider name (e.g. vllm-local)')
    .action(async (name: string) => {
      const res = await fetchJson<ProviderStatusResponse | ErrorResponse>(
        `${mcplocalUrl}/llm/providers/${encodeURIComponent(name)}/start`,
        'POST',
      );
      if (res.status !== 202 && res.status !== 200) {
        deps.log(`error: ${(res.body as ErrorResponse).error}`);
        process.exitCode = 1;
        return;
      }
      const status = res.body as ProviderStatusResponse;
      deps.log(`${status.name}: ${status.state ?? 'starting'} (warmup kicked — chat to confirm it's ready)`);
    });

  cmd
    .command('down')
    .description('Stop a managed provider now (releases GPU memory)')
    .argument('<name>', 'Provider name (e.g. vllm-local)')
    .action(async (name: string) => {
      const res = await fetchJson<ProviderStatusResponse | ErrorResponse>(
        `${mcplocalUrl}/llm/providers/${encodeURIComponent(name)}/stop`,
        'POST',
      );
      if (res.status !== 200) {
        deps.log(`error: ${(res.body as ErrorResponse).error}`);
        process.exitCode = 1;
        return;
      }
      const status = res.body as ProviderStatusResponse;
      deps.log(`${status.name}: ${status.state ?? 'stopped'} (GPU released — next chat will trigger restart)`);
    });

  cmd
    .command('disable')
    .description('Persistently disable a managed provider (survives mcplocal restart)')
    .argument('<name>', 'Provider name (e.g. vllm-local)')
    .action(async (name: string) => {
      const res = await fetchJson<ProviderStatusResponse | ErrorResponse>(
        `${mcplocalUrl}/llm/providers/${encodeURIComponent(name)}/disable`,
        'POST',
      );
      if (res.status !== 200) {
        deps.log(`error: ${(res.body as ErrorResponse).error}`);
        process.exitCode = 1;
        return;
      }
      const status = res.body as ProviderStatusResponse;
      const persistedNote = status.persisted === true
        ? ' (saved to ~/.mcpctl/config.json — survives restart)'
        : ' (live only — provider is not in config file, restart will undo)';
      deps.log(`${status.name}: disabled${persistedNote}`);
    });

  cmd
    .command('enable')
    .description('Re-enable a previously-disabled provider')
    .argument('<name>', 'Provider name (e.g. vllm-local)')
    .action(async (name: string) => {
      const res = await fetchJson<ProviderStatusResponse | ErrorResponse>(
        `${mcplocalUrl}/llm/providers/${encodeURIComponent(name)}/enable`,
        'POST',
      );
      if (res.status !== 200) {
        deps.log(`error: ${(res.body as ErrorResponse).error}`);
        process.exitCode = 1;
        return;
      }
      const status = res.body as ProviderStatusResponse;
      const persistedNote = status.persisted === true
        ? ' (saved to ~/.mcpctl/config.json)'
        : '';
      deps.log(`${status.name}: enabled${persistedNote} — next chat will start it`);
    });

  return cmd;
}
