#!/usr/bin/env node

// src/bin/cli.ts
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "fs";
import { dirname, resolve } from "path";
var OVERRIDES_PATH = ".epilot/sdk-overrides.json";
var usage = () => {
  console.log(`
epilot-sdk CLI

Commands:
  override                              Apply all overrides from ${OVERRIDES_PATH}
  override <api-name> <spec-path>       Override or register a single API
  typegen                               Regenerate types from current specs

Examples:
  npx epilot-sdk override
  npx epilot-sdk override entity ./my-entity-spec.yaml
  npx epilot-sdk override myNewApi ./specs/my-new-api.yaml
  npx epilot-sdk typegen
`);
};
var isYamlFile = (filePath) => filePath.endsWith(".yaml") || filePath.endsWith(".yml");
var parseYaml = async (content) => {
  const { load } = await import("../js-yaml-DLCVPJ7G.js");
  return load(content);
};
var readOverrides = () => {
  const filePath = resolve(process.cwd(), OVERRIDES_PATH);
  if (!existsSync(filePath)) return {};
  return JSON.parse(readFileSync(filePath, "utf-8"));
};
var writeOverrides = (overrides) => {
  const filePath = resolve(process.cwd(), OVERRIDES_PATH);
  mkdirSync(dirname(filePath), { recursive: true });
  writeFileSync(filePath, `${JSON.stringify(overrides, null, 2)}
`);
};
var fetchSpec = async (specPath) => {
  if (specPath.startsWith("http://") || specPath.startsWith("https://")) {
    const response = await fetch(specPath);
    if (!response.ok) throw new Error(`Failed to fetch ${specPath}: ${response.statusText}`);
    return await response.text();
  }
  const absolutePath = resolve(process.cwd(), specPath);
  if (!existsSync(absolutePath)) throw new Error(`Spec file not found: ${absolutePath}`);
  return readFileSync(absolutePath, "utf-8");
};
var parseSpec = async (content, specPath) => {
  if (isYamlFile(specPath)) {
    return await parseYaml(content);
  }
  return JSON.parse(content);
};
var toKebabCase = (name) => name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
var compactifyParam = (p) => {
  if (p.$ref) {
    return [p.$ref.split("/").pop()];
  }
  const loc = { path: "p", query: "q", header: "h", cookie: "c" }[p.in] || "q";
  const cp = [p.name, loc];
  if (p.required) cp.push(true);
  if (p.style) {
    if (!p.required) cp.push(false);
    cp.push(p.style);
  }
  if (p.explode !== void 0) {
    cp.push(p.explode);
  }
  return cp;
};
var compactifySpec = (spec) => {
  const server = spec.servers?.[0]?.url || "";
  const openapiVersion = spec.openapi || "3.0.2";
  const paths = spec.paths || {};
  const ops = [];
  const pathParams = {};
  for (const [path, methods] of Object.entries(paths)) {
    if (methods.parameters) {
      pathParams[path] = methods.parameters.map(compactifyParam);
    }
    for (const [method, rawOp] of Object.entries(methods)) {
      if (method === "parameters") continue;
      const op = rawOp;
      if (typeof op !== "object" || !op.operationId) continue;
      const params = op.parameters || [];
      const compactParams = params.map(compactifyParam);
      const hasBody = op.requestBody ? 1 : 0;
      const entry = [op.operationId, method, path];
      if (compactParams.length > 0 || hasBody) entry.push(compactParams.length > 0 ? compactParams : void 0);
      if (hasBody) entry.push(1);
      ops.push(entry);
    }
  }
  const componentParams = spec.components?.parameters;
  const result = { s: server, o: ops };
  if (openapiVersion !== "3.0.2") result.v = openapiVersion;
  if (componentParams && Object.keys(componentParams).length > 0) {
    const cpObj = {};
    for (const [refName, p] of Object.entries(componentParams)) {
      cpObj[refName] = compactifyParam(p);
    }
    result.cp = cpObj;
  }
  if (Object.keys(pathParams).length > 0) {
    result.pp = pathParams;
  }
  return result;
};
var findSdkRoot = () => {
  try {
    let dir = process.cwd();
    while (true) {
      const candidate = resolve(dir, "node_modules/@epilot/sdk");
      if (existsSync(candidate)) return candidate;
      const parent = dirname(dir);
      if (parent === dir) break;
      dir = parent;
    }
  } catch {
  }
  return null;
};
var patchRuntimeChunk = (distDir, kebabName, compactSpec) => {
  if (!existsSync(distDir)) return;
  const runtimeFnName = `require_${kebabName.replace(/-/g, "_")}_runtime`;
  const files = readdirSync(distDir);
  let patched = false;
  for (const file of files) {
    if (!file.endsWith(".js") && !file.endsWith(".cjs")) continue;
    if (file.startsWith("apis/")) continue;
    const filePath = resolve(distDir, file);
    const content = readFileSync(filePath, "utf-8");
    if (!content.includes(runtimeFnName)) continue;
    const newContent = content.replace(
      /module\.exports\s*=\s*\{.*\};/,
      `module.exports = ${JSON.stringify(compactSpec)};`
    );
    if (newContent !== content) {
      writeFileSync(filePath, newContent);
      console.log(`    -> patched ${file} (runtime chunk)`);
      patched = true;
    }
  }
  if (!patched) {
    console.log(`    (no runtime chunk found to patch for ${kebabName})`);
  }
};
var overrideCmd = async (args) => {
  if (args.length === 2) {
    const [apiName, specPath] = args;
    const overrides2 = readOverrides();
    overrides2[apiName] = specPath;
    writeOverrides(overrides2);
    console.log(`Saved override: ${apiName} -> ${specPath}`);
  }
  const overrides = readOverrides();
  const entries = Object.entries(overrides);
  if (entries.length === 0) {
    console.log(`No overrides found in ${OVERRIDES_PATH}`);
    console.log(`Usage: npx epilot-sdk override <api-name> <spec-path>`);
    return;
  }
  const sdkRoot = findSdkRoot();
  if (!sdkRoot) {
    console.error("Could not find @epilot/sdk in node_modules. Is it installed?");
    process.exit(1);
  }
  const defsDir = resolve(sdkRoot, "definitions");
  const distDir = resolve(sdkRoot, "dist");
  if (!existsSync(defsDir)) {
    mkdirSync(defsDir, { recursive: true });
  }
  console.log(`Applying ${entries.length} override(s)...`);
  for (const [apiName, specPath] of entries) {
    try {
      const kebabName = toKebabCase(apiName);
      console.log(`  ${apiName} (${kebabName}): ${specPath}`);
      const rawContent = await fetchSpec(specPath);
      const spec = await parseSpec(rawContent, specPath);
      const fullSpecDest = resolve(defsDir, `${kebabName}.json`);
      writeFileSync(fullSpecDest, `${JSON.stringify(spec, null, 2)}
`);
      console.log(`    -> definitions/${kebabName}.json`);
      const compactSpec = compactifySpec(spec);
      const runtimeDest = resolve(defsDir, `${kebabName}-runtime.json`);
      writeFileSync(runtimeDest, JSON.stringify(compactSpec));
      console.log(`    -> definitions/${kebabName}-runtime.json`);
      patchRuntimeChunk(distDir, kebabName, compactSpec);
    } catch (err) {
      console.error(`    Error: ${err.message}`);
    }
  }
  console.log("");
  await typegenCmd();
};
var typegenCmd = async () => {
  try {
    const { execSync } = await import("child_process");
    const sdkRoot = findSdkRoot();
    if (!sdkRoot) {
      console.error("Could not find @epilot/sdk in node_modules. Is it installed?");
      process.exit(1);
    }
    const overrides = readOverrides();
    const entries = Object.entries(overrides);
    if (entries.length === 0) {
      console.log('No overrides found. Run "npx epilot-sdk override" first.');
      return;
    }
    const defsDir = resolve(sdkRoot, "definitions");
    const distDir = resolve(sdkRoot, "dist");
    for (const [apiName] of entries) {
      const kebabName = toKebabCase(apiName);
      const specPath = resolve(defsDir, `${kebabName}.json`);
      if (!existsSync(specPath)) {
        console.error(`  No spec found for ${apiName}. Run "npx epilot-sdk override" first.`);
        continue;
      }
      console.log(`Generating types for ${apiName}...`);
      try {
        const dtsPath = resolve(distDir, `apis/${kebabName}.d.ts`);
        const dctsPath = resolve(distDir, `apis/${kebabName}.d.cts`);
        const generatedTypes = execSync(`npx openapi-client-axios-typegen ${specPath} --client`, {
          stdio: "pipe",
          encoding: "utf-8"
        });
        const moduleExports = [
          "",
          `import type { ApiHandle } from '../types';`,
          `export { authorize } from '../authorize';`,
          `export type { TokenArg } from '../authorize';`,
          `export type { OpenAPIClient } from 'openapi-client-axios';`,
          "",
          "/** Get the cached singleton client (lazy-initialized on first call) */",
          "declare const getClient: () => Client;",
          "/** Create a fresh client instance (not cached) */",
          "declare const createClient: () => Client;",
          "/**",
          " * API handle \u2014 also exposes operations directly:",
          ` * \`${apiName}.getEntity(...)\` calls forwarded to lazy singleton`,
          " */",
          `declare const ${apiName}: ApiHandle<Client>;`,
          "",
          `export { getClient, createClient, ${apiName} };`
        ].join("\n");
        const fullContent = `${generatedTypes.trimEnd()}
${moduleExports}
`;
        writeFileSync(dtsPath, fullContent);
        console.log(`  -> dist/apis/${kebabName}.d.ts`);
        writeFileSync(dctsPath, fullContent);
        console.log(`  -> dist/apis/${kebabName}.d.cts`);
      } catch (err) {
        console.error(`  Error generating types for ${apiName}: ${err.message}`);
      }
    }
    console.log("\nDone.");
  } catch (err) {
    console.error(`Error: ${err.message}`);
  }
};
var main = async () => {
  const [, , command, ...args] = process.argv;
  switch (command) {
    case "override":
      await overrideCmd(args);
      break;
    case "typegen":
      await typegenCmd();
      break;
    case "help":
    case "--help":
    case "-h":
      usage();
      break;
    default:
      usage();
      process.exit(1);
  }
};
main().catch((err) => {
  console.error(err);
  process.exit(1);
});
