"use strict";
/*
 * @adonisjs/lucid
 *
 * (c) Harminder Virk <virk@adonisjs.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const pretty_hrtime_1 = __importDefault(require("pretty-hrtime"));
const standalone_1 = require("@adonisjs/core/build/standalone");
const utils_1 = require("../../src/utils");
const prettyPrint_1 = require("../../src/Helpers/prettyPrint");
/**
 * Base class to execute migrations and print logs
 */
class MigrationsBase extends standalone_1.BaseCommand {
    constructor() {
        super(...arguments);
        /**
         * Should print one-liner compact output
         */
        Object.defineProperty(this, "compactOutput", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: false
        });
    }
    /**
     * Not a valid connection
     */
    printNotAValidConnection(connection) {
        this.logger.error(`"${connection}" is not a valid connection name. Double check "config/database" file`);
    }
    /**
     * Prompts to take consent for running migrations in production
     */
    async takeProductionConstent() {
        /**
         * Do not prompt when CLI is not interactive
         */
        if (!this.isInteractive) {
            return false;
        }
        const question = 'You are in production environment. Want to continue running migrations?';
        try {
            return await this.prompt.confirm(question);
        }
        catch (error) {
            return false;
        }
    }
    /**
     * Returns beautified log message string
     */
    printLogMessage(file, direction) {
        const color = file.status === 'pending' ? 'gray' : file.status === 'completed' ? 'green' : 'red';
        const arrow = this.colors[color]('❯');
        const message = file.status === 'pending'
            ? direction === 'up'
                ? 'migrating'
                : 'reverting'
            : file.status === 'completed'
                ? direction === 'up'
                    ? 'migrated'
                    : 'reverted'
                : 'error';
        this.logger.logUpdate(`${arrow} ${this.colors[color](message)} ${file.file.name}`);
    }
    /**
     * Pretty print sql queries of a file
     */
    prettyPrintSql(file, connectionName) {
        console.log(this.logger.colors.gray(`------------- ${file.file.name} -------------`));
        console.log();
        file.queries.map((sql) => {
            (0, prettyPrint_1.prettyPrint)({
                connection: connectionName,
                sql: sql,
                ddl: true,
                method: (0, utils_1.getDDLMethod)(sql),
                bindings: [],
            });
            console.log();
        });
        console.log(this.logger.colors.gray('------------- END -------------'));
    }
    /**
     * Log final status with verbose output
     */
    logVerboseFinalStatus(migrator, duration) {
        switch (migrator.status) {
            case 'completed':
                const completionMessage = migrator.direction === 'up' ? 'Migrated in' : 'Reverted in';
                console.log(`\n${completionMessage} ${this.colors.cyan((0, pretty_hrtime_1.default)(duration))}`);
                break;
            case 'skipped':
                const message = migrator.direction === 'up' ? 'Already up to date' : 'Already at latest batch';
                console.log(this.colors.cyan(message));
                break;
            case 'error':
                this.logger.fatal(migrator.error);
                this.exitCode = 1;
                break;
        }
    }
    /**
     * Log final status with compact output
     */
    logCompactFinalStatus(processedFiles, migrator, duration) {
        let output = '';
        let message = '';
        let isUp = migrator.direction === 'up';
        switch (migrator.status) {
            case 'completed':
                message = `❯ ${isUp ? 'Executed' : 'Reverted'} ${processedFiles.size} migrations`;
                output = this.colors.grey(message + ` (${(0, pretty_hrtime_1.default)(duration)})`);
                break;
            case 'skipped':
                message = `❯ ${isUp ? 'Already up to date' : 'Already at latest batch'}`;
                output = this.colors.grey(message);
                break;
            case 'error':
                const skippedMigrations = Object.values(migrator.migratedFiles).filter((file) => file.status === 'pending').length;
                message = `❯ Executed ${processedFiles.size} migrations, 1 error, ${skippedMigrations} skipped`;
                console.log(this.colors.red(message));
                console.log('\n' + this.colors.red(migrator.error.message));
                this.exitCode = 1;
                break;
        }
        this.logger.log(output);
    }
    /**
     * Runs the migrations using the migrator
     */
    async runMigrations(migrator, connectionName) {
        /**
         * Pretty print SQL in dry run and return early
         */
        if (migrator.dryRun) {
            await migrator.run();
            Object.keys(migrator.migratedFiles).forEach((file) => {
                this.prettyPrintSql(migrator.migratedFiles[file], connectionName);
            });
            return;
        }
        /**
         * A set of files processed and emitted using event emitter.
         */
        const processedFiles = new Set();
        let start;
        let duration;
        /**
         * Starting to process a new migration file
         */
        migrator.on('migration:start', (file) => {
            processedFiles.add(file.file.name);
            if (!this.compactOutput) {
                this.printLogMessage(file, migrator.direction);
            }
        });
        /**
         * Migration completed
         */
        migrator.on('migration:completed', (file) => {
            if (!this.compactOutput) {
                this.printLogMessage(file, migrator.direction);
                this.logger.logUpdatePersist();
            }
        });
        /**
         * Migration error
         */
        migrator.on('migration:error', (file) => {
            if (!this.compactOutput) {
                this.printLogMessage(file, migrator.direction);
                this.logger.logUpdatePersist();
            }
        });
        /**
         * Migration completed
         */
        migrator.on('upgrade:version', ({ from, to }) => {
            this.logger.info(`Upgrading migrations version from "${from}" to "${to}"`);
        });
        migrator.on('start', () => (start = process.hrtime()));
        migrator.on('end', () => (duration = process.hrtime(start)));
        /**
         * Run migrations
         */
        await migrator.run();
        /**
         * Log all pending files. This will happen, when one of the migration
         * fails with an error and then the migrator stops emitting events.
         */
        Object.keys(migrator.migratedFiles).forEach((file) => {
            if (!processedFiles.has(file) && !this.compactOutput) {
                this.printLogMessage(migrator.migratedFiles[file], migrator.direction);
            }
        });
        if (this.compactOutput) {
            this.logCompactFinalStatus(processedFiles, migrator, duration);
        }
        else {
            this.logVerboseFinalStatus(migrator, duration);
        }
    }
}
exports.default = MigrationsBase;
