import {
  standardQueries
} from "./chunk-FMY4RHS4.js";
import {
  InvalidGraphQlError
} from "./chunk-73ZTML2E.js";
import {
  getSchemaObjectParameters,
  getStructuralSubpaths
} from "./chunk-Y4KCQNPQ.js";
import {
  require_parser,
  require_printer
} from "./chunk-7K4RMTU4.js";
import {
  require_source
} from "./chunk-M5OAS5QZ.js";
import {
  __toESM,
  init_cjs_shims
} from "./chunk-7GH3YGSC.js";

// src/graphql.ts
init_cjs_shims();
var import_chalk = __toESM(require_source(), 1);
var import_parser = __toESM(require_parser(), 1);
var import_printer = __toESM(require_printer(), 1);
import { statSync, existsSync } from "node:fs";
var getAccountParams = (accountPath, schema) => {
  const subpaths = getStructuralSubpaths(accountPath);
  return subpaths.flatMap(
    (subp) => getSchemaObjectParameters(
      schema.chartOfAccounts.accountPathToAccount.get(subp)
    )
  );
};
var schemaToEntryDefinitions = ({
  schema,
  includeRuntimeArgs
}) => {
  return schema.ledgerEntries.types.flatMap((ledgerEntry) => {
    const entryOnlyParams = getSchemaObjectParameters({
      ...ledgerEntry,
      lines: void 0,
      conditions: void 0
    });
    const staticLineParams = [];
    const repeatedKeys = /* @__PURE__ */ new Set();
    (ledgerEntry.lines ?? []).forEach((line) => {
      if (line.repeated) {
        repeatedKeys.add(line.repeated.key);
      } else {
        const lineParams = getSchemaObjectParameters(line);
        const accountParams = getAccountParams(line.account.path, schema);
        staticLineParams.push(...lineParams, ...accountParams);
      }
    });
    (ledgerEntry.conditions ?? []).forEach((condition) => {
      if (condition.repeated) {
        repeatedKeys.add(condition.repeated.key);
      } else {
        const condParams = getSchemaObjectParameters(condition);
        const accountParams = getAccountParams(condition.account.path, schema);
        staticLineParams.push(...condParams, ...accountParams);
      }
    });
    const parameters = Array.from(
      /* @__PURE__ */ new Set([...entryOnlyParams, ...staticLineParams])
    );
    const repeatedParameterKeys = Array.from(repeatedKeys);
    const isReconciliationEntry = (ledgerEntry.lines ?? []).some(
      (line) => !!line.tx
    );
    const isRuntimeEntry = (ledgerEntry.lines ?? []).length === 0;
    const hasSchemaConditions = (ledgerEntry.conditions ?? []).length > 0;
    const codegenInput = {
      entryType: ledgerEntry.type,
      typeVersion: ledgerEntry.typeVersion,
      runtimeEntry: isRuntimeEntry,
      parameters,
      repeatedParameterKeys: repeatedParameterKeys.length > 0 ? repeatedParameterKeys : void 0,
      includeRuntimeArgs: includeRuntimeArgs ?? false,
      hasSchemaConditions
    };
    if (isRuntimeEntry) {
      return [
        {
          ...codegenInput,
          method: "reconcileTx"
        },
        {
          ...codegenInput,
          method: "addLedgerEntry"
        }
      ];
    }
    return [
      {
        ...codegenInput,
        method: isReconciliationEntry ? "reconcileTx" : "addLedgerEntry"
      }
    ];
  });
};
var camelCase = (value, delimiter) => {
  return value.split(delimiter).filter((x) => !!x).map((word, index) => {
    if (index === 0) {
      return word;
    }
    return word[0].toUpperCase() + word.slice(1);
  }).join("");
};
var OnErrorFragment = `
... on BadRequestError {
  code
  message
  retryable
}
... on InternalError {
  code
  message
  retryable
}
`;
var LedgerEntryFragment = `
    ik
    id
    created
    type
    posted
    description
    ledger {
      ik
    }
    tags {
      key
      value
    }
    groups {
      key
      value
    }
`;
var LedgerLineFragment = `
    account {
      path
    }
    amount
    currency {
      code
      customCurrencyId
    }
    key
`;
var AddLedgerEntryFragment = `
__typename
... on AddLedgerEntryResult {
  entry {
    ${LedgerEntryFragment}
  }
  lines {
    ${LedgerLineFragment}
  }
  isIkReplay
}
${OnErrorFragment}
`;
var ReconcileTxFragment = `
__typename
... on ReconcileTxResult {
  entry {
    ${LedgerEntryFragment}
  }
  lines {
    ${LedgerLineFragment}
    tx {
      externalId
      externalAccount {
        externalId
      }
    }
  }
  isIkReplay
}
${OnErrorFragment}
`;
var isValidGraphQlName = (name) => {
  return /^[a-zA-Z_]+[a-zA-Z0-9_]*$/.exec(name) !== null;
};
var generateGraphQlEntryType = (entryType, typeVersion) => {
  const readableEntryType = camelCase(camelCase(entryType, "_"), "-").split("").map((ch, idx) => {
    if (idx === 0) {
      return ch.toUpperCase();
    }
    return ch;
  }).join("").replace(/\s+/g, "_").replace(/\./g, "_").replace(/[^a-zA-Z0-9_]/g, "").replace(/^(\d)/, "_$1").concat(typeVersion && typeVersion > 1 ? `_v${typeVersion}` : "");
  if (!isValidGraphQlName(readableEntryType)) {
    throw new InvalidGraphQlError(
      `Operation name ${readableEntryType} (for entry: ${import_chalk.default.yellow(
        entryType
      )}) is not a valid GraphQL name`
    );
  }
  return readableEntryType;
};
var getPredefinedParameters = (definition) => {
  const params = {};
  if (definition.method === "addLedgerEntry") {
    params["ik"] = "SafeString!";
    params["posted"] = "DateTime";
  }
  if (definition.runtimeEntry) {
    params["lines"] = "[LedgerLineInput!]!";
    params["tags"] = "[LedgerEntryTagInput!]";
    params["groups"] = "[LedgerEntryGroupInput!]";
  }
  if (definition.includeRuntimeArgs && !definition.runtimeEntry) {
    params["tags"] = "[LedgerEntryTagInput!]";
    params["groups"] = "[LedgerEntryGroupInput!]";
    if (!definition.hasSchemaConditions) {
      params["conditions"] = "[LedgerEntryConditionInput!]";
    }
  }
  params["ledgerIk"] = "SafeString!";
  return params;
};
var entryDefinitionToMutation = (definition) => {
  const { typeVersion } = definition;
  const predefinedParameters = getPredefinedParameters(definition);
  const repeatedKeys = new Set(definition.repeatedParameterKeys ?? []);
  const filteredParameters = definition.parameters.filter(
    (p) => !Object.prototype.hasOwnProperty.call(predefinedParameters, p) && !repeatedKeys.has(p)
  );
  [...filteredParameters, ...definition.repeatedParameterKeys ?? []].forEach(
    (param) => {
      if (!isValidGraphQlName(param)) {
        throw new InvalidGraphQlError(
          `Parameter name ${param} is not a valid GraphQL name`
        );
      }
    }
  );
  const readableEntryType = generateGraphQlEntryType(
    definition.entryType,
    typeVersion
  );
  const mutationArgs = filteredParameters.map((p) => `$${p}: String!`).join("\n");
  const repeatedParamArgs = (definition.repeatedParameterKeys ?? []).map((key) => `$${key}: [JSON!]!`).join("\n");
  const graphQlFragment = definition.method === "addLedgerEntry" ? AddLedgerEntryFragment : ReconcileTxFragment;
  const operationPrefix = definition.method === "addLedgerEntry" ? "Post" : "Reconcile";
  const isIkRequired = Object.prototype.hasOwnProperty.call(
    predefinedParameters,
    "ik"
  );
  const isPostedAllowed = Object.prototype.hasOwnProperty.call(
    predefinedParameters,
    "posted"
  );
  const operationName = `${operationPrefix}${readableEntryType}`;
  const predefinedArgs = Object.entries(predefinedParameters).map(([name, type]) => `$${name}: ${type}`).join(",\n");
  const allVarArgs = [predefinedArgs, mutationArgs, repeatedParamArgs].filter(Boolean).join(",\n");
  const command = [
    // Named mutation with input parameters block
    `mutation ${operationName} (`,
    `${allVarArgs}) {`,
    // The actual mutation method
    `${definition.method}(`,
    // The input object
    `${isIkRequired ? "ik: $ik," : ""}`,
    `entry: {`,
    `ledger: { ik: $ledgerIk },`,
    `type: "${definition.entryType}",`
  ];
  if (typeVersion !== void 0) {
    command.push(`typeVersion: ${typeVersion},`);
  }
  if (isPostedAllowed) {
    command.push(`posted: $posted,`);
  }
  if (definition.runtimeEntry) {
    command.push(`lines: $lines, tags: $tags, groups: $groups,`);
  }
  if (definition.includeRuntimeArgs && !definition.runtimeEntry) {
    const runtimeArgs = ["tags: $tags", "groups: $groups"];
    if (!definition.hasSchemaConditions) {
      runtimeArgs.push("conditions: $conditions");
    }
    command.push(`${runtimeArgs.join(", ")},`);
  }
  const allParamKeys = [
    ...definition.parameters.filter((p) => !repeatedKeys.has(p)),
    ...definition.repeatedParameterKeys ?? []
  ];
  if (allParamKeys.length > 0) {
    command.push(`parameters: {`);
    command.push(allParamKeys.map((p) => `${p}: $${p}`).join("\n"));
    command.push(`}`);
  }
  command.push(`}) { ${graphQlFragment} } }`);
  return {
    mutation: (0, import_printer.print)((0, import_parser.parse)(command.join(""))),
    operationName
  };
};
var generateQueriesFileContent = ({
  definitions,
  includeStandardQueries
}) => {
  const generatedCode = definitions.map(
    (def) => entryDefinitionToMutation(def).mutation
  );
  if (includeStandardQueries) {
    generatedCode.push(standardQueries);
  }
  return `${generatedCode.join("\n")}
`;
};
var generateQueryFiles = ({
  definitions,
  includeStandardQueries
}) => {
  const generatedCode = definitions.map((def) => {
    const { operationName, mutation } = entryDefinitionToMutation(def);
    return {
      content: `${mutation}
`,
      fileName: `${operationName}.graphql`
    };
  });
  if (includeStandardQueries) {
    const parsedStdQueries = (0, import_parser.parse)(standardQueries);
    parsedStdQueries.definitions.forEach((def) => {
      if (def.kind !== "OperationDefinition") {
        return;
      }
      generatedCode.push({
        content: `${(0, import_printer.print)(def)}
`,
        fileName: `${def.name.value}.standard-query.graphql`
      });
    });
  }
  return generatedCode;
};
var validateOutputName = ({
  output,
  outputFilePerQuery
}) => {
  if (outputFilePerQuery) {
    if (!existsSync(output)) {
      throw new Error("Output path must exist");
    }
    if (!statSync(output).isDirectory()) {
      throw new Error("Output path must be a directory");
    }
  } else {
    if (/.+\.(graphql|gql)/.exec(output) === null) {
      throw new Error("Output name must end with .graphql or .gql");
    }
  }
};

export {
  schemaToEntryDefinitions,
  camelCase,
  isValidGraphQlName,
  generateGraphQlEntryType,
  getPredefinedParameters,
  entryDefinitionToMutation,
  generateQueriesFileContent,
  generateQueryFiles,
  validateOutputName
};
