"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InsertQueryBuilder = void 0;
const fields_processor_js_1 = require("./fields-processor.js");
const query_builder_js_1 = require("./query-builder.js");
const selection_builder_js_1 = require("./selection-builder.js");
const fieldsProcessor = new fields_processor_js_1.FieldsProcessor();
/**
 * A query builder for creating new records in the database.
 * @template DB - The database type.
 * @template TableName - The name of the table to insert into.
 * @template Selection - The type of the selected fields.
 */
class InsertQueryBuilder {
    #node;
    #executor;
    constructor(node, executor) {
        this.#node = node;
        this.#executor = executor;
    }
    /**
     * The values to insert into the table.
     * @param values - An object or array of objects representing the records to create.
     * @returns The `InsertQueryBuilder` instance for chaining.
     *
     * @example Single Record Insertion
     * ```typescript
     * const newUser = await qb
     *   .insertInto('users')
     *   .values({ name: 'John Doe', email: 'john.doe@example.com' })
     *   .executeTakeFirst();
     * ```
     *
     * @example Multiple Record Insertion
     * ```typescript
     * const newUsers = await qb
     *   .insertInto('users')
     *   .values([{ name: 'John Doe' }, { name: 'Jane Doe' }])
     *   .execute();
     * ```
     */
    values(values) {
        const records = Array.isArray(values) ? values : [values];
        const createdRecords = records.map(record => fieldsProcessor.process(record));
        return new InsertQueryBuilder({
            ...this.#node,
            createdRecords,
        }, this.#executor);
    }
    /**
     * The fields to return after the insert operation.
     * @param fields - An array of field names to return.
     * @returns The `InsertQueryBuilder` instance for chaining.
     *
     * @example
     * ```typescript
     * const newUser = await qb
     *   .insertInto('users')
     *   .values({ name: 'John Doe' })
     *   .returning(['id', 'name'])
     *   .executeTakeFirst();
     * ```
     */
    returning(fields) {
        const newSelects = fields.map(field => {
            if (typeof field === 'function') {
                const builder = new selection_builder_js_1.SelectionBuilder(this.#executor);
                // @ts-ignore
                const subQuery = field(builder);
                return subQuery._node;
            }
            return field;
        });
        return new InsertQueryBuilder({
            ...this.#node,
            returning: [...this.#node.returning, ...newSelects],
        }, this.#executor);
    }
    /**
     * Executes the insert query.
     * @returns A promise that resolves with an array of the selected fields from the inserted records.
     *
     * @example
     * ```typescript
     * const newUsers = await qb
     *   .insertInto('users')
     *   .values([{ name: 'John Doe' }, { name: 'Jane Doe' }])
     *   .returning(['id', 'name'])
     *   .execute();
     * ```
     */
    async execute() {
        return (await this.#executor.execute(this))[0];
    }
    /**
     * Executes the insert query and returns the first result.
     * @returns A promise that resolves with the first inserted record, or `null` if no records were inserted.
     *
     * @example
     * ```typescript
     * const newUser = await qb
     *   .insertInto('users')
     *   .values({ name: 'John Doe' })
     *   .returning(['id', 'name'])
     *   .executeTakeFirst();
     * ```
     */
    async executeTakeFirst() {
        const response = await this.execute();
        return response[0] ?? null;
    }
    compile() {
        const query = 'mutation ($metadata: JSON) { execute(metadata: $metadata) }';
        const metadata = [this._prepareMetadata()];
        return {
            query,
            variables: {
                metadata,
            },
        };
    }
    _prepareMetadata() {
        const buildSelects = (selects) => {
            return selects.map(field => {
                if (typeof field === 'string') {
                    return field;
                }
                const subQueryBuilder = new query_builder_js_1.QueryBuilder(field, this.#executor);
                return subQueryBuilder._prepareMetadata();
            });
        };
        const returningSelection = this.#node.returning.length
            ? buildSelects(this.#node.returning)
            : ['id'];
        return {
            type: 'create',
            tableName: this.#node.tableName,
            createdRecords: this.#node.createdRecords,
            returning: returningSelection,
        };
    }
}
exports.InsertQueryBuilder = InsertQueryBuilder;
//# sourceMappingURL=insert-query-builder.js.map