import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import { homedir } from 'node:os';

/** Configuration for the mcplocal HTTP server. */
export interface HttpConfig {
  /** Port for the HTTP server (default: 3200) */
  httpPort: number;
  /** Host to bind to (default: 127.0.0.1) */
  httpHost: string;
  /** URL of the mcpd daemon (default: http://localhost:3100) */
  mcpdUrl: string;
  /** Bearer token for authenticating with mcpd */
  mcpdToken: string;
  /** Log level (default: info) */
  logLevel: 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
}

const DEFAULT_HTTP_PORT = 3200;
const DEFAULT_HTTP_HOST = '127.0.0.1';
const DEFAULT_MCPD_URL = 'http://localhost:3100';
const DEFAULT_LOG_LEVEL = 'info';

/**
 * Read the user's mcpctl credentials from ~/.mcpctl/credentials.
 * Returns the token if found, empty string otherwise.
 */
function loadUserToken(): string {
  try {
    const credPath = join(homedir(), '.mcpctl', 'credentials');
    if (!existsSync(credPath)) return '';
    const raw = readFileSync(credPath, 'utf-8');
    const parsed = JSON.parse(raw) as { token?: string };
    return parsed.token ?? '';
  } catch {
    return '';
  }
}

export interface LlmFileConfig {
  provider: string;
  model?: string;
  url?: string;
  binaryPath?: string;
}

/** Multi-provider entry from config file. */
export interface LlmProviderFileEntry {
  name: string;
  type: string;
  model?: string;
  url?: string;
  binaryPath?: string;
  tier?: 'fast' | 'heavy';
  /** vllm-managed: path to Python venv (e.g. "~/vllm_env") */
  venvPath?: string;
  /** vllm-managed: port for vLLM HTTP server */
  port?: number;
  /** vllm-managed: GPU memory utilization fraction (0.1–1.0) */
  gpuMemoryUtilization?: number;
  /** vllm-managed: max model context length */
  maxModelLen?: number;
  /** vllm-managed: minutes of idle before stopping vLLM */
  idleTimeoutMinutes?: number;
  /** vllm-managed: extra args for `vllm serve` */
  extraArgs?: string[];
}

export interface ProjectLlmOverride {
  model?: string;
  provider?: string;
}

interface LlmMultiFileConfig {
  providers: LlmProviderFileEntry[];
}

interface McpctlConfig {
  llm?: LlmFileConfig | LlmMultiFileConfig;
  projects?: Record<string, { llm?: ProjectLlmOverride }>;
}

/** Cached config for the process lifetime (reloaded on SIGHUP if needed). */
let cachedConfig: McpctlConfig | null = null;

function loadFullConfig(): McpctlConfig {
  if (cachedConfig) return cachedConfig;
  try {
    const configPath = join(homedir(), '.mcpctl', 'config.json');
    if (!existsSync(configPath)) return {};
    const raw = readFileSync(configPath, 'utf-8');
    cachedConfig = JSON.parse(raw) as McpctlConfig;
    return cachedConfig;
  } catch {
    return {};
  }
}

/** Type guard: is config the multi-provider format? */
function isMultiConfig(llm: LlmFileConfig | LlmMultiFileConfig): llm is LlmMultiFileConfig {
  return 'providers' in llm && Array.isArray((llm as LlmMultiFileConfig).providers);
}

/**
 * Load LLM configuration from ~/.mcpctl/config.json.
 * Returns undefined if no LLM section is configured.
 * @deprecated Use loadLlmProviders() for multi-provider support.
 */
export function loadLlmConfig(): LlmFileConfig | undefined {
  const config = loadFullConfig();
  if (!config.llm) return undefined;
  if (isMultiConfig(config.llm)) {
    // Multi-provider format — return first provider as legacy compat
    const first = config.llm.providers[0];
    if (!first) return undefined;
    const legacy: LlmFileConfig = { provider: first.type };
    if (first.model) legacy.model = first.model;
    if (first.url) legacy.url = first.url;
    if (first.binaryPath) legacy.binaryPath = first.binaryPath;
    return legacy;
  }
  if (!config.llm.provider || config.llm.provider === 'none') return undefined;
  return config.llm;
}

/**
 * Load LLM providers from ~/.mcpctl/config.json.
 * Normalizes both legacy single-provider and multi-provider formats.
 * Returns empty array if no LLM is configured.
 */
export function loadLlmProviders(): LlmProviderFileEntry[] {
  const config = loadFullConfig();
  if (!config.llm) return [];

  if (isMultiConfig(config.llm)) {
    return config.llm.providers.filter((p) => p.type !== 'none');
  }

  // Legacy single-provider format → normalize to one entry
  if (!config.llm.provider || config.llm.provider === 'none') return [];
  const entry: LlmProviderFileEntry = {
    name: config.llm.provider,
    type: config.llm.provider,
  };
  if (config.llm.model) entry.model = config.llm.model;
  if (config.llm.url) entry.url = config.llm.url;
  if (config.llm.binaryPath) entry.binaryPath = config.llm.binaryPath;
  return [entry];
}

/**
 * Load per-project LLM override from ~/.mcpctl/config.json.
 * Returns the project-specific model/provider override, or undefined.
 */
export function loadProjectLlmOverride(projectName: string): ProjectLlmOverride | undefined {
  const config = loadFullConfig();
  return config.projects?.[projectName]?.llm;
}

/** Reset cached config (for testing). */
export function resetConfigCache(): void {
  cachedConfig = null;
}

export function loadHttpConfig(env: Record<string, string | undefined> = process.env): HttpConfig {
  const portStr = env['MCPLOCAL_HTTP_PORT'];
  const port = portStr !== undefined ? parseInt(portStr, 10) : DEFAULT_HTTP_PORT;

  return {
    httpPort: Number.isFinite(port) ? port : DEFAULT_HTTP_PORT,
    httpHost: env['MCPLOCAL_HTTP_HOST'] ?? DEFAULT_HTTP_HOST,
    mcpdUrl: env['MCPLOCAL_MCPD_URL'] ?? DEFAULT_MCPD_URL,
    mcpdToken: env['MCPLOCAL_MCPD_TOKEN'] ?? loadUserToken(),
    logLevel: (env['MCPLOCAL_LOG_LEVEL'] as HttpConfig['logLevel'] | undefined) ?? DEFAULT_LOG_LEVEL,
  };
}
