#!/usr/bin/env node
import { spawn, execSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
import { existsSync } from "node:fs";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const DEFAULT_PORT = 25947;

const envPort = process.env["PORT"];
const portDefault = envPort !== undefined ? Number(envPort) : DEFAULT_PORT;

// Simple argument parsing
const args = process.argv.slice(2);
let port = portDefault;
let open = true;
let configDir: string | undefined;
let providersJson: string | undefined;

for (let i = 0; i < args.length; i++) {
  const arg = args[i] ?? "";
  switch (arg) {
    case "--port":
    case "-p":
      port = Number(args[i + 1]);
      i++;
      break;
    case "--no-open":
      open = false;
      break;
    case "--open":
      open = true;
      break;
    case "--config-dir":
      configDir = args[i + 1];
      i++;
      break;
    case "--providers":
      providersJson = args[i + 1];
      i++;
      break;
    default:
      break;
  }
}

/**
 * Check if a port is in use and kill the process using it
 */
function killProcessOnPort(targetPort: number): void {
  const platform = process.platform;

  try {
    let pids: number[] = [];

    if (platform === "win32") {
      // Windows: use netstat to find PID, then taskkill
      const output = execSync(`netstat -ano | findstr :${targetPort}`, {
        encoding: "utf8",
        timeout: 5000,
      });
      const lines = output.trim().split("\n");
      for (const line of lines) {
        const parts = line.trim().split(/\s+/);
        if (parts.length >= 5) {
          const localAddress = parts[1] ?? "";
          const pidStr = parts[4] ?? "";
          if (localAddress !== "" && pidStr !== "" && localAddress.includes(`:${targetPort}`)) {
            const pid = parseInt(pidStr, 10);
            if (!isNaN(pid) && pid > 0) {
              pids.push(pid);
            }
          }
        }
      }
      // Remove duplicates
      pids = [...new Set(pids)];

      for (const pid of pids) {
        try {
          console.log(`Killing process ${pid} on port ${port}...`);
          execSync(`taskkill /PID ${pid} /F`, { encoding: "utf8", timeout: 5000 });
        } catch {
          // Process may have already exited
        }
      }
    } else {
      // Unix-like: use lsof
      const output = execSync(`lsof -ti:${targetPort}`, { encoding: "utf8", timeout: 5000 });
      const lines = output.trim().split("\n");
      for (const line of lines) {
        const pid = parseInt(line.trim(), 10);
        if (!isNaN(pid) && pid > 0) {
          pids.push(pid);
        }
      }
      // Remove duplicates
      pids = [...new Set(pids)];

      for (const pid of pids) {
        try {
          console.log(`Killing process ${pid} on port ${targetPort}...`);
          execSync(`kill -9 ${pid}`, { encoding: "utf8", timeout: 5000 });
        } catch {
          // Process may have already exited
        }
      }
    }
  } catch {
    // No process found on port, which is fine
  }
}

process.env["PORT"] = String(port);

// Kill any existing process on the port
killProcessOnPort(port);

const url = `http://localhost:${port}`;

console.log(`Server running at ${url}`);
console.log(`   Proxy: ${url}/proxy`);
console.log(``);
console.log(`Route AI coding tools through the proxy:`);
console.log(`   Claude Code:  ANTHROPIC_BASE_URL=${url}/proxy claude`);
console.log(`   OpenCode:     LLM_BASE_URL=${url}/proxy opencode`);
console.log(`   MiMo Code:    OPENAI_BASE_URL=${url}/proxy mimo`);
console.log(`   Direct HTTP:  curl ${url}/proxy/v1/messages -d '{"model":"...","messages":[...]}'`);
console.log(``);
console.log(`Routing environment variables:`);
console.log(`   ROUTES            JSON map of model prefix -> upstream URL`);
console.log(`   DEFAULT_UPSTREAM  Fallback upstream for unmatched models`);
console.log(
  `   Example: ROUTES='{"claude-":"https://api.anthropic.com","MiniMax":"https://api.minimaxi.com/anthropic"}'`,
);

const openBrowser = (targetUrl: string): void => {
  let command: string[] | undefined;
  // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
  switch (process.platform) {
    case "darwin":
      command = ["open", targetUrl];
      break;
    case "linux":
      command = ["xdg-open", targetUrl];
      break;
    case "win32":
      command = ["cmd", "/c", "start", targetUrl];
      break;
    default:
      // Unsupported platform - do nothing
      break;
  }
  if (command === undefined) return;
  const [bin, ...cmdArgs] = command;
  if (bin === undefined) return;
  spawn(bin, cmdArgs, { stdio: "ignore", detached: true });
};

if (open) {
  openBrowser(url);
}

// Compute server path
const outputDir = __dirname;
const serverPath = join(outputDir, "../.output/server/index.mjs");

// Start server with node
const serverEnv = { ...process.env };
if (configDir !== undefined) {
  // Convert MSYS/Git Bash path like /c/Users/... to Windows absolute path
  let resolvedPath = join(configDir, "config.json");
  // Convert /c/... to C:\... format
  if (resolvedPath.startsWith("\\c\\")) {
    resolvedPath = "C:" + resolvedPath;
  }
  serverEnv["LLM_INSPECTOR_CONFIG_PATH"] = resolvedPath;
}
if (providersJson !== undefined) {
  serverEnv["LLM_INSPECTOR_PROVIDERS_JSON"] = providersJson;
}
const serverProcess = spawn(process.execPath, [serverPath], {
  stdio: ["ignore", "inherit", "inherit"],
  detached: true,
  env: serverEnv,
});

serverProcess.unref();
