import { hashObject } from "../../utils/hashObject.js";
import { createAggregator } from "../Aggregators/index.js";
import { getInitialHeader, mergeHeaders } from "../headerUtils.js";
import { emptyGroup, extractAggregateResult } from "./emptyGroup.js";
import { createParallelQueryResult } from "../parallelQueryResult.js";
/** @hidden */
export class GroupByEndpointComponent {
    executionContext;
    queryInfo;
    constructor(executionContext, queryInfo) {
        this.executionContext = executionContext;
        this.queryInfo = queryInfo;
    }
    groupings = new Map();
    aggregateResultArray = [];
    completed = false;
    hasMoreResults() {
        return this.executionContext.hasMoreResults();
    }
    async fetchMore(diagnosticNode) {
        if (this.completed) {
            return {
                result: undefined,
                headers: getInitialHeader(),
            };
        }
        const aggregateHeaders = getInitialHeader();
        const response = await this.executionContext.fetchMore(diagnosticNode);
        if (!response) {
            // If there are any groupings, consolidate and return them
            if (this.groupings.size > 0) {
                return this.consolidateGroupResults(aggregateHeaders);
            }
            return { result: undefined, headers: aggregateHeaders };
        }
        mergeHeaders(aggregateHeaders, response.headers);
        if (response.result === undefined ||
            !Array.isArray(response.result.buffer) ||
            response.result.buffer.length === 0) {
            // If there are any groupings, consolidate and return them
            if (this.groupings.size > 0) {
                return this.consolidateGroupResults(aggregateHeaders);
            }
            return { result: undefined, headers: aggregateHeaders };
        }
        const parallelResult = response.result;
        const dataToProcess = parallelResult.buffer;
        // Process GROUP BY aggregation logic
        for (const item of dataToProcess) {
            // If it exists, process it via aggregators
            if (item) {
                const group = item.groupByItems ? await hashObject(item.groupByItems) : emptyGroup;
                const aggregators = this.groupings.get(group);
                const payload = item.payload;
                if (aggregators) {
                    // Iterator over all results in the payload
                    for (const key of Object.keys(payload)) {
                        // in case the value of a group is null make sure we create a dummy payload with item2==null
                        const effectiveGroupByValue = payload[key]
                            ? payload[key]
                            : new Map().set("item2", null);
                        const aggregateResult = extractAggregateResult(effectiveGroupByValue);
                        aggregators.get(key).aggregate(aggregateResult);
                    }
                }
                else {
                    // This is the first time we have seen a grouping. Setup the initial result without aggregate values
                    const grouping = new Map();
                    this.groupings.set(group, grouping);
                    // Iterator over all results in the payload
                    for (const key of Object.keys(payload)) {
                        const aggregateType = this.queryInfo.groupByAliasToAggregateType[key];
                        // Create a new aggregator for this specific aggregate field
                        const aggregator = createAggregator(aggregateType);
                        grouping.set(key, aggregator);
                        if (aggregateType) {
                            const aggregateResult = extractAggregateResult(payload[key]);
                            aggregator.aggregate(aggregateResult);
                        }
                        else {
                            aggregator.aggregate(payload[key]);
                        }
                    }
                }
            }
        }
        if (this.executionContext.hasMoreResults()) {
            // Return empty buffer but preserve the structure and pass-through fields
            const result = createParallelQueryResult([], // empty buffer
            new Map());
            return { result, headers: aggregateHeaders };
        }
        else {
            return this.consolidateGroupResults(aggregateHeaders);
        }
    }
    consolidateGroupResults(aggregateHeaders) {
        for (const grouping of this.groupings.values()) {
            const groupResult = {};
            for (const [aggregateKey, aggregator] of grouping.entries()) {
                groupResult[aggregateKey] = aggregator.getResult();
            }
            this.aggregateResultArray.push(groupResult);
        }
        this.completed = true;
        // Return in the new structure format using the utility function
        const result = createParallelQueryResult(this.aggregateResultArray, new Map());
        return { result, headers: aggregateHeaders };
    }
}
//# sourceMappingURL=GroupByEndpointComponent.js.map