#!/usr/bin/env node

import process from "node:process";
import { pathToFileURL } from "node:url";

export const VERSION = "1.0.0";
const DEFAULT_BASE_URL = "https://rida.me";

export function usage() {
  return `Rida.me CLI ${VERSION}

Usage:
  rida api [--json] [--base-url URL]
  rida guide [--json] [--base-url URL]
  rida read PATH [--json] [--base-url URL]
  rida --help
  rida --version

Commands:
  api          Discover the Rida.me Public Content API.
  guide        Read the curated Rida.me agent guide.
  read PATH    Read one canonical Rida.me page as Markdown.
`;
}

export function parseArguments(argv) {
  const args = [...argv];
  let json = false;
  let baseUrl = DEFAULT_BASE_URL;
  const positional = [];

  for (let index = 0; index < args.length; index += 1) {
    const value = args[index];
    if (value === "--json") {
      json = true;
    } else if (value === "--base-url") {
      const next = args[index + 1];
      if (!next) throw new Error("--base-url requires a URL");
      baseUrl = next;
      index += 1;
    } else {
      positional.push(value);
    }
  }

  const parsedBase = new URL(baseUrl);
  if (
    parsedBase.protocol !== "https:" && parsedBase.hostname !== "localhost" &&
    parsedBase.hostname !== "127.0.0.1"
  ) {
    throw new Error("--base-url must use HTTPS, except for localhost testing");
  }
  return { positional, json, baseUrl: parsedBase.origin };
}

function write(stream, value) {
  stream.write(value.endsWith("\n") ? value : `${value}\n`);
}

async function requestJson(fetchImpl, url) {
  const response = await fetchImpl(url, {
    headers: {
      Accept: "application/json",
      "User-Agent": `rida-me-cli/${VERSION}`,
    },
  });
  let payload;
  try {
    payload = await response.json();
  } catch {
    throw new Error(`HTTP ${response.status}: response was not JSON`);
  }
  if (!response.ok) {
    const error = new Error(payload.detail || `HTTP ${response.status}`);
    error.problem = payload;
    throw error;
  }
  return payload;
}

export async function run(argv, options = {}) {
  const fetchImpl = options.fetchImpl || globalThis.fetch;
  const stdout = options.stdout || process.stdout;
  const stderr = options.stderr || process.stderr;

  let parsed;
  try {
    parsed = parseArguments(argv);
  } catch (error) {
    write(stderr, `INVALID_ARGUMENT: ${error.message}`);
    write(
      stderr,
      "Resolution: Run rida --help and correct the command arguments.",
    );
    return 2;
  }

  const [command, argument, ...extra] = parsed.positional;
  if (!command || command === "--help" || command === "help") {
    write(stdout, usage());
    return 0;
  }
  if (command === "--version" || command === "version") {
    write(stdout, VERSION);
    return 0;
  }
  if (extra.length > 0 || !["api", "guide", "read"].includes(command)) {
    write(stderr, `UNKNOWN_COMMAND: ${command}`);
    write(
      stderr,
      "Resolution: Run rida --help and choose a documented command.",
    );
    return 2;
  }
  if (
    command === "read" &&
    (!argument || !argument.startsWith("/") || argument.startsWith("//"))
  ) {
    write(
      stderr,
      "INVALID_PATH: read requires a canonical path beginning with one slash.",
    );
    write(stderr, "Resolution: Try rida read /about/.");
    return 2;
  }

  const endpoint = command === "api"
    ? "/api/v1"
    : command === "guide"
    ? "/api/v1/site-guide"
    : `/api/v1/page?path=${encodeURIComponent(argument)}`;

  try {
    const payload = await requestJson(
      fetchImpl,
      `${parsed.baseUrl}${endpoint}`,
    );
    if (parsed.json || command === "api") {
      write(stdout, JSON.stringify(payload, null, 2));
    } else {
      write(stdout, payload.data.markdown);
    }
    return 0;
  } catch (error) {
    const problem = error.problem;
    write(
      stderr,
      `${problem?.code || "REQUEST_FAILED"}: ${
        problem?.detail || error.message
      }`,
    );
    if (problem?.resolution) write(stderr, `Resolution: ${problem.resolution}`);
    return 1;
  }
}

if (
  process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href
) {
  process.exitCode = await run(process.argv.slice(2));
}
