import ImmutableBytes from "../snapshot/ImmutableBytes.js";
function isSnapshotPropObject(value) {
    return value !== undefined &&
        value !== null &&
        typeof value === 'object' &&
        !(value instanceof ImmutableBytes) &&
        !Array.isArray(value);
}
function resolvePropertyPath(obj, segments) {
    const first = segments[0];
    if (first === undefined) {
        return undefined;
    }
    let value = obj[first];
    for (let i = 1; i < segments.length; i++) {
        if (!isSnapshotPropObject(value)) {
            return undefined;
        }
        const segment = segments[i];
        if (segment === undefined) {
            return undefined;
        }
        value = value[segment];
    }
    return value;
}
function computeSingleAggregate(key, values) {
    if (values.length === 0) {
        return 0;
    }
    if (key === 'sum') {
        return values.reduce((a, b) => a + b, 0);
    }
    if (key === 'avg') {
        return values.reduce((a, b) => a + b, 0) / values.length;
    }
    if (key === 'min') {
        return Math.min(...values);
    }
    return Math.max(...values);
}
const NUMERIC_AGGREGATE_KEYS = ['sum', 'avg', 'min', 'max'];
function activeAggregateKeys(spec) {
    return NUMERIC_AGGREGATE_KEYS.filter((key) => spec[key] !== undefined && spec[key] !== null);
}
function buildAggMap(activeAggs, spec) {
    const aggMap = new Map();
    for (const key of activeAggs) {
        const path = spec[key];
        if (path === undefined) {
            continue;
        }
        aggMap.set(key, {
            segments: path.replace(/^props\./, '').split('.'),
            values: [],
        });
    }
    return aggMap;
}
function collectAggValues(propsList, aggMap) {
    for (const propsRecord of propsList) {
        for (const { segments, values } of aggMap.values()) {
            const value = resolvePropertyPath(propsRecord, segments);
            if (typeof value === 'number' && !Number.isNaN(value)) {
                values.push(value);
            }
        }
    }
}
async function collectProps(strand, getProps) {
    const props = [];
    for (const nodeId of strand) {
        props.push(await getProps(nodeId));
    }
    return props;
}
async function computeNumericAggregates(params) {
    const aggMap = buildAggMap(params.activeAggs, params.spec);
    const propsList = await collectProps(params.strand, params.getProps);
    collectAggValues(propsList, aggMap);
    for (const [key, { values }] of aggMap) {
        params.result[key] = computeSingleAggregate(key, values);
    }
    return params.result;
}
export async function runAggregate(params) {
    const result = { stateHash: params.stateHash };
    if (params.spec.count === true) {
        result.count = params.strand.length;
    }
    const activeAggs = activeAggregateKeys(params.spec);
    if (activeAggs.length === 0) {
        return result;
    }
    return await computeNumericAggregates({
        strand: params.strand,
        getProps: params.getProps,
        activeAggs,
        spec: params.spec,
        result,
    });
}
