/**
 * Query Builder - Builds and executes GraphQL operations
 * @generated by @constructive-io/graphql-codegen
 * DO NOT EDIT - changes will be overwritten
 */
import { parseType, print } from '@constructive-io/graphql-query/runtime';
import * as t from 'gql-ast';
import { GraphQLRequestError } from './client';
export class QueryBuilder {
    config;
    constructor(config) {
        this.config = config;
    }
    /**
     * Execute the query and return a discriminated union result
     * Use result.ok to check success, or .unwrap() to throw on error
     */
    async execute() {
        const rawResult = await this.config.client.execute(this.config.document, this.config.variables);
        if (!rawResult.ok) {
            return rawResult;
        }
        if (!this.config.transform) {
            return rawResult;
        }
        return {
            ok: true,
            data: this.config.transform(rawResult.data),
            errors: undefined,
        };
    }
    /**
     * Execute and unwrap the result, throwing GraphQLRequestError on failure
     * @throws {GraphQLRequestError} If the query returns errors
     */
    async unwrap() {
        const result = await this.execute();
        if (!result.ok) {
            throw new GraphQLRequestError(result.errors, result.data);
        }
        return result.data;
    }
    /**
     * Execute and unwrap, returning defaultValue on error instead of throwing
     */
    async unwrapOr(defaultValue) {
        const result = await this.execute();
        if (!result.ok) {
            return defaultValue;
        }
        return result.data;
    }
    /**
     * Execute and unwrap, calling onError callback on failure
     */
    async unwrapOrElse(onError) {
        const result = await this.execute();
        if (!result.ok) {
            return onError(result.errors);
        }
        return result.data;
    }
    toGraphQL() {
        return this.config.document;
    }
    getVariables() {
        return this.config.variables;
    }
}
const OP_QUERY = 'query';
const OP_MUTATION = 'mutation';
const ENUM_VALUE_KIND = 'EnumValue';
// ============================================================================
// Selection Builders
// ============================================================================
export function buildSelections(select, connectionFieldsMap, entityType) {
    if (!select) {
        return [];
    }
    const fields = [];
    const entityConnections = entityType ? connectionFieldsMap?.[entityType] : undefined;
    for (const [key, value] of Object.entries(select)) {
        if (value === false || value === undefined) {
            continue;
        }
        if (value === true) {
            fields.push(t.field({ name: key }));
            continue;
        }
        if (typeof value === 'object' && value !== null) {
            const nested = value;
            if (!nested.select || typeof nested.select !== 'object') {
                throw new Error(`Invalid selection for field "${key}": nested selections must include a "select" object.`);
            }
            const relatedEntityType = entityConnections?.[key];
            const nestedSelections = buildSelections(nested.select, connectionFieldsMap, relatedEntityType);
            const isConnection = nested.connection === true ||
                nested.first !== undefined ||
                nested.filter !== undefined ||
                relatedEntityType !== undefined;
            const args = buildArgs([
                buildOptionalArg('first', nested.first),
                nested.filter
                    ? t.argument({
                        name: 'filter',
                        value: buildValueAst(nested.filter),
                    })
                    : null,
                buildEnumListArg('orderBy', nested.orderBy),
            ]);
            if (isConnection) {
                fields.push(t.field({
                    name: key,
                    args,
                    selectionSet: t.selectionSet({
                        selections: buildConnectionSelections(nestedSelections),
                    }),
                }));
            }
            else {
                fields.push(t.field({
                    name: key,
                    args,
                    selectionSet: t.selectionSet({ selections: nestedSelections }),
                }));
            }
        }
    }
    return fields;
}
// ============================================================================
// Document Builders
// ============================================================================
export function buildFindManyDocument(operationName, queryField, select, args, filterTypeName, orderByTypeName, connectionFieldsMap) {
    const selections = select
        ? buildSelections(select, connectionFieldsMap, operationName)
        : [t.field({ name: 'id' })];
    const variableDefinitions = [];
    const queryArgs = [];
    const variables = {};
    addVariable({
        varName: 'where',
        typeName: filterTypeName,
        value: args.where,
    }, variableDefinitions, queryArgs, variables);
    addVariable({
        varName: 'orderBy',
        typeName: '[' + orderByTypeName + '!]',
        value: args.orderBy?.length ? args.orderBy : undefined,
    }, variableDefinitions, queryArgs, variables);
    addVariable({ varName: 'first', typeName: 'Int', value: args.first }, variableDefinitions, queryArgs, variables);
    addVariable({ varName: 'last', typeName: 'Int', value: args.last }, variableDefinitions, queryArgs, variables);
    addVariable({ varName: 'after', typeName: 'Cursor', value: args.after }, variableDefinitions, queryArgs, variables);
    addVariable({ varName: 'before', typeName: 'Cursor', value: args.before }, variableDefinitions, queryArgs, variables);
    addVariable({ varName: 'offset', typeName: 'Int', value: args.offset }, variableDefinitions, queryArgs, variables);
    const document = t.document({
        definitions: [
            t.operationDefinition({
                operation: OP_QUERY,
                name: operationName + 'Query',
                variableDefinitions: variableDefinitions.length ? variableDefinitions : undefined,
                selectionSet: t.selectionSet({
                    selections: [
                        t.field({
                            name: queryField,
                            args: queryArgs.length ? queryArgs : undefined,
                            selectionSet: t.selectionSet({
                                selections: buildConnectionSelections(selections),
                            }),
                        }),
                    ],
                }),
            }),
        ],
    });
    return { document: print(document), variables };
}
export function buildFindFirstDocument(operationName, queryField, select, args, filterTypeName, connectionFieldsMap) {
    const selections = select
        ? buildSelections(select, connectionFieldsMap, operationName)
        : [t.field({ name: 'id' })];
    const variableDefinitions = [];
    const queryArgs = [];
    const variables = {};
    // Always add first: 1 for findFirst
    addVariable({ varName: 'first', typeName: 'Int', value: 1 }, variableDefinitions, queryArgs, variables);
    addVariable({
        varName: 'where',
        typeName: filterTypeName,
        value: args.where,
    }, variableDefinitions, queryArgs, variables);
    const document = t.document({
        definitions: [
            t.operationDefinition({
                operation: OP_QUERY,
                name: operationName + 'Query',
                variableDefinitions,
                selectionSet: t.selectionSet({
                    selections: [
                        t.field({
                            name: queryField,
                            args: queryArgs,
                            selectionSet: t.selectionSet({
                                selections: [
                                    t.field({
                                        name: 'nodes',
                                        selectionSet: t.selectionSet({ selections }),
                                    }),
                                ],
                            }),
                        }),
                    ],
                }),
            }),
        ],
    });
    return { document: print(document), variables };
}
export function buildCreateDocument(operationName, mutationField, entityField, select, data, inputTypeName, connectionFieldsMap) {
    const selections = select
        ? buildSelections(select, connectionFieldsMap, operationName)
        : [t.field({ name: 'id' })];
    return {
        document: buildInputMutationDocument({
            operationName,
            mutationField,
            inputTypeName,
            resultSelections: [
                t.field({
                    name: entityField,
                    selectionSet: t.selectionSet({ selections }),
                }),
            ],
        }),
        variables: {
            input: {
                [entityField]: data,
            },
        },
    };
}
export function buildUpdateDocument(operationName, mutationField, entityField, select, where, data, inputTypeName, patchFieldName, connectionFieldsMap) {
    const selections = select
        ? buildSelections(select, connectionFieldsMap, operationName)
        : [t.field({ name: 'id' })];
    return {
        document: buildInputMutationDocument({
            operationName,
            mutationField,
            inputTypeName,
            resultSelections: [
                t.field({
                    name: entityField,
                    selectionSet: t.selectionSet({ selections }),
                }),
            ],
        }),
        variables: {
            input: {
                id: where.id,
                [patchFieldName]: data,
            },
        },
    };
}
export function buildUpdateByPkDocument(operationName, mutationField, entityField, select, id, data, inputTypeName, idFieldName, patchFieldName, connectionFieldsMap) {
    const selections = select
        ? buildSelections(select, connectionFieldsMap, operationName)
        : [t.field({ name: 'id' })];
    return {
        document: buildInputMutationDocument({
            operationName,
            mutationField,
            inputTypeName,
            resultSelections: [
                t.field({
                    name: entityField,
                    selectionSet: t.selectionSet({ selections }),
                }),
            ],
        }),
        variables: {
            input: {
                [idFieldName]: id,
                [patchFieldName]: data,
            },
        },
    };
}
export function buildFindOneDocument(operationName, queryField, id, select, idArgName, idTypeName, connectionFieldsMap) {
    const selections = select
        ? buildSelections(select, connectionFieldsMap, operationName)
        : [t.field({ name: 'id' })];
    const variableDefinitions = [
        t.variableDefinition({
            variable: t.variable({ name: idArgName }),
            type: parseType(idTypeName),
        }),
    ];
    const queryArgs = [
        t.argument({
            name: idArgName,
            value: t.variable({ name: idArgName }),
        }),
    ];
    const document = t.document({
        definitions: [
            t.operationDefinition({
                operation: OP_QUERY,
                name: operationName + 'Query',
                variableDefinitions,
                selectionSet: t.selectionSet({
                    selections: [
                        t.field({
                            name: queryField,
                            args: queryArgs,
                            selectionSet: t.selectionSet({ selections }),
                        }),
                    ],
                }),
            }),
        ],
    });
    return {
        document: print(document),
        variables: { [idArgName]: id },
    };
}
export function buildDeleteDocument(operationName, mutationField, entityField, where, inputTypeName, select, connectionFieldsMap) {
    const entitySelections = select
        ? buildSelections(select, connectionFieldsMap, operationName)
        : [t.field({ name: 'id' })];
    return {
        document: buildInputMutationDocument({
            operationName,
            mutationField,
            inputTypeName,
            resultSelections: [
                t.field({
                    name: entityField,
                    selectionSet: t.selectionSet({
                        selections: entitySelections,
                    }),
                }),
            ],
        }),
        variables: {
            input: {
                id: where.id,
            },
        },
    };
}
export function buildDeleteByPkDocument(operationName, mutationField, entityField, keys, inputTypeName, select, connectionFieldsMap) {
    const entitySelections = select
        ? buildSelections(select, connectionFieldsMap, operationName)
        : [t.field({ name: 'id' })];
    return {
        document: buildInputMutationDocument({
            operationName,
            mutationField,
            inputTypeName,
            resultSelections: [
                t.field({
                    name: entityField,
                    selectionSet: t.selectionSet({ selections: entitySelections }),
                }),
            ],
        }),
        variables: {
            input: keys,
        },
    };
}
export function buildJunctionRemoveDocument(operationName, mutationField, keys, inputTypeName) {
    return {
        document: buildInputMutationDocument({
            operationName,
            mutationField,
            inputTypeName,
            resultSelections: [t.field({ name: 'clientMutationId' })],
        }),
        variables: {
            input: keys,
        },
    };
}
export function buildCustomDocument(operationType, operationName, fieldName, select, args, variableDefinitions, connectionFieldsMap, entityType) {
    let actualSelect = select;
    let isConnection = false;
    if (isCustomSelectionWrapper(select)) {
        actualSelect = select.select;
        isConnection = select.connection === true;
    }
    const selections = actualSelect
        ? buildSelections(actualSelect, connectionFieldsMap, entityType)
        : [];
    const variableDefs = variableDefinitions.map((definition) => t.variableDefinition({
        variable: t.variable({ name: definition.name }),
        type: parseType(definition.type),
    }));
    const fieldArgs = variableDefinitions.map((definition) => t.argument({
        name: definition.name,
        value: t.variable({ name: definition.name }),
    }));
    const fieldSelections = isConnection ? buildConnectionSelections(selections) : selections;
    const document = t.document({
        definitions: [
            t.operationDefinition({
                operation: operationType === 'mutation' ? OP_MUTATION : OP_QUERY,
                name: operationName,
                variableDefinitions: variableDefs.length ? variableDefs : undefined,
                selectionSet: t.selectionSet({
                    selections: [
                        t.field({
                            name: fieldName,
                            args: fieldArgs.length ? fieldArgs : undefined,
                            selectionSet: fieldSelections.length
                                ? t.selectionSet({ selections: fieldSelections })
                                : undefined,
                        }),
                    ],
                }),
            }),
        ],
    });
    return {
        document: print(document),
        variables: (args ?? {}),
    };
}
function isCustomSelectionWrapper(value) {
    if (!value || typeof value !== 'object' || Array.isArray(value)) {
        return false;
    }
    const record = value;
    const keys = Object.keys(record);
    if (!keys.includes('select') || !keys.includes('connection')) {
        return false;
    }
    if (keys.some((key) => key !== 'select' && key !== 'connection')) {
        return false;
    }
    return !!record.select && typeof record.select === 'object' && !Array.isArray(record.select);
}
// ============================================================================
// Helper Functions
// ============================================================================
function buildArgs(args) {
    return args.filter((arg) => arg !== null);
}
function buildOptionalArg(name, value) {
    if (value === undefined) {
        return null;
    }
    const valueNode = typeof value === 'number' ? t.intValue({ value: value.toString() }) : t.stringValue({ value });
    return t.argument({ name, value: valueNode });
}
function buildEnumListArg(name, values) {
    if (!values || values.length === 0) {
        return null;
    }
    return t.argument({
        name,
        value: t.listValue({
            values: values.map((value) => buildEnumValue(value)),
        }),
    });
}
function buildEnumValue(value) {
    return {
        kind: ENUM_VALUE_KIND,
        value,
    };
}
function buildPageInfoSelections() {
    return [
        t.field({ name: 'hasNextPage' }),
        t.field({ name: 'hasPreviousPage' }),
        t.field({ name: 'startCursor' }),
        t.field({ name: 'endCursor' }),
    ];
}
function buildConnectionSelections(nodeSelections) {
    return [
        t.field({
            name: 'nodes',
            selectionSet: t.selectionSet({ selections: nodeSelections }),
        }),
        t.field({ name: 'totalCount' }),
        t.field({
            name: 'pageInfo',
            selectionSet: t.selectionSet({ selections: buildPageInfoSelections() }),
        }),
    ];
}
function buildInputMutationDocument(config) {
    const document = t.document({
        definitions: [
            t.operationDefinition({
                operation: OP_MUTATION,
                name: config.operationName + 'Mutation',
                variableDefinitions: [
                    t.variableDefinition({
                        variable: t.variable({ name: 'input' }),
                        type: parseType(config.inputTypeName + '!'),
                    }),
                ],
                selectionSet: t.selectionSet({
                    selections: [
                        t.field({
                            name: config.mutationField,
                            args: [
                                t.argument({
                                    name: 'input',
                                    value: t.variable({ name: 'input' }),
                                }),
                            ],
                            selectionSet: t.selectionSet({
                                selections: config.resultSelections,
                            }),
                        }),
                    ],
                }),
            }),
        ],
    });
    return print(document);
}
function addVariable(spec, definitions, args, variables) {
    if (spec.value === undefined || !spec.typeName)
        return;
    definitions.push(t.variableDefinition({
        variable: t.variable({ name: spec.varName }),
        type: parseType(spec.typeName),
    }));
    args.push(t.argument({
        name: spec.argName ?? spec.varName,
        value: t.variable({ name: spec.varName }),
    }));
    variables[spec.varName] = spec.value;
}
function buildValueAst(value) {
    if (value === null) {
        return t.nullValue();
    }
    if (typeof value === 'boolean') {
        return t.booleanValue({ value });
    }
    if (typeof value === 'number') {
        return Number.isInteger(value)
            ? t.intValue({ value: value.toString() })
            : t.floatValue({ value: value.toString() });
    }
    if (typeof value === 'string') {
        return t.stringValue({ value });
    }
    if (Array.isArray(value)) {
        return t.listValue({
            values: value.map((item) => buildValueAst(item)),
        });
    }
    if (typeof value === 'object' && value !== null) {
        const obj = value;
        return t.objectValue({
            fields: Object.entries(obj).map(([key, val]) => t.objectField({
                name: key,
                value: buildValueAst(val),
            })),
        });
    }
    throw new Error('Unsupported value type: ' + typeof value);
}
