import chalk from 'chalk';
import ora from "ora";

export class PrettyPrint {
    static success(message) {
        console.log(chalk.green("✓") + " " + chalk.white(message));
    }

    static error(message) {
        console.log(chalk.red("x") + " " + chalk.white(message));
    }

    static red(message) {
        console.log(chalk.red(message));
    }

    static green(message) {
        console.log(chalk.green(message));
    }

    static white(message) {
        console.log(chalk.white(message));
    }


    static display(result, columns, rawJson = false) {
        try {
            console.log('\n');

            if (rawJson) console.log(JSON.stringify(result.data));
            else {
                if (typeof (result) === "object") {
                    if (result.data) {
                        if (Array.isArray(result.data)) {
                            if (result.data.length > 0) {
                                PrettyPrint._renderTable(result.data, columns);
                                if (result.offset !== undefined && result.limit !== undefined && result.total !== undefined) {
                                    const currentPage = result.offset / result.limit + 1;
                                    const maxPage = Math.ceil(result.total / result.limit);
                                    console.log(`Displayed: ${result.offset + 1}-${result.offset + result.data.length} / Total results: ${result.total} / Page ${currentPage}/${maxPage}`);
                                }
                            }
                            else {
                                console.log('No results');
                            }
                        }
                        else {
                            console.log(result.data);
                        }
                    }
                    else {
                        console.log(result);
                    }
                }
                else {
                    console.log(result);
                }
            }
        }
        catch (error) {
            this.logger.error(error);
        }
    }

    static _renderTable(rows, columns) {
        if (!rows || rows.length === 0) return;

        const headers = columns || Object.keys(rows[0]);
        const MAX_W   = 35;

        // Détecter les colonnes numériques (toutes les valeurs non-null sont des nombres)
        const isNum = {};
        for (const h of headers) {
            isNum[h] = rows.every(r => r[h] == null || typeof r[h] === 'number');
        }

        // Formater une valeur
        const fmt = (v) => {
            if (typeof v !== 'number') return String(v ?? '');
            return Number.isInteger(v)
                ? v.toLocaleString('fr-FR')
                : v.toLocaleString('fr-FR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
        };

        // Calculer les largeurs de colonnes
        const widths = {};
        for (const h of headers) widths[h] = h.length;
        for (const row of rows) {
            for (const h of headers) {
                widths[h] = Math.min(MAX_W, Math.max(widths[h], fmt(row[h]).length));
            }
        }

        // Tronquer une chaîne avec ellipsis
        const trunc = (s, w) => s.length > w ? s.slice(0, w - 1) + '…' : s;

        // Aligner une cellule
        const cell = (s, h) => {
            const t = trunc(s, widths[h]);
            return isNum[h] ? t.padStart(widths[h]) : t.padEnd(widths[h]);
        };

        // En-tête
        const headerLine = headers.map(h => chalk.bold(cell(h, h))).join('  ');
        const separator  = headers.map(h => chalk.dim('─'.repeat(widths[h]))).join('  ');
        console.log('  ' + headerLine);
        console.log('  ' + separator);

        // Lignes de données
        for (const row of rows) {
            const line = headers.map(h => {
                const v   = fmt(row[h]);
                const txt = cell(v, h);
                return isNum[h] ? chalk.yellow(txt) : txt;
            }).join('  ');
            console.log('  ' + line);
        }
        console.log('');
    }

    static spinner = ora({
        text: ' Request ongoing',
        spinner: 'dots',
    });

    // Référence à la dernière promesse withSpinner — utilisée par le mode shell
    // pour attendre la fin des appels non-awaités avant de rendre le prompt.
    static _lastSpinner    = Promise.resolve();
    // Statut de la dernière commande : 0 = succès, 1 = échec (lu par le shell pour $?)
    static _lastStatus     = 0;
    // Désactiver le spinner (animation + messages) — utilisé en mode script JS
    static _silentSpinner  = false;

    static withSpinner(callback, output_callback) {
        const p = PrettyPrint._runSpinner(callback, output_callback);
        PrettyPrint._lastSpinner = p;
        return p;
    }

    static async _runSpinner(callback, output_callback) {
        PrettyPrint._lastStatus = 0;
        const spin = !PrettyPrint._silentSpinner;

        if (spin) this.spinner.start();
        try {
            const json = await callback();
            if (spin) this.spinner.succeed(' Request succeeded');
            try {
                // output_callback peut être async (ex: export PDF) — on l'awaite
                const r = output_callback(json);
                if (r instanceof Promise) await r;
                return json;
            }
            catch (error) {
                console.log(`Result output error: ${error}`);
                PrettyPrint._lastStatus = 1;
            }
        }
        catch (error) {
            if (spin) this.spinner.fail(` Request failed: ${error}`);
            else       console.error(`  Request failed: ${error.message ?? error}`);
            PrettyPrint._lastStatus = 1;
        }
    }

    static isJSONString(str) {
        try {
            JSON.parse(str);
            return true;
        }
        catch (error) {
            return false;
        }
    }
}