#!/usr/bin/env node
import { readFileSync } from 'node:fs';
import type { FastifyInstance } from 'fastify';
import type { ProxyConfig, UpstreamConfig } from './types.js';
import { McpRouter } from './router.js';
import { StdioProxyServer } from './server.js';
import { StdioUpstream } from './upstream/stdio.js';
import { HttpUpstream } from './upstream/http.js';
import { createHttpServer } from './http/server.js';
import { loadHttpConfig, loadLlmProviders } from './http/config.js';
import type { HttpConfig } from './http/config.js';
import { createProvidersFromConfig } from './llm-config.js';
import { createSecretStore } from '@mcpctl/shared';
import type { ProviderRegistry } from './providers/registry.js';
import { startWatchers, stopWatchers, reloadStages } from './proxymodel/watcher.js';

interface ParsedArgs {
  configPath: string | undefined;
  upstreams: string[];
  noHttp: boolean;
}

function parseArgs(argv: string[]): ParsedArgs {
  let configPath: string | undefined;
  const upstreams: string[] = [];
  let noHttp = false;

  for (let i = 2; i < argv.length; i++) {
    const arg = argv[i];
    if (arg === '--config' && i + 1 < argv.length) {
      configPath = argv[++i];
    } else if (arg?.startsWith('--config=')) {
      configPath = arg.slice('--config='.length);
    } else if (arg === '--upstream' && i + 1 < argv.length) {
      upstreams.push(argv[++i]!);
    } else if (arg?.startsWith('--upstream=')) {
      upstreams.push(arg.slice('--upstream='.length));
    } else if (arg === '--no-http') {
      noHttp = true;
    }
  }
  return { configPath, upstreams, noHttp };
}

function loadConfig(configPath: string): ProxyConfig {
  const raw = readFileSync(configPath, 'utf-8');
  return JSON.parse(raw) as ProxyConfig;
}

function createUpstream(config: UpstreamConfig) {
  if (config.transport === 'stdio') {
    return new StdioUpstream(config);
  }
  return new HttpUpstream(config);
}

export interface MainResult {
  router: McpRouter;
  server: StdioProxyServer;
  httpServer: FastifyInstance | undefined;
  httpConfig: HttpConfig;
  providerRegistry: ProviderRegistry;
}

export async function main(argv: string[] = process.argv): Promise<MainResult> {
  const args = parseArgs(argv);
  const httpConfig = loadHttpConfig();

  // Load LLM providers from user config + secret store
  const llmEntries = loadLlmProviders();
  const secretStore = await createSecretStore();
  const providerRegistry = await createProvidersFromConfig(llmEntries, secretStore);
  if (providerRegistry.hasTierConfig()) {
    const fast = providerRegistry.getTierProviders('fast');
    const heavy = providerRegistry.getTierProviders('heavy');
    process.stderr.write(`LLM providers: fast=[${fast.join(',')}] heavy=[${heavy.join(',')}]\n`);
  } else {
    const activeLlm = providerRegistry.getActive();
    if (activeLlm) {
      process.stderr.write(`LLM provider: ${activeLlm.name}\n`);
    }
  }

  let upstreamConfigs: UpstreamConfig[] = [];

  if (args.configPath) {
    const config = loadConfig(args.configPath);
    upstreamConfigs = config.upstreams;
  }

  // --upstream flags: "name:command arg1 arg2" for STDIO or "name:http://url" for HTTP
  for (const spec of args.upstreams) {
    const colonIdx = spec.indexOf(':');
    if (colonIdx === -1) continue;
    const name = spec.slice(0, colonIdx);
    const rest = spec.slice(colonIdx + 1);

    if (rest.startsWith('http://') || rest.startsWith('https://')) {
      upstreamConfigs.push({
        serverId: name,
        name,
        transport: 'streamable-http',
        url: rest,
      });
    } else {
      const parts = rest.split(' ').filter(Boolean);
      const command = parts[0];
      if (!command) continue;
      const config: UpstreamConfig = {
        serverId: name,
        name,
        transport: 'stdio',
        command,
        args: parts.slice(1),
      };
      upstreamConfigs.push(config);
    }
  }

  const router = new McpRouter();

  for (const config of upstreamConfigs) {
    const upstream = createUpstream(config);
    if (upstream instanceof StdioUpstream) {
      await upstream.start();
    }
    router.addUpstream(upstream);
  }

  // Start stdio proxy server
  const server = new StdioProxyServer(router);
  server.start();
  process.stderr.write(`mcpctl-proxy started with ${upstreamConfigs.length} upstream(s)\n`);

  // Start HTTP server unless disabled
  let httpServer: FastifyInstance | undefined;
  if (!args.noHttp) {
    httpServer = await createHttpServer(httpConfig, { router, providerRegistry });
    await httpServer.listen({ port: httpConfig.httpPort, host: httpConfig.httpHost });
    process.stderr.write(`mcpctl-proxy HTTP server listening on ${httpConfig.httpHost}:${httpConfig.httpPort}\n`);
  }

  // Load custom stages and start file watchers for hot-reload
  await reloadStages();
  startWatchers();

  // Graceful shutdown
  let shuttingDown = false;
  const shutdown = async () => {
    if (shuttingDown) return;
    shuttingDown = true;

    stopWatchers();
    providerRegistry.disposeAll();
    server.stop();
    if (httpServer) {
      await httpServer.close();
    }
    await router.closeAll();
    process.exit(0);
  };

  process.on('SIGTERM', () => void shutdown());
  process.on('SIGINT', () => void shutdown());

  return { router, server, httpServer, httpConfig, providerRegistry };
}

// Run when executed directly
const isMain =
  process.argv[1]?.endsWith('main.js') ||
  process.argv[1]?.endsWith('main.ts') ||
  process.argv[1]?.endsWith('mcpctl-local');
if (isMain) {
  main().catch((err) => {
    process.stderr.write(`Fatal: ${err}\n`);
    process.exit(1);
  });
}
