"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseSingle = exports.parse = exports.assertNoErrors = exports.ResultError = void 0;
const grammar_1 = require("./grammar");
/**
 * A ResultError is thrown when a query generates errorful results from Influx.
 */
class ResultError extends Error {
    constructor(message) {
        super();
        this.message = `Error from InfluxDB: ${message}`;
    }
}
exports.ResultError = ResultError;
function groupMethod(matcher) {
    // We do a tiny bit of 'custom' deep equality checking here, taking
    // advantage of the fact that the tag keys are consistent across all
    // series results. This lets us match groupings much more efficiently,
    // ~6000x faster than the fastest vanilla equality checker (lodash)
    // when operating on large (~100,000 grouping) sets.
    const srcKeys = this.groupsTagsKeys;
    const dstKeys = Object.keys(matcher);
    if (srcKeys.length === 0 || srcKeys.length !== dstKeys.length) {
        return [];
    }
    L: for (let row of this.groupRows) {
        // eslint-disable-line no-labels
        for (let src of srcKeys) {
            if (row.tags[src] !== matcher[src]) {
                continue L; // eslint-disable-line no-labels
            }
        }
        return row.rows;
    }
    return [];
}
function groupsMethod() {
    return this.groupRows;
}
/**
 * Inner parsing function which unpacks the series into a table and attaches
 * methods to the array. This is quite optimized and a bit of a mess to read,
 * but it's all fairly easy procedural logic.
 *
 * We do this instead of subclassing Array since subclassing has some
 * undesirable side-effects. For example, calling .slice() on the array
 * makes it impossible to preserve groups as would be necessary if it's
 * subclassed.
 */
function parseInner(series = [], precision) {
    const results = [];
    results.groupsTagsKeys =
        series.length && series[0].tags ? Object.keys(series[0].tags) : [];
    const tags = results.groupsTagsKeys;
    let nextGroup = [];
    results.groupRows = new Array(series.length); // Tslint:disable-line
    for (let i = 0; i < series.length; i += 1, results.length) {
        const { columns = [], values = [] } = series[i];
        for (let value of values) {
            const obj = {};
            for (let j = 0; j < columns.length; j += 1) {
                if (columns[j] === "time") {
                    obj.time = grammar_1.isoOrTimeToDate(value[j], precision);
                }
                else {
                    obj[columns[j]] = value[j];
                }
            }
            for (let tag of tags) {
                obj[tag] = series[i].tags[tag];
            }
            results.push(obj);
            nextGroup.push(obj);
        }
        results.groupRows[i] = {
            name: series[i].name,
            rows: nextGroup,
            tags: series[i].tags || {},
        };
        nextGroup = [];
    }
    results.group = groupMethod;
    results.groups = groupsMethod;
    return results;
}
/**
 * Checks if there are any errors in the IResponse and, if so, it throws them.
 * @private
 * @throws {ResultError}
 */
function assertNoErrors(res) {
    for (let result of res.results) {
        const { error } = result;
        if (error) {
            throw new ResultError(error);
        }
    }
    return res;
}
exports.assertNoErrors = assertNoErrors;
/**
 * From parses out a response to a result or list of responses.
 * There are three situations we cover here:
 *  1. A single query without groups, like `select * from myseries`
 *  2. A single query with groups, generated with a `group by` statement
 *     which groups by series *tags*, grouping by times is case (1)
 *  3. Multiple queries of types 1 and 2
 * @private
 */
function parse(res, precision) {
    assertNoErrors(res);
    if (res.results.length === 1) {
        // Normalize case 3
        return parseInner(res.results[0].series, precision);
    }
    return res.results.map((result) => parseInner(result.series, precision));
}
exports.parse = parse;
/**
 * ParseSingle asserts that the response contains a single result,
 * and returns that result.
 * @throws {Error} if the number of results is not exactly one
 * @private
 */
function parseSingle(res, precision) {
    assertNoErrors(res);
    if (res.results.length !== 1) {
        throw new Error("node-influx expected the results length to equal 1, but " +
            `it was ${0}. Please report this here: https://git.io/influx-err`);
    }
    return parseInner(res.results[0].series, precision);
}
exports.parseSingle = parseSingle;
