/**
 * Single parameter definition for a worksheet function.
 *
 * @property name      Formal parameter name (used by docs & IDE hints).
 * @property required  `true` if caller must supply a value.
 * @property variadic  Marks the *last* parameter as “collect the rest”.
 * @property default   Default value substituted when the arg is omitted
 *                     (Excel‐style blank).  Untyped because it varies.
 */
export interface FunctionArg {
  name: string;
  required: boolean;
  variadic?: boolean;
  default?: unknown;
}

/**
 * Metadata for an Excel worksheet function.
 *
 * @property args            Ordered list of parameter specs.
 * @property description     Human-readable summary for help/tooltips.
 * @property returnType      Textual type label (“Number”, “Any”, …).
 * @property customArgParser Set to `true` if this function needs a
 *                           bespoke argument-normalisation pass.
 * @property variadic        Convenience flag: whole function is variadic
 *                           (alias for last arg having `variadic:true`).
 */
export interface FunctionMeta {
  args: FunctionArg[];
  description: string;
  returnType: string;
  customArgParser?: boolean;
  variadic?: boolean;
}

/**
 * Core catalogue of functions the parser knows about.
 * Keys are *uppercase* Excel names; values are {@link FunctionMeta}.
 * Kept `as const` to preserve literal key names in `TypeMap`.
 */
