/*
 * Copyright 2023 Palantir Technologies, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import invariant from "tiny-invariant";
import { fullyQualifyPropName } from "./fullyQualifyPropName.js";
import { makeGeoFilterIntersects } from "./makeGeoFilterIntersects.js";
import { makeGeoFilterWithin } from "./makeGeoFilterWithin.js";
function isAndClause(whereClause) {
  return "$and" in whereClause && whereClause.$and !== undefined;
}
function isOrClause(whereClause) {
  return "$or" in whereClause && whereClause.$or !== undefined;
}
function isNotClause(whereClause) {
  return "$not" in whereClause && whereClause.$not !== undefined;
}

/** @internal */
export function modernToLegacyWhereClause(whereClause, objectOrInterface, rdpNames) {
  const parts = Object.entries(whereClause).map(([key, value]) => ({
    [key]: value
  }));
  if (parts.length === 1) {
    return modernToLegacyWhereClauseInner(whereClause, objectOrInterface, rdpNames);
  }
  return {
    type: "and",
    value: parts.map(v => modernToLegacyWhereClauseInner(v, objectOrInterface, rdpNames))
  };
}

/** @internal */
export function modernToLegacyWhereClauseInner(whereClause, objectOrInterface, rdpNames) {
  const parts = Object.entries(whereClause);
  !(parts.length === 1) ? process.env.NODE_ENV !== "production" ? invariant(false, "Invalid where clause provided.") : invariant(false) : void 0;
  if (isAndClause(whereClause)) {
    return {
      type: "and",
      value: whereClause.$and.map(clause => modernToLegacyWhereClause(clause, objectOrInterface, rdpNames))
    };
  } else if (isOrClause(whereClause)) {
    return {
      type: "or",
      value: whereClause.$or.map(clause => modernToLegacyWhereClause(clause, objectOrInterface, rdpNames))
    };
  } else if (isNotClause(whereClause)) {
    return {
      type: "not",
      value: modernToLegacyWhereClause(whereClause.$not, objectOrInterface, rdpNames)
    };
  }
  return handleWherePair(parts[0], objectOrInterface, undefined, rdpNames);
}
function handleWherePair([fieldName, filter], objectOrInterface, structFieldSelector, rdpNames) {
  !(filter != null) ? process.env.NODE_ENV !== "production" ? invariant(false, "Defined key values are only allowed when they are not undefined.") : invariant(false) : void 0;
  const isRdp = !structFieldSelector && rdpNames?.has(fieldName);
  const propertyIdentifier = isRdp ? {
    type: "property",
    apiName: fieldName
  } : structFieldSelector != null ? {
    type: "structField",
    ...structFieldSelector,
    propertyApiName: fullyQualifyPropName(structFieldSelector.propertyApiName, objectOrInterface)
  } : undefined;
  const field = !isRdp && structFieldSelector == null ? fullyQualifyPropName(fieldName, objectOrInterface) : undefined;
  !(field == null || propertyIdentifier == null && (field != null || isRdp != null)) ? process.env.NODE_ENV !== "production" ? invariant(false, "Encountered error constructing where clause: field and propertyIdentifier cannot both be defined") : invariant(false) : void 0;
  if (typeof filter === "string" || typeof filter === "number" || typeof filter === "boolean") {
    return {
      type: "eq",
      ...(propertyIdentifier != null && {
        propertyIdentifier
      }),
      field,
      value: filter
    };
  }
  const keysOfFilter = Object.keys(filter);

  // If any of the keys start with `$` then they must be the only one.
  // e.g. `where({ name: { $eq: "foo", $ne: "bar" } })` is invalid currently
  const hasDollarSign = keysOfFilter.some(key => key.startsWith("$"));
  !(!hasDollarSign || keysOfFilter.length === 1) ? process.env.NODE_ENV !== "production" ? invariant(false, "A WhereClause Filter with multiple clauses/fields is not allowed. Instead, use an 'or'/'and' clause to combine multiple filters.") : invariant(false) : void 0;

  // Struct
  if (!hasDollarSign) {
    const structFilter = Object.entries(filter);
    !(structFilter.length === 1) ? process.env.NODE_ENV !== "production" ? invariant(false, "Cannot filter on more than one struct field in the same clause, need to use an and clause") : invariant(false) : void 0;
    const structFieldApiName = keysOfFilter[0];
    return handleWherePair(Object.entries(filter)[0], objectOrInterface, {
      propertyApiName: fieldName,
      structFieldApiName
    }, rdpNames);
  }
  const firstKey = keysOfFilter[0];
  !(filter[firstKey] != null) ? process.env.NODE_ENV !== "production" ? invariant(false) : invariant(false) : void 0;

  // Struct array
  if (firstKey === "$contains" && filter[firstKey] instanceof Object) {
    const structFilter = Object.entries(filter[firstKey]);
    !(structFilter.length === 1) ? process.env.NODE_ENV !== "production" ? invariant(false, "Cannot filter on more than one struct field in the same clause, need to use an and clause") : invariant(false) : void 0;
    const structFieldApiName = structFilter[0][0];
    return handleWherePair(structFilter[0], objectOrInterface, {
      propertyApiName: fieldName,
      structFieldApiName
    });
  }
  if (firstKey === "$ne") {
    return {
      type: "not",
      value: {
        type: "eq",
        ...(propertyIdentifier != null && {
          propertyIdentifier
        }),
        field,
        value: filter[firstKey]
      }
    };
  }
  if (firstKey === "$within") {
    return makeGeoFilterWithin(filter[firstKey], propertyIdentifier, field);
  }
  if (firstKey === "$intersects") {
    return makeGeoFilterIntersects(filter[firstKey], propertyIdentifier, field);
  }
  if (firstKey === "$containsAllTerms" || firstKey === "$containsAnyTerm") {
    return {
      type: firstKey.substring(1),
      ...(propertyIdentifier != null && {
        propertyIdentifier
      }),
      field,
      value: typeof filter[firstKey] === "string" ? filter[firstKey] : filter[firstKey]["term"],
      fuzzy: typeof filter[firstKey] === "string" ? false : filter[firstKey]["fuzzySearch"] ?? false
    };
  }
  return {
    type: firstKey.substring(1),
    ...(propertyIdentifier != null && {
      propertyIdentifier
    }),
    field,
    value: filter[firstKey]
  };
}
//# sourceMappingURL=modernToLegacyWhereClause.js.map