import { isRecord } from '@takeshape/util';
import has from 'lodash/has.js';
import isArray from 'lodash/isArray.js';
import isPlainObject from 'lodash/isPlainObject.js';
import isString from 'lodash/isString.js';
import isUndefined from 'lodash/isUndefined.js';
import { latestSchemaJson } from "../schemas/index.js";
import { ArgsType } from "./types.js";
/** Resolver Type Utils **/
/**
 * Only tests that the shape is right, not that the name is correct. That's a job for the validator.
 */
export function isBasicResolver(resolver) {
    return isRecord(resolver) && isString(resolver.name);
}
export function isServiceResolver(resolver) {
    return isRecord(resolver) && isString(resolver.name) && isString(resolver.service);
}
export function isComposeResolver(resolver) {
    return Boolean(isArray(resolver?.compose) &&
        resolver.compose.length &&
        resolver.compose.every(isBasicResolver));
}
const { definitions: { takeshapeResolver, shapedbResolver, utilResolver, graphqlResolver, restResolver, awsLambdaResolver } } = latestSchemaJson;
export function isTakeshapeResolver(resolver) {
    return isBasicResolver(resolver) && takeshapeResolver.properties.name.enum.includes(resolver.name);
}
export function isShapedbResolver(resolver) {
    return isBasicResolver(resolver) && shapedbResolver.properties.name.enum.includes(resolver.name);
}
export function isGraphqlResolver(resolver) {
    return isBasicResolver(resolver) && graphqlResolver.properties.name.enum.includes(resolver.name);
}
export function isRestResolver(resolver) {
    return isBasicResolver(resolver) && restResolver.properties.name.enum.includes(resolver.name);
}
export function isAwsLambdaResolver(resolver) {
    return isBasicResolver(resolver) && awsLambdaResolver.properties.name.enum.includes(resolver.name);
}
export function isUtilResolver(resolver) {
    return isBasicResolver(resolver) && utilResolver.properties.name.enum.includes(resolver.name);
}
export const aiResolverNames = ['ai:generateText', 'ai:chat'];
export function isAIResolverName(name) {
    return name ? aiResolverNames.includes(name) : false;
}
export function isAIResolver(resolver) {
    return isBasicResolver(resolver) && isAIResolverName(resolver.name);
}
export function isAIAgentStateExecution(agentExecution) {
    return agentExecution.type === 'generate' || agentExecution.type === 'chat';
}
export function isProjectSchemaWithServices(schema) {
    return !isUndefined(schema.services);
}
export function containsResolver(resolver, predicate) {
    if (isBasicResolver(resolver)) {
        return predicate(resolver);
    }
    return Boolean(resolver.compose.find((resolver) => containsResolver(resolver, predicate)));
}
/** Directive Mapping Utils **/
/**
 * Determine whether the ambiguous Directive structure is a DirectiveMappingMap
 */
export function isDirectiveMappingMap(maybeMap) {
    return isPlainObject(maybeMap) && Object.keys(maybeMap).length > 0;
}
/**
 * Determine whether the ambiguous Directive structure is a DirectiveMappingArray
 */
export function isDirectiveMappingArray(maybeArray) {
    return isArray(maybeArray) && isArray(maybeArray[0]) && isString(maybeArray[0][0]) && isArray(maybeArray[0][1]);
}
/**
 * Determine whether the ambiguous Directive structure is a DirectiveConfig array
 */
export function isDirectiveConfig(maybeConfig) {
    return isArray(maybeConfig) && isArray(maybeConfig[0]) && isString(maybeConfig[0][0]) && isRecord(maybeConfig[0][1]);
}
/**
 * Determine whether a ParameterSerializeOption is a content type serializer
 */
export function isParameterSerializeContentOptions(maybeContent) {
    return Boolean(maybeContent.contentType);
}
/**
 * Determine whether an unknown variable is a `ParameterOp`
 */
export function isParameterOp(maybeOp) {
    const op = maybeOp;
    if (!op.path) {
        return false;
    }
    return isParameterOpNested(op) || isParameterOpMapping(op) || isParameterOpValue(op) || isParameterOpOp(op);
}
/**
 * Determine whether a `ParameterOp` is a `ParameterOpNested`
 */
export function isParameterOpNested(maybeOp) {
    return Boolean(maybeOp.ops);
}
/**
 * Determine whether a `ParameterOp` is a `ParameterOpMapping`
 */
export function isParameterOpMapping(maybeOp) {
    return Boolean(maybeOp.mapping);
}
/**
 * Determine whether a `ParameterOp` is a `ParameterOpValue`
 */
export function isParameterOpValue(maybeOp) {
    return Boolean(maybeOp.value);
}
/**
 * Determine whether a `ParameterOp` is a `ParameterOpOp`
 */
export function isParameterOpOp(maybeOp) {
    const op = maybeOp;
    if (!op.op) {
        return false;
    }
    return !(isParameterOpNested(op) || isParameterOpMapping(op) || isParameterOpValue(op));
}
const { definitions: { parameterSerializeStyleOptionsForSearchParams, parameterSerializeStyleOptionsForPathParams } } = latestSchemaJson;
export function isParameterSerializeStyleForSearchParams(maybeParam) {
    return Boolean(maybeParam && parameterSerializeStyleOptionsForSearchParams.properties.style.enum.includes(maybeParam));
}
export function isParameterSerializeStyleForPathParams(maybeParam) {
    return Boolean(maybeParam && parameterSerializeStyleOptionsForPathParams.properties.style.enum.includes(maybeParam));
}
/**
 * Determine whether a ParameterSerializeOption is a style serializer
 */