export const typeMap: Record<string, FunctionMeta> = {
  IF: {
    args: [
      { name: "condition", required: true },
      { name: "trueResult", required: true },
      { name: "falseResult", required: false },
    ],
    description:
      "Evaluates a condition and returns different results based on true/false.",
    returnType: "Any",
  },
  LAMBDA: {
    args: [
      { name: "parameters", required: true, variadic: true },
      { name: "body", required: true },
    ],
    customArgParser: true,
    description: "Creates a reusable function with given parameters.",
    returnType: "Function",
  },
  LET: {
    args: [
      { name: "name", required: true, variadic: true },
      { name: "value", required: true, variadic: true },
      { name: "calculation", required: true },
    ],
    customArgParser: true,
    description:
      "Assigns names to calculation results and returns a final expression.",
    returnType: "Any",
  },
  SUM: {
    args: [{ name: "values", required: true, variadic: true }],
    description: "Adds all numbers (or arrays of numbers) together.",
    returnType: "Number",
  },
  AND: {
    args: [{ name: "conditions", required: true, variadic: true }],
    variadic: true,
    description: "Returns TRUE if all conditions are true.",
    returnType: "Boolean",
  },

  OR: {
    args: [{ name: "conditions", required: true, variadic: true }],
    description: "Returns TRUE if at least one condition is true.",
    returnType: "Boolean",
  },
  NOT: {
    args: [{ name: "condition", required: true }],
    description: "Reverses the logical value of its argument.",
    returnType: "Boolean",
  },
  INDEX: {
    args: [
      { name: "array", required: true },
      { name: "rowNum", required: false },
      { name: "columnNum", required: false },
      { name: "areaNum", required: false },
    ],
    description:
      "Returns the value of an element in a table or an array, selected by the row and column number.",
    returnType: "Any",
  },
  INDIRECT: {
    args: [
      { name: "refText", required: true },
      { name: "a1Style", required: false, default: true },
    ],
    description:
      "Returns a reference specified by a text string. Optionally controls whether the reference is in A1 or R1C1 style.",
    returnType: "Reference",
  },

  MATCH: {
    args: [
      { name: "lookupValue", required: true },
      { name: "lookupArray", required: true },
      { name: "matchType", required: false, default: 1 },
    ],
    description:
      "Searches for a specified item in a range of cells and returns its relative position.",
    returnType: "Number",
  },
  IFERROR: {
    args: [
      { name: "value", required: true },
      { name: "valueIfError", required: true },
    ],
    description:
      "Returns a specified value if the formula results in an error; otherwise returns the formula result.",
    returnType: "Any",
  },
  VLOOKUP: {
    args: [
      { name: "lookupValue", required: true },
      { name: "tableArray", required: true },
      { name: "colIndexNum", required: true },
      { name: "rangeLookup", required: false, default: true },
    ],
    description:
      "Searches for a value in the first column of a table array and returns a value in the same row from the specified column.",
    returnType: "Any",
  },
  XLOOKUP: {
    args: [
      { name: "lookupValue", required: true },
      { name: "lookupArray", required: true },
      { name: "returnArray", required: true },
      { name: "ifNotFound", required: false, default: "#N/A" },
      { name: "matchMode", required: false, default: 0 },
      { name: "searchMode", required: false, default: 1 },
    ],
    description:
      "Searches a range or array and returns an item corresponding to the first match it finds.",
    returnType: "Any",
  },
  CHOOSE: {
    args: [
      { name: "indexNum", required: true },
      { name: "values", required: true, variadic: true },
    ],
    description:
      "Returns a value from a list of values based on the given index number.",
    returnType: "Any",
  },
  ISERROR: {
    args: [{ name: "value", required: true }],
    description:
      "Checks whether a value is any Excel error and returns TRUE or FALSE.",
    returnType: "Boolean",
  },
  ISBLANK: {
    args: [{ name: "value", required: true }],
    description: "Returns TRUE if the value is blank (empty), FALSE otherwise.",
    returnType: "Boolean",
  },

  ISNUMBER: {
    args: [{ name: "value", required: true }],
    description:
      "Checks whether a value is a number and returns TRUE or FALSE.",
    returnType: "Boolean",
  },

  TEXT: {
    args: [
      { name: "value", required: true },
      { name: "formatText", required: true },
    ],
    description:
      "Formats a number and converts it to text according to a specified format string.",
    returnType: "String",
  },
  TODAY: {
    args: [],
    description: "Returns the current date as a serial number.",
    returnType: "Date",
  },

  VALUE: {
    args: [{ name: "text", required: true }],
    description:
      "Converts a text string that represents a number to a numeric value.",
    returnType: "Number",
  },
  ROUND: {
    args: [
      { name: "number", required: true },
      { name: "numDigits", required: true },
    ],
    description: "Rounds a number to a specified number of digits.",
    returnType: "Number",
  },
  ROUNDUP: {
    args: [
      { name: "number", required: true },
      { name: "numDigits", required: true },
    ],
    description:
      "Rounds a number up, away from zero, to a specified number of digits.",
    returnType: "Number",
  },
  ROUNDDOWN: {
    args: [
      { name: "number", required: true },
      { name: "numDigits", required: true },
    ],
    description:
      "Rounds a number down, toward zero, to a specified number of digits.",
    returnType: "Number",
  },
  LEFT: {
    args: [
      { name: "text", required: true },
      { name: "numChars", required: false, default: 1 },
    ],
    description:
      "Returns the first character or characters in a text string, based on the number of characters you specify.",
    returnType: "String",
  },
  RIGHT: {
    args: [
      { name: "text", required: true },
      { name: "numChars", required: false, default: 1 },
    ],
    description:
      "Returns the last character or characters in a text string, based on the number of characters you specify.",
    returnType: "String",
  },
  MID: {
    args: [
      { name: "text", required: true },
      { name: "startNum", required: true },
      { name: "numChars", required: true },
    ],
    description:
      "Returns a specific number of characters from a text string, starting at the position you specify.",
    returnType: "String",
  },
  LEN: {
    args: [{ name: "text", required: true }],
    description: "Returns the number of characters in a text string.",
    returnType: "Number",
  },
  CONCAT: {
    args: [{ name: "text1", required: true, variadic: true }],
    description: "Combines multiple text strings into one string.",
    returnType: "String",
  },
  FILTER: {
    args: [
      { name: "array", required: true },
      { name: "include", required: true },
      { name: "ifEmpty", required: false },
    ],
    description:
      "Returns an array of values that meet specified criteria; if no values meet the criteria, returns the ifEmpty argument or a #CALC! error if omitted.",
    returnType: "Array",
  },
  SUBSTITUTE: {
    args: [
      { name: "text", required: true },
      { name: "oldText", required: true },
      { name: "newText", required: true },
      { name: "instanceNum", required: false },
    ],
    description:
      "Replaces occurrences of oldText with newText in a text string; if instanceNum is specified, only that occurrence is replaced.",
    returnType: "String",
  },
  UNIQUE: {
    args: [
      { name: "array", required: true },
      { name: "byColumn", required: false, default: false },
      { name: "exactlyOnce", required: false, default: false },
    ],
    description: "Returns a list of unique values from the array.",
    returnType: "Array",
  },

  SORT: {
    args: [
      { name: "array", required: true },
      { name: "sortIndex", required: false, default: 1 },
      { name: "sortOrder", required: false, default: 1 },
      { name: "byColumn", required: false, default: false },
    ],
    description: "Sorts the array by row (or column).",
    returnType: "Array",
  },

  SORTBY: {
    args: [
      { name: "array", required: true },
      { name: "sortArray", required: true, variadic: true },
      // pattern: sortArray1, sortOrder1, sortArray2, sortOrder2, …
    ],
    description: "Sorts array based on one or more corresponding sort arrays.",
    returnType: "Array",
  },

  SEQUENCE: {
    args: [
      { name: "rows", required: true },
      { name: "columns", required: false, default: 1 },
      { name: "start", required: false, default: 1 },
      { name: "step", required: false, default: 1 },
    ],
    description: "Generates a numeric sequence as a spill array.",
    returnType: "Array",
  },

  RANDARRAY: {
    args: [
      { name: "rows", required: false, default: 1 },
      { name: "columns", required: false, default: 1 },
      { name: "min", required: false, default: 0 },
      { name: "max", required: false, default: 1 },
      { name: "wholeNumber", required: false, default: false },
    ],
    description: "Returns an array of random numbers.",
    returnType: "Array",
  },

  COUNTIF: {
    args: [
      { name: "range", required: true },
      { name: "criteria", required: true },
    ],
    description: "Counts cells that meet a single criterion.",
    returnType: "Number",
  },

  SUMIF: {
    args: [
      { name: "range", required: true },
      { name: "criteria", required: true },
      { name: "sumRange", required: false },
    ],
    description: "Adds cells specified by a single criterion.",
    returnType: "Number",
  },

  SUMIFS: {
    args: [
      { name: "sumRange", required: true },
      { name: "criteriaRange", required: true, variadic: true },
      // pattern: criteriaRange1, criteria1, criteriaRange2, criteria2, …
    ],
    description: "Adds cells specified by multiple criteria.",
    returnType: "Number",
  },

  AVERAGE: {
    args: [{ name: "values", required: true, variadic: true }],
    description: "Returns the average of its arguments.",
    returnType: "Number",
  },

  AVERAGEIF: {
    args: [
      { name: "range", required: true },
      { name: "criteria", required: true },
      { name: "averageRange", required: false },
    ],
    description: "Averages cells that meet a single criterion.",
    returnType: "Number",
  },
  MIN: {
    args: [{ name: "numbers", required: true, variadic: true }],
    description:
      "Returns the smallest numeric value in the supplied arguments.",
    returnType: "Number",
  },

  MAX: {
    args: [{ name: "numbers", required: true, variadic: true }],
    description: "Returns the largest numeric value in the supplied arguments.",
    returnType: "Number",
  },

  COUNT: {
    args: [{ name: "values", required: true, variadic: true }],
    description:
      "Counts the number of numeric values among the supplied arguments.",
    returnType: "Number",
  },

  COUNTA: {
    args: [{ name: "values", required: true, variadic: true }],
    description:
      "Counts the number of non-empty values among the supplied arguments.",
    returnType: "Number",
  },

  COUNTBLANK: {
    args: [{ name: "range", required: true }],
    description: "Counts the number of empty cells within the specified range.",
    returnType: "Number",
  },

  TEXTJOIN: {
    args: [
      { name: "delimiter", required: true },
      { name: "ignoreEmpty", required: true },
      { name: "text", required: true, variadic: true },
    ],
    description:
      "Concatenates text items using a delimiter, optionally ignoring empty cells.",
    returnType: "String",
  },

  IFNA: {
    args: [
      { name: "value", required: true },
      { name: "valueIfNA", required: true },
    ],
    description:
      "Returns valueIfNA if the first argument evaluates to #N/A; otherwise returns the first argument.",
    returnType: "Any",
  },

  ABS: {
    args: [{ name: "number", required: true }],
    description: "Returns the absolute value of a number.",
    returnType: "Number",
  },

  CEILING: {
    args: [
      { name: "number", required: true },
      { name: "significance", required: false, default: 1 },
    ],
    description:
      "Rounds a number up to the nearest multiple of the specified significance.",
    returnType: "Number",
  },

  FLOOR: {
    args: [
      { name: "number", required: true },
      { name: "significance", required: false, default: 1 },
    ],
    description:
      "Rounds a number down to the nearest multiple of the specified significance.",
    returnType: "Number",
  },

  RANK: {
    args: [
      { name: "number", required: true },
      { name: "ref", required: true },
      { name: "order", required: false, default: 0 },
    ],
    description:
      "Returns the rank of a number in a list: 0 for descending (default) or 1 for ascending order.",
    returnType: "Number",
  },
} as const;

/* -------------------------------------------------------------------------- */
/*  Derived helper types                                                      */
/* -------------------------------------------------------------------------- */

/** Literal view of {@link typeMap}. */
export type TypeMap = typeof typeMap;

/** All recognised function names (uppercase Excel spelling). */
export type FuncName = keyof TypeMap;

/** Metadata object for a given function name. */
export type FuncDef<F extends FuncName> = TypeMap[F];

/** Tuple of argument definitions for a function. */
export type ArgList<F extends FuncName> = FuncDef<F>["args"];

/** Single parameter definition (union of list elements). */
export type ArgDef<F extends FuncName> = ArgList<F>[number];

/** Declared return type string literal for a function. */
export type ReturnType<F extends FuncName> = FuncDef<F>["returnType"];
