import { Command, flags } from '@heroku-cli/command';
import { color, hux } from '@heroku/heroku-cli-util';
import { ux } from '@oclif/core/ux';
import errorInfo from '../../lib/apps/error_info.js';
const colorize = (level, s) => {
    switch (level) {
        case 'critical': {
            return color.failure(s);
        }
        case 'warning': {
            return color.warning(s);
        }
        case 'info': {
            return color.info(s);
        }
        default: {
            return s;
        }
    }
};
function buildErrorTable(errors, source) {
    return Object.keys(errors).map(name => {
        const count = errors[name];
        const info = errorInfo.find(e => e.name === name);
        if (info) {
            return {
                count,
                level: info.level,
                name,
                source,
                title: info.title,
            };
        }
        return {
            count,
            level: 'critical',
            name,
            source,
            title: 'unknown error',
        };
    });
}
const sumErrors = (errors) => {
    const summed = {};
    Object.keys(errors.data).forEach(key => {
        summed[key] = errors.data[key].reduce((a, b) => a + b, 0);
    });
    return summed;
};
export default class Errors extends Command {
    static description = 'view app errors';
    static flags = {
        app: flags.app({ required: true }),
        dyno: flags.boolean({ description: 'show only dyno errors' }),
        hours: flags.string({ default: '24', description: 'number of hours to look back (default 24)' }),
        json: flags.boolean({ description: 'output in json format' }),
        remote: flags.remote(),
        router: flags.boolean({ description: 'show only router errors' }),
    };
    async run() {
        const { flags } = await this.parse(Errors);
        const hours = Number.parseInt(flags.hours, 10);
        const NOW = new Date().toISOString();
        const YESTERDAY = new Date(Date.now() - (hours * 60 * 60 * 1000)).toISOString();
        const DATE_QUERY = `start_time=${YESTERDAY}&end_time=${NOW}&step=1h`;
        async function getAllDynoErrors(types) {
            const values = await Promise.all(types.map(dynoErrors));
            const memo = {};
            types.forEach((key, index) => {
                memo[key] = values[index];
            });
            return memo;
        }
        const routerErrors = () => this.heroku.get(`/apps/${flags.app}/router-metrics/errors?${DATE_QUERY}&process_type=web`, {
            hostname: 'api.metrics.herokai.com',
        }).then(({ body }) => sumErrors(body));
        const dynoErrors = (type) => this.heroku.get(`/apps/${flags.app}/formation/${type}/metrics/errors?${DATE_QUERY}`, {
            hostname: 'api.metrics.herokai.com',
        }).catch(error => {
            const { http } = error;
            // eslint-disable-next-line prefer-regex-literals
            const match = new RegExp('^invalid process_type provided', 'i');
            if (http && http.statusCode === 400 && http.body && http.body.message && match.test(http.body.message)) {
                return { body: { data: {} } };
            }
            throw error;
        }).then(rsp => {
            const { body } = rsp;
            return sumErrors(body);
        });
        const { body: formation } = await this.heroku.get(`/apps/${flags.app}/formation`);
        const types = formation.map((p) => p.type);
        const showDyno = flags.dyno || !flags.router;
        const showRouter = flags.router || !flags.dyno;
        const noDynoEmpty = Promise.resolve({});
        const noRouterEmpty = Promise.resolve({});
        const [dyno, router] = await Promise.all([
            showDyno ? getAllDynoErrors(types) : noDynoEmpty,
            showRouter ? routerErrors() : noRouterEmpty,
        ]);
        const errors = {
            dyno,
            router,
        };
        if (flags.json) {
            hux.styledJSON(errors);
        }
        else {
            let t = buildErrorTable(errors.router, 'router');
            for (const type of Object.keys(errors.dyno)) {
                t = t.concat(buildErrorTable(dyno[type], type));
            }
            if (t.length === 0) {
                ux.stdout(`No errors on ${color.app(flags.app)} in the last ${hours} hours`);
            }
            else {
                /* eslint-disable perfectionist/sort-objects */
                hux.table(t, {
                    Source: { get: ({ source }) => source },
                    Name: { get: ({ name, level }) => colorize(level, name) },
                    Level: { get: ({ level }) => colorize(level, level) },
                    title: { header: 'Desc' },
                    Count: { get: ({ count }) => count },
                }, { title: `=== Errors on ${color.app(flags.app)} in the last ${hours} hours\n`, titleOptions: { bold: true } });
                /* eslint-enable perfectionist/sort-objects */
            }
        }
    }
}
