#!/usr/bin/env node

const SECRET_KEY_PATTERN = /(token|password|secret|cookie|authorization|auth[_-]?header)/i;

function fail(message) {
  console.error(`error: ${message}`);
  process.exit(1);
}

function requiredEnv(name) {
  const value = process.env[name];
  if (!value || !value.trim()) fail(`${name} is required`);
  return value.trim();
}

function optionalEnv(name) {
  const value = process.env[name];
  return value && value.trim() ? value.trim() : "";
}

function parseArgs(argv) {
  const args = {};
  for (let i = 0; i < argv.length; i += 1) {
    const arg = argv[i];
    if (!arg.startsWith("--")) fail(`unexpected argument ${arg}`);
    const key = arg.slice(2);
    if (!key) fail("empty option name");
    const next = argv[i + 1];
    if (!next || next.startsWith("--")) fail(`--${key} requires a value`);
    args[key] = next;
    i += 1;
  }
  return args;
}

function readArg(args, key, { required = false, fallback = "" } = {}) {
  const value = args[key] ?? fallback;
  if (required && (!value || !String(value).trim())) {
    fail(`--${key} is required`);
  }
  return typeof value === "string" ? value.trim() : value;
}

function redact(value) {
  if (Array.isArray(value)) return value.map((entry) => redact(entry));
  if (value && typeof value === "object") {
    return Object.fromEntries(
      Object.entries(value).map(([key, entry]) => [
        key,
        SECRET_KEY_PATTERN.test(key) ? "[REDACTED]" : redact(entry),
      ])
    );
  }
  return value;
}

async function readJsonResponse(response) {
  const text = await response.text();
  if (!text) return {};
  try {
    return JSON.parse(text);
  } catch {
    return { raw: text };
  }
}

class HostClient {
  constructor(origin) {
    this.origin = origin.replace(/\/+$/, "");
    this.token = optionalEnv("OYSTERUN_DASHBOARD_TOKEN");
  }

  async ensureToken() {
    if (this.token) return;
    const username = requiredEnv("OYSTERUN_DASHBOARD_USERNAME");
    const password = requiredEnv("OYSTERUN_DASHBOARD_PASSWORD");
    const response = await fetch(`${this.origin}/auth/login`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ username, password }),
    });
    const data = await readJsonResponse(response);
    if (!response.ok) {
      fail(`login failed (${response.status}): ${JSON.stringify(redact(data))}`);
    }
    if (!data.token || typeof data.token !== "string") {
      fail("login response did not include a token");
    }
    this.token = data.token;
  }

  async request(method, path, body = undefined, { auth = true } = {}) {
    if (auth) await this.ensureToken();
    const headers = { "Content-Type": "application/json" };
    if (auth) headers.Authorization = `Bearer ${this.token}`;
    const response = await fetch(`${this.origin}${path}`, {
      method,
      headers,
      body: body === undefined ? undefined : JSON.stringify(body),
    });
    const data = await readJsonResponse(response);
    if (!response.ok) {
      fail(`${method} ${path} failed (${response.status}): ${JSON.stringify(redact(data))}`);
    }
    return data;
  }
}

function withOptionalSessionFields(args, body) {
  const sessionName = readArg(args, "session-name");
  const provider = readArg(args, "provider");
  const model = readArg(args, "model");
  if (sessionName) body.session_name = sessionName;
  if (provider) body.provider = provider;
  if (model) body.model = model;
  return body;
}

async function main() {
  const [command, ...rest] = process.argv.slice(2);
  if (!command) fail("command is required");
  const args = parseArgs(rest);
  const origin = requiredEnv("OYSTERUN_HOST_ORIGIN");
  const client = new HostClient(origin);

  let result;
  switch (command) {
    case "health":
      result = await client.request("GET", "/health", undefined, { auth: false });
      break;
    case "start": {
      const body = withOptionalSessionFields(args, {
        agent_id: readArg(args, "agent-id", { required: true }),
        agent_folder: readArg(args, "agent-folder", { required: true }),
      });
      result = await client.request("POST", "/session/start", body);
      break;
    }
    case "status": {
      const sessionId = readArg(args, "session-id", { required: true });
      result = await client.request(
        "GET",
        `/session/status?session_id=${encodeURIComponent(sessionId)}`
      );
      break;
    }
    case "restart": {
      result = await client.request("POST", "/session/restart", {
        session_id: readArg(args, "session-id", { required: true }),
      });
      break;
    }
    case "resume": {
      const sessionId = readArg(args, "session-id", { required: true });
      const body = withOptionalSessionFields(args, {
        session_id: sessionId,
        source_session_id: sessionId,
        agent_id: readArg(args, "agent-id", { required: true }),
        agent_folder: readArg(args, "agent-folder", { required: true }),
      });
      result = await client.request("POST", "/session/resume", body);
      break;
    }
    case "branch-resume": {
      const body = withOptionalSessionFields(args, {
        source_session_id: readArg(args, "source-session-id", { required: true }),
        agent_id: readArg(args, "agent-id", { required: true }),
        agent_folder: readArg(args, "agent-folder", { required: true }),
      });
      result = await client.request("POST", "/sessions/resume", body);
      break;
    }
    case "send": {
      result = await client.request("POST", "/session/send", {
        session_id: readArg(args, "session-id", { required: true }),
        text: readArg(args, "text", { required: true }),
        nickname: readArg(args, "nickname", { fallback: "Owner" }),
      });
      break;
    }
    case "create-loop": {
      result = await client.request("POST", "/session/loops", {
        session_id: readArg(args, "session-id", { required: true }),
        interval: readArg(args, "interval", { required: true }),
        command_text: readArg(args, "text", { required: true }),
        start_at: readArg(args, "start-at") || null,
        end_at: readArg(args, "end-at") || null,
        enabled: readArg(args, "enabled", { fallback: "true" }) !== "false",
      });
      break;
    }
    case "terminal-command": {
      result = await client.request("POST", "/session/terminal-command", {
        session_id: readArg(args, "session-id", { required: true }),
        command: readArg(args, "command", { required: true }),
        route: readArg(args, "route", { fallback: "project_local_session_ops_skill" }),
      });
      break;
    }
    case "recent": {
      const sessionId = readArg(args, "session-id", { required: true });
      const limitRaw = readArg(args, "limit", { fallback: "20" });
      const limit = Number.parseInt(limitRaw, 10);
      if (!Number.isInteger(limit) || limit < 1) fail("--limit must be a positive integer");
      const data = await client.request(
        "GET",
        `/session/history?session_id=${encodeURIComponent(sessionId)}`
      );
      const messages = Array.isArray(data.messages) ? data.messages.slice(-limit) : [];
      result = { ...data, messages, returned_count: messages.length };
      break;
    }
    default:
      fail(`unknown command ${command}`);
  }

  console.log(JSON.stringify(redact(result), null, 2));
}

main().catch((err) => fail(err?.message || String(err)));
