import { cloneDeep } from "lodash-es";
import { classAttributes, isClassConstructor } from "../utils/object.js";
import { useGraphQl } from "./graphql.js";
import { useGraphqlBridge } from "../bridges/graphql.js";
export function buildFields(operationFields, fields, depth = 0) {
  const { graphqlType } = useGraphqlBridge();
  if (!operationFields) return [];
  if (isClassConstructor(fields)) fields = classAttributes(fields);
  if (!fields || fields.length == 0) fields = ["*"];
  else fields = [...fields];
  if (fields.includes("*")) {
    operationFields.fields.forEach((field) => {
      if (!fields.includes(field) && !field.toLowerCase().endsWith("base64") && !field.toLowerCase().endsWith("base64string")) fields.push(field);
    });
    fields.splice(fields.indexOf("*"), 1);
  }
  if (depth > 0) operationFields.relations.forEach((relation) => fields.push(relation.name));
  if (!fields.includes("timestampField") && operationFields.relations.find((relation) => relation.name == "timestampField")) fields.push("timestampField");
  if (!fields.includes("userstampField") && operationFields.relations.find((relation) => relation.name == "userstampField")) fields.push("userstampField");
  return fields.map((field) => {
    if (typeof field == "string" && !operationFields?.fields?.includes(field)) {
      const relationField = operationFields?.relations?.find((relation) => relation.name == field);
      if (relationField && relationField.type) {
        return { [relationField.name]: buildFields(graphqlType[relationField.type], void 0, depth - 1) };
      } else {
        return void 0;
      }
    } else if (typeof field == "object") {
      Object.keys(field).forEach((key) => {
        const relationField = operationFields?.relations?.find((relation) => relation.name == key);
        if (relationField && relationField.type) {
          const castField = field;
          castField[key] = buildFields(graphqlType[relationField.type], castField[key], depth - 1);
        }
      });
    }
    return field;
  }).filter((field) => !!field);
}
export function buildVariables(outputVariables, inputVariables, reject, isRoot = true) {
  const { graphqlInputType, scalarType } = useGraphqlBridge();
  if (!outputVariables) return void 0;
  let localOutputVariables = cloneDeep(outputVariables);
  if (inputVariables) {
    localOutputVariables.map((variable) => {
      if (variable.type && !scalarType.includes(variable.type)) {
        if (variable.list) {
          if (inputVariables[variable.name] && Array.isArray(inputVariables[variable.name])) {
            const tmpVariables = [];
            inputVariables[variable.name].forEach((inputVariable) => {
              tmpVariables.push(buildVariables(graphqlInputType[variable.type].variables, inputVariable, reject, false));
            });
            variable.value = tmpVariables;
          } else {
            variable.value = void 0;
          }
        } else {
          variable.value = buildVariables(graphqlInputType[variable.type].variables, inputVariables[variable.name], reject, false);
        }
      } else {
        variable.value = inputVariables[variable.name];
      }
      return variable;
    });
  }
  localOutputVariables.forEach((variable) => {
    if (variable.required && variable.value === void 0) {
      console.warn("Required variable " + variable.name + " is not found, operation abort");
      if (reject) {
        reject("Required variable " + variable.name + " is not found");
        return;
      }
    }
  });
  const usedVariables = localOutputVariables.map((variable) => variable.name);
  const droppedVariable = Object.keys(inputVariables || {}).filter((key) => !usedVariables.includes(key));
  if (droppedVariable.length > 0) console.debug("There is data not appeared in schema and dropped before operation.", droppedVariable);
  return localOutputVariables.reduce((acc, item) => {
    acc[item.name] = isRoot ? item : item.value;
    return acc;
  }, {});
}
export function buildRequiredInputFields(variables, inputField = "input") {
  const { graphqlInputType } = useGraphqlBridge();
  const inputVariable = variables?.find((variable) => variable.name == inputField);
  if (inputVariable) {
    const returnFields = [];
    graphqlInputType[inputVariable.type]?.variables?.forEach((variable) => {
      if (variable.required) returnFields.push(variable.name);
    });
    return returnFields;
  } else {
    return [];
  }
}
export function useGraphQlOperation(operationType, operation, fields, variables, cache = false) {
  const { graphqlOperation } = useGraphqlBridge();
  return new Promise((resolve, reject) => {
    let rejectReason = void 0;
    function rejectInside(reason) {
      rejectReason = reason;
    }
    variables = buildVariables(graphqlOperation[operation]?.variables, variables, rejectInside);
    if (rejectReason) {
      reject(rejectReason);
      return;
    }
    if (graphqlOperation[operation]?.fields) {
      fields = buildFields(graphqlOperation[operation]?.fields, fields, 0);
      if (operationType == "Query") useGraphQl().queryPromise(operation, fields, variables, cache).then((result) => resolve(result)).catch((error) => reject(error));
      else if (operationType == "Mutation") useGraphQl().mutationPromise(operation, fields, variables).then((result) => resolve(result)).catch((error) => reject(error));
      else reject("Invalid Operation Type");
    } else {
      reject("No schema of return data available");
    }
  });
}