export function isParameterSerializeStyleOptions(maybeStyle) {
    return Boolean(maybeStyle.style);
}
/** Typeguards for different types of shape schemas **/
export function isRefSchema(propertySchema) {
    return isString(propertySchema['@ref']) || isString(propertySchema.$ref);
}
export function isRefSchemaLegacy(propertySchema) {
    return isString(propertySchema.$ref);
}
export function isAllOfSchema(propertySchema) {
    return isArray(propertySchema.allOf);
}
export function isExtendsSchema(propertySchema) {
    return isRecord(propertySchema) && isArray(propertySchema.extends);
}
export function isOneOfSchema(propertySchema) {
    return isArray(propertySchema.oneOf);
}
export function isEnumSchema(obj) {
    return isArray(obj.enum);
}
export function isArraySchema(propertySchema) {
    return propertySchema.type === 'array' && isRecord(propertySchema.items);
}
export function isReturnShapeArraySchema(returnShape) {
    return typeof returnShape === 'object' && returnShape.type === 'array';
}
export function isObjectSchema(propertySchema) {
    return isRecord(propertySchema) && propertySchema.type === 'object' && isRecord(propertySchema.properties);
}
/** Service Config Utils **/
/**
 * Test all required props, currently:
 * "name", "slug", "provider", "serviceType", "authenticationType"
 */
export function isAnyServiceConfig(maybeConfig) {
    return (maybeConfig &&
        isString(maybeConfig.title) &&
        isString(maybeConfig.id) &&
        isString(maybeConfig.provider) &&
        isString(maybeConfig.serviceType) &&
        isString(maybeConfig.authenticationType));
}
export function isGraphQLServiceConfig(maybeConfig) {
    return (isAnyServiceConfig(maybeConfig) && maybeConfig.serviceType === 'graphql' && isString(maybeConfig.options?.endpoint));
}
export function isRESTServiceConfig(maybeConfig) {
    return isAnyServiceConfig(maybeConfig) && maybeConfig.serviceType === 'rest';
}
export function isOpenAPIServiceConfig(maybeConfig) {
    return isAnyServiceConfig(maybeConfig) && maybeConfig.serviceType === 'openapi';
}
export function isShopifyServiceConfig(maybeShopify) {
    return maybeShopify.provider === 'shopify';
}
export function isEncryptedServiceConfig(maybeConfig) {
    return isAnyServiceConfig(maybeConfig) && isString(maybeConfig.authentication);
}
export function isServiceConfigWithOAuth2Authentication(maybeServiceConfig) {
    return (isAnyServiceConfig(maybeServiceConfig) &&
        isServiceAuthentication(maybeServiceConfig.authentication) &&
        isOAuth2Authentication(maybeServiceConfig.authentication));
}
export function isServiceConfigWithCustomAuthentication(maybeServiceConfig) {
    return (isAnyServiceConfig(maybeServiceConfig) &&
        isServiceAuthentication(maybeServiceConfig.authentication) &&
        isCustomAuthentication(maybeServiceConfig.authentication));
}
/** Authentication Utils **/
export function isServiceAuthentication(maybeAuthentication) {
    return maybeAuthentication?.type;
}
export function isBearerAuthentication(authentication) {
    return authentication?.type === 'bearer';
}
export function isBasicAuthentication(authentication) {
    return authentication?.type === 'basic';
}
export function isSearchParamsAuthentication(authentication) {
    return authentication?.type === 'searchParams';
}
export function isOAuth2Authentication(authentication) {
    return authentication?.type === 'oauth2';
}
export function isOAuth2BearerAuthentication(authentication) {
    return authentication?.type === 'oauth2Bearer';
}
export function isGoogleAuthentication(authentication) {
    return authentication?.type === 'google';
}
export function isCustomAuthentication(authentication) {
    return authentication?.type === 'custom';
}
/** Miscellaneous Utils **/
/**
 * A utility to return the type of args present on a schema.
 *
 * @param argsSchema Any schema that may have an `args` property.
 */
export const getArgsType = (argsSchema) => {
    if (has(argsSchema, '@args')) {
        return ArgsType['@args'];
    }
    if (has(argsSchema, 'args')) {
        return ArgsType.args;
    }
};
export function isPropertySchemaWithRelationship(schema) {
    return schema['@resolver']?.name === 'shapedb:getRelated';
}
export function isModelShape(shape) {
    return Boolean(shape.model);
}
export function isCachedShape(shape) {
    return shape.cache?.enabled === true && shape.loaders !== undefined;
}
export function isReferenceableShape(shape) {
    return Boolean(shape.loaders?.get);
}
export function listCachedShapes(shapes) {
    return Object.values(shapes).filter(isCachedShape);
}
export function getCachedShapeMap(shapes) {
    const results = {};
    for (const shape of Object.values(shapes)) {
        if (isCachedShape(shape)) {
            results[shape.name] = shape;
        }
    }
    return results;
}
export function isMeshShape(shape) {
    return Boolean(shape.joins);
}
