import type { ToolFunc } from "../types.ts";

type transformer = (arg: string) => any;
interface IConfig {
  multiTransform?: boolean;
  args?: string[];
}

export async function call<T extends unknown[]>(
  tool: ToolFunc<T>,
  transforms: transformer[],
  conf?: IConfig,
) {
  const config: IConfig = {};

  if (typeof conf === "object") {
    Object.assign(config, conf);
  }

  let args = config.args || Deno.args;
  if (!args.length && transforms.length) {
    config.multiTransform ? args = transforms.map(() => "") : args = [""];
  }

  const shouldPair = transforms.length === args.length;
  const multiTransform = config.multiTransform ||
    !shouldPair && transforms.length > 1;

  const transformedArgs = args.map((arg, i) => {
    if (shouldPair) return transforms[i](arg);
    if (multiTransform) return transforms.reduce((a, b) => b(a), arg);
    return transforms[0] ? transforms[0](arg) : arg;
  });

  await tool(...transformedArgs as T);
}

type prompt = [string, (v?: string) => boolean] | string;

export async function callWithArgPrompt<T extends unknown[]>(
  tool: ToolFunc<T>,
  prompts: prompt[],
  args?: string[],
) {
  function buildPromptTransform(p: prompt): transformer {
    let validation = (v?: string) => !!v;
    let pText = p as string;

    if (Array.isArray(p)) {
      [pText, validation] = p;
    }

    return (a: string) => {
      while (!validation(a)) {
        a = prompt(pText) || "";
      }
    };
  }

  await call(tool, prompts.map(buildPromptTransform), { args });
}
