import * as path from 'path'
import { promises as fs } from 'fs'
import { FileMigrationProvider, Migrator, type MigrationResultSet } from 'kysely/migration'
import { db } from './db'
import { argv } from 'bun'
import { env, log } from '../util'

type HandlerResult = {
    message: string;
};

function processResults(label: string, { error, results }: MigrationResultSet): string {
    let message = "";

    results?.forEach((it) => {
        if (it.status === 'Success') {
            message = `migration ${label} succeeded: "${it.migrationName}"`;
        } else if (it.status === 'Error') {
            throw error ?? new Error(`migration ${label} failed: "${it.migrationName}"`);
        }
    });

    if (error) throw error;

    return message;
}

const handlers: Record<string, (migrator: Migrator) => Promise<HandlerResult>> = {
    "up": async function (migrator: Migrator) {
        const resultSet = await migrator.migrateUp();
        const message = processResults("up", resultSet);

        return { message };
    },
    "down": async function (migrator: Migrator) {
        const resultSet = await migrator.migrateDown();
        const message = processResults("down", resultSet);

        return { message };
    },
    "latest": async function (migrator: Migrator) {
        const resultSet = await migrator.migrateToLatest();
        const message = processResults("to latest", resultSet);

        return { message };
    },
    "rollback": async function (migrator: Migrator) {
        let count = 0;

        while (true) {
            const resultSet = await migrator.migrateDown();
            if (!resultSet.results?.length) break;

            processResults("rollback", resultSet);
            ++count;
        }

        return { message: `rolled back ${count} migration(s)` };
    },
    "reset": async function (migrator: Migrator) {
        if (env.NODE_ENV !== "development") throw new Error("Development only command");

        if (typeof handlers["rollback"] !== "function") throw new Error("Missing rollback handler");
        await handlers["rollback"](migrator);

        if (typeof handlers["latest"] !== "function") throw new Error("Missing latest handler");
        await handlers["latest"](migrator);

        return { message: "database fully reset" };
    }
};

// Main
(async () => {
    const command = argv[2] ?? "";
    if (!handlers[command]) throw new Error("Unrecognized command");

    const answer = prompt(`Run "${command}" migration against ${env.DATABASE_URL}? (y/N)`);
    if (answer?.trim().toLowerCase() !== "y") {
        log.info("Aborted");
        await db.destroy();
        return;
    }

    const migrator = new Migrator({
        db,
        provider: new FileMigrationProvider({
            fs,
            path,
            // This needs to be an absolute path.
            migrationFolder: path.join(__dirname, "migrations"),
        }),
    });

    try {
        const result = await handlers[command](migrator);
        log.info({ name: command, result }, "finished");
    }
    catch (err) {
        log.error({ name: command, err }, "failed");
        process.exit(1)
    }
    finally {
        await db.destroy()
    }
})();
