import { Command } from '@heroku-cli/command';
import { color, hux } from '@heroku/heroku-cli-util';
import { ux } from '@oclif/core/ux';
import { execSync } from 'child_process';
import * as path from 'path';
import * as process from 'process';
import img from 'term-img';
import { fileURLToPath } from 'url';
import { lazyModuleLoader } from '../lib/lazy-module-loader.js';
import { ago } from '../lib/time.js';
import { sparkline } from '../lib/utils/sparkline.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const empty = (o) => Object.keys(o).length === 0;
function displayErrors(metrics, _) {
    let errors = [];
    if (metrics.routerErrors) {
        errors = errors.concat(Object.entries(metrics.routerErrors.data)
            .map(e => color.failure(`${_.sum(e[1])} ${e[0]}`)));
    }
    if (metrics.dynoErrors) {
        metrics.dynoErrors.filter(Boolean)
            .forEach(dynoErrors => {
            errors = errors.concat(Object.entries(dynoErrors?.data || {})
                .map(e => color.failure(`${_.sum(e[1])} ${e[0]}`)));
        });
    }
    if (errors.length > 0)
        ux.stdout(`  ${label('Errors:')} ${errors.join(dim(', '))} (see details with ${color.code('heroku apps:errors')})`);
}
function displayFormation(formation, _) {
    formation = _.groupBy(formation, 'size');
    formation = _.map(formation, (p, size) => `${bold(_.sumBy(p, 'quantity').toString())} | ${size}`);
    ux.stdout(`  ${label('Dynos:')} ${formation.join(', ')}`);
}
function displayMetrics(metrics, _) {
    function rpmSparkline() {
        if (['win32', 'windows'].includes(process.platform))
            return '';
        const points = [];
        Object.values(metrics.routerStatus?.data || {})
            .forEach(cur => {
            for (const [i, element] of cur.entries()) {
                const j = Math.floor(i / 3);
                points[j] = (points[j] || 0) + element;
            }
        });
        points.pop();
        return dim(sparkline(points)) + ' last 24 hours rpm';
    }
    let ms = '';
    let rpm = '';
    if (metrics.routerLatency && !empty(metrics.routerLatency.data)) {
        const latency = metrics.routerLatency.data['latency.ms.p50'];
        if (!empty(latency))
            ms = `${_.round(_.mean(latency))} ms `;
    }
    if (metrics.routerStatus && !empty(metrics.routerStatus.data)) {
        rpm = `${_.round(_.sum(Object.values(metrics.routerStatus.data).flat()) / 24 / 60)} rpm ${rpmSparkline()}`;
    }
    if (rpm || ms)
        ux.stdout(`  ${label('Metrics:')} ${ms}${rpm}`);
}
function displayNotifications(notifications) {
    if (!notifications)
        return;
    notifications = notifications.filter(n => !n.read);
    if (notifications.length > 0) {
        ux.stdout(`\nYou have ${color.yellow(notifications.length.toString())} unread notifications. Read them with ${color.code('heroku notifications')}`);
    }
}
const dim = (s) => color.gray(s);
const bold = (s) => color.bold(s);
const label = (s) => color.label(s);
const fetchMetrics = async (apps, heroku) => {
    const NOW = new Date().toISOString();
    const YESTERDAY = new Date(Date.now() - (24 * 60 * 60 * 1000)).toISOString();
    const date = `start_time=${YESTERDAY}&end_time=${NOW}&step=1h`;
    const metricsData = await Promise.all(apps.map(app => {
        const types = app.formation.map((p) => p.type);
        const dynoErrorsPromise = Promise.all(types.map((type) => heroku.get(`/apps/${app.app.name}/formation/${type}/metrics/errors?${date}`, { hostname: 'api.metrics.herokai.com' }).catch(() => { })));
        return Promise.all([
            dynoErrorsPromise,
            heroku.get(`/apps/${app.app.name}/router-metrics/latency?${date}&process_type=${types[0]}`, { hostname: 'api.metrics.herokai.com' }).catch(() => { }),
            heroku.get(`/apps/${app.app.name}/router-metrics/errors?${date}&process_type=${types[0]}`, { hostname: 'api.metrics.herokai.com' }).catch(() => { }),
            heroku.get(`/apps/${app.app.name}/router-metrics/status?${date}&process_type=${types[0]}`, { hostname: 'api.metrics.herokai.com' }).catch(() => { }),
        ]);
    }));
    return metricsData.map(([dynoErrors, routerLatency, routerErrors, routerStatus]) => ({
        dynoErrors, routerErrors: routerErrors?.body, routerLatency: routerLatency?.body, routerStatus: routerStatus?.body,
    }));
};
export default class Dashboard extends Command {
    static baseFlags = Command.baseFlagsWithoutPrompt();
    static description = 'display information about favorite apps';
    static hidden = true;
    static promptFlagActive = false;
    static topic = 'dashboard';
    async run() {
        const _ = await lazyModuleLoader.loadLodash();
        if (!this.heroku.auth && process.env.IS_HEROKU_TEST_ENV !== 'true') {
            execSync('heroku help', { stdio: 'inherit' });
            return;
        }
        const favoriteApps = async () => {
            const { body: apps } = await this.heroku.get('/favorites?type=app', {
                hostname: 'particleboard.heroku.com',
            });
            return apps.map(app => app.resource_name);
        };
        try {
            const imagePath = path.join(__dirname, '..', '..', 'assets', 'heroku.png');
            let image = img(imagePath, { fallback() { } });
            if (image) {
                // Add filename to iTerm2 inline image protocol for better permission prompts
                // Format: ]1337;File=name=<base64>;inline=1;size=...
                const filename = 'heroku.png';
                const nameBase64 = Buffer.from(filename).toString('base64');
                image = image.replace('File=inline=1;', `File=name=${nameBase64};inline=1;`);
                ux.stdout(image);
                ux.stdout('\n');
            }
        }
        catch {
            // Image display not supported in this terminal
        }
        ux.action.start('Loading');
        const apps = await favoriteApps();
        const [{ body: teams }, notificationsResponse, appsWithMoreInfo] = await Promise.all([
            this.heroku.get('/teams'),
            this.heroku.get('/user/notifications', { hostname: 'telex.heroku.com' })
                .catch(() => null),
            Promise.all(apps.map(async (appID) => {
                const [{ body: app }, { body: formation }, pipelineResponse] = await Promise.all([
                    this.heroku.get(`/apps/${appID}`),
                    this.heroku.get(`/apps/${appID}/formation`),
                    this.heroku.get(`/apps/${appID}/pipeline-couplings`)
                        .catch(() => null),
                ]);
                return {
                    app, formation, pipeline: pipelineResponse?.body,
                };
            })),
        ]);
        const metrics = await fetchMetrics(appsWithMoreInfo, this.heroku);
        ux.action.stop();
        if (apps.length > 0)
            displayApps(appsWithMoreInfo, metrics, _);
        else
            ux.warn(`Add apps to this dashboard by favoriting them with ${color.code('heroku apps:favorites:add')}`);
        ux.stdout(`See all add-ons with ${color.code('heroku addons')}`);
        const sampleTeam = _.sortBy(teams.filter(o => o.role !== 'collaborator'), o => new Date(o.created_at || ''))[0];
        if (sampleTeam)
            ux.stdout(`See all apps in ${color.team(sampleTeam.name || '')} with ${color.code('heroku apps --team ' + sampleTeam.name)}`);
        ux.stdout(`See all apps with ${color.code('heroku apps --all')}`);
        displayNotifications(notificationsResponse?.body);
        ux.stdout(`\nSee other CLI commands with ${color.code('heroku help')}\n`);
    }
}
function displayApps(apps, appsMetrics, _) {
    const getOwner = (owner) => owner?.email?.endsWith('@herokumanager.com') ? owner.email.split('@')[0] : owner?.email;
    const zipped = _.zip(apps, appsMetrics);
    for (const a of zipped) {
        const app = a[0];
        const metrics = a[1];
        hux.styledHeader(color.app(app.app.name || ''));
        ux.stdout(`  ${label('Owner:')} ${color.user(getOwner(app.app.owner) || '')}`);
        if (app.pipeline) {
            ux.stdout(`  ${label('Pipeline:')} ${color.pipeline(app.pipeline.pipeline?.name || '')}`);
        }
        displayFormation(app.formation, _);
        ux.stdout(`  ${label('Last release:')} ${ago(new Date(app.app.released_at || ''))}`);
        displayMetrics(metrics, _);
        displayErrors(metrics, _);
        ux.stdout();
    }
}
