import { z } from 'zod';

const ConfigSchema = z.object({
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  PORT: z.coerce.number().int().positive().default(3000),
  HOST: z.string().default('0.0.0.0'),
  DATABASE_URL: z.string().url(),
  LLAMA_SWAP_URL: z.string().url(),
  PROJECT_ROOT_WHITELIST: z.string().default('/opt'),
  BOOTSTRAP_ROOT: z.string().default('/opt/projects'),
  DEFAULT_MODEL: z.string().default('sam-desktop/qwen3.6-35b-a3b'),
  LOG_LEVEL: z.string().default('info'),
  // v1.11.8: SearXNG JSON endpoint for web_search / web_fetch tools.
  // Defaults to the internal Tailscale Fathom URL (bypasses Authelia).
  // The public search.indifferentketchup.com URL would 302 to auth and
  // is unusable from the server context — keep the internal one.
  SEARXNG_URL: z.string().url().default('http://100.114.205.53:8888'),
  GITEA_BASE_URL: z.string().url().default('https://git.indifferentketchup.com'),
  GITEA_USER: z.string().default('indifferentketchup'),
  GITEA_TOKEN: z.string().optional(),
  // v1.15.0-mcp-multi: path to the MCP config JSON file. Default /data/mcp.json
  // (bind-mounted alongside AGENTS.md). File missing = no MCP (opt-in).
  MCP_CONFIG_PATH: z.string().optional(),
  // v2.0.5: cheaper model for titles, summaries, labeling. Falls back to
  // session model (auto_name) or DEFAULT_MODEL when unset.
  FAST_MODEL: z.string().optional(),
  TASK_MODEL_URL: z.string().url().optional(),
  // vDeepSeek: DeepSeek API key for direct API access. When set, models
  // with IDs starting with 'deepseek-' route through DeepSeek's API instead
  // of llama-swap. Defaults to empty (DeepSeek routing disabled).
  DEEPSEEK_API_KEY: z.string().optional(),
  // Optional base URL override for DeepSeek API. Defaults to api.deepseek.com.
  DEEPSEEK_BASE_URL: z.string().url().default('https://api.deepseek.com'),
  // Beta endpoint for experimental features (strict tools, prefix completion, etc.).
  // Defaults to api.deepseek.com/beta. When set, deepseek calls with tools or
  // prefix content route through this endpoint.
  DEEPSEEK_BETA_BASE_URL: z.string().url().default('https://api.deepseek.com/beta'),
  // Hosted Anthropic Claude. When set, models with provider id "anthropic"
  // (or bare "claude-*" ids) route through the Anthropic Messages API via
  // @ai-sdk/anthropic instead of llama-swap. Unset = Claude routing disabled.
  ANTHROPIC_API_KEY: z.string().optional(),
  ANTHROPIC_BASE_URL: z.string().url().optional(),
  // vWhale hooks: path to hooks JSON config file. Missing file = no hooks.
  HOOKS_CONFIG_PATH: z.string().default('/data/hooks.json'),
  // vMultiProvider: path to the local providers config JSON file. Missing file
  // = legacy synthesis from LLAMA_SWAP_URL.
  LLAMA_PROVIDERS_PATH: z.string().optional(),
  BOOCONTROL_URL: z.string().url().optional(),
});

export type Config = z.infer<typeof ConfigSchema>;

let cached: Config | null = null;

export function loadConfig(): Config {
  if (cached) return cached;
  const parsed = ConfigSchema.safeParse(process.env);
  if (!parsed.success) {
    console.error('Invalid environment configuration:');
    console.error(parsed.error.flatten().fieldErrors);
    process.exit(1);
  }
  cached = parsed.data;
  return cached;
}
