/**
 * Centralised, typed configuration loaded from environment (.env at repo root).
 * Nothing else in the bot reads process.env directly.
 */
import "dotenv/config";

function req(name: string): string {
  const v = process.env[name];
  if (!v) throw new Error(`Missing required env var: ${name} (see .env.example)`);
  return v;
}

function opt(name: string, fallback = ""): string {
  return process.env[name] ?? fallback;
}

export type StreamBackend = "selfbot" | "novnc" | "screenshot" | "none";

export const config = {
  // --- Discord identity ---
  // Normal bot token/appId are OPTIONAL: when absent (and a selfbot token is
  // present) the app runs in userbot mode (one user account does voice +
  // Go-Live). When present, the legacy normal-bot + slash-command path runs.
  botToken: opt("DISCORD_BOT_TOKEN"),
  appId: opt("DISCORD_APP_ID"),
  guildId: req("DISCORD_GUILD_ID"),
  // Userbot auto-join: if set, the userbot joins this voice channel on startup;
  // if empty, it waits for a text command (e.g. "!자비스 join") to join.
  autoJoinChannelId: opt("DISCORD_VOICE_CHANNEL_ID"),
  // Optional text channel where the bot mirrors every voice turn it heard
  // (transcript + reply) so you can verify what it understood even when it
  // doesn't answer aloud.
  transcriptChannelId: opt("DISCORD_TRANSCRIPT_CHANNEL_ID"),

  // --- Python brain bridge ---
  bridgeUrl: opt("BRIDGE_URL", "http://127.0.0.1:8765"),

  // --- VNC screen broadcast ---
  // selfbot   = real live "Go Live" stream via a user (burner) account token
  // novnc     = post a noVNC web link the channel can open in a browser
  // screenshot= periodically upload VNC screenshots
  // none      = disable screen sharing
  streamBackend: (opt("STREAM_BACKEND", "selfbot") as StreamBackend),

  // x11grab source for the VNC display (TigerVNC runs the desktop on :1)
  vncDisplay: opt("VNC_DISPLAY", ":1"),
  vncResolution: opt("VNC_RESOLUTION", "1920x1080"),
  vncFramerate: parseInt(opt("VNC_FRAMERATE", "60"), 10),
  vncBitrateKbps: parseInt(opt("VNC_BITRATE_KBPS", "8000"), 10),

  // selfbot backend (ToS-risk; use a throwaway account token, never your main)
  selfbotToken: opt("DISCORD_SELFBOT_TOKEN"),
  // Account used for the Go-Live broadcast. In userbot mode the conversation and
  // the broadcast CANNOT share one account: Discord allows a single voice
  // presence per account, so the broadcaster's second session never connects
  // (confirmed: alone it goes Live, alongside the conversation it times out).
  // Set DISCORD_STREAM_TOKEN to a SECOND burner account dedicated to the
  // broadcast. Empty = reuse DISCORD_SELFBOT_TOKEN (correct for normal-bot mode,
  // where the conversation runs on the bot account, not the selfbot account).
  streamToken: opt("DISCORD_STREAM_TOKEN"),
  // How long to wait for the Go-Live stream WebRTC to actually reach
  // "connected" before declaring the broadcast failed (and tearing the local
  // ffmpeg pipeline down). Guards against reporting "live" when nothing reaches
  // Discord.
  streamReadyTimeoutMs: parseInt(opt("STREAM_READY_TIMEOUT_MS", "25000"), 10),
  // Use NVENC hardware encode + hw-accelerated decode for the stream (RTX 5050).
  streamHw: opt("STREAM_HW", "1") !== "0",
  // Capture desktop audio into the broadcast so the stream has sound. Pulls the
  // PipeWire/Pulse monitor of the default sink (what the desktop plays). Set
  // STREAM_AUDIO=0 to mute; STREAM_AUDIO_SOURCE overrides the capture source.
  streamAudio: opt("STREAM_AUDIO", "1") !== "0",
  streamAudioSource: opt("STREAM_AUDIO_SOURCE", "@DEFAULT_MONITOR@"),
  // Screen-share + browser mode. true = the bot may go Live (Go-Live screen
  // share of the VNC desktop) and drive the on-screen browser for real-time
  // info. false = no screen share; use voice + API/MCP tools for info only.
  screenBrowser: opt("STREAM_BROWSER", "true") !== "false",

  // novnc backend
  novncUrl: opt("NOVNC_URL", ""),

  // screenshot backend
  screenshotIntervalSec: parseInt(opt("SCREENSHOT_INTERVAL_SEC", "5"), 10),

  // --- Voice behaviour ---
  // Min/max captured utterance bounds (ms) before forwarding to the brain.
  silenceMs: parseInt(opt("VOICE_SILENCE_MS", "800"), 10),
};

export type AppConfig = typeof config;
