#!/usr/bin/env node

import { termost } from "termost";
import { readThisNpmPackageVersion } from "./tools/readThisNpmPackageVersion";
import * as child_process from "child_process";
import { assertNoPnpmDlx } from "./tools/assertNoPnpmDlx";
import { getBuildContext } from "./shared/buildContext";

type CliCommandOptions = {
    projectDirPath: string | undefined;
};

assertNoPnpmDlx();

const program = termost<CliCommandOptions>(
    {
        name: "keycloakify",
        description: "Keycloakify CLI",
        version: readThisNpmPackageVersion()
    },
    {
        onException: error => {
            console.error(error);
            process.exit(1);
        }
    }
);

const optionsKeys: string[] = [];

program.option({
    key: "projectDirPath",
    name: (() => {
        const long = "project";
        const short = "p";

        optionsKeys.push(long, short);

        return { long, short };
    })(),
    description: [
        `For monorepos, path to the keycloakify project.`,
        "Example: `npx keycloakify build --project packages/keycloak-theme`",
        "https://docs.keycloakify.dev/build-options#project-or-p-cli-option"
    ].join(" "),
    defaultValue: undefined
});

function skip(_context: any, argv: { options: Record<string, unknown> }) {
    const unrecognizedOptionKey = Object.keys(argv.options).find(
        key => !optionsKeys.includes(key)
    );

    if (unrecognizedOptionKey !== undefined) {
        console.error(
            `keycloakify: Unrecognized option: ${
                unrecognizedOptionKey.length === 1 ? "-" : "--"
            }${unrecognizedOptionKey}`
        );
        process.exit(1);
    }

    return false;
}

program
    .command({
        name: "build",
        description: "Build the theme (default subcommand)."
    })
    .task({
        skip,
        handler: async ({ projectDirPath }) => {
            const { command } = await import("./keycloakify");

            await command({ buildContext: getBuildContext({ projectDirPath }) });
        }
    });

program
    .command<{
        port: number | undefined;
        keycloakVersion: string | undefined;
        realmJsonFilePath: string | undefined;
    }>({
        name: "start-keycloak",
        description:
            "Spin up a pre configured Docker image of Keycloak to test your theme."
    })
    .option({
        key: "port",
        name: (() => {
            const name = "port";

            optionsKeys.push(name);

            return name;
        })(),
        description: ["Keycloak server port.", "Example `--port 8085`"].join(" "),
        defaultValue: undefined
    })
    .option({
        key: "keycloakVersion",
        name: (() => {
            const name = "keycloak-version";

            optionsKeys.push(name);

            return name;
        })(),
        description: [
            "Use a specific version of Keycloak.",
            "Example `--keycloak-version 21.1.1`"
        ].join(" "),
        defaultValue: undefined
    })
    .option({
        key: "realmJsonFilePath",
        name: (() => {
            const name = "import";

            optionsKeys.push(name);

            return name;
        })(),
        defaultValue: undefined,
        description: [
            "Import your own realm configuration file",
            "Example `--import path/to/myrealm-realm.json`"
        ].join(" ")
    })
    .task({
        skip,
        handler: async ({ projectDirPath, keycloakVersion, port, realmJsonFilePath }) => {
            const { command } = await import("./start-keycloak");

            await command({
                buildContext: getBuildContext({ projectDirPath }),
                cliCommandOptions: { keycloakVersion, port, realmJsonFilePath }
            });
        }
    });

program
    .command({
        name: "eject-page",
        description: "Eject a Keycloak page."
    })
    .task({
        skip,
        handler: async ({ projectDirPath }) => {
            const { command } = await import("./eject-page");

            await command({ buildContext: getBuildContext({ projectDirPath }) });
        }
    });

program
    .command({
        name: "add-story",
        description: "Add *.stories.tsx file for a specific page to in your Storybook."
    })
    .task({
        skip,
        handler: async ({ projectDirPath }) => {
            const { command } = await import("./add-story");

            await command({ buildContext: getBuildContext({ projectDirPath }) });
        }
    });

program
    .command({
        name: "initialize-email-theme",
        description: "Initialize an email theme."
    })
    .task({
        skip,
        handler: async ({ projectDirPath }) => {
            const { command } = await import("./initialize-email-theme");

            await command({ buildContext: getBuildContext({ projectDirPath }) });
        }
    });

program
    .command({
        name: "initialize-account-theme",
        description: "Initialize the account theme."
    })
    .task({
        skip,
        handler: async ({ projectDirPath }) => {
            const { command } = await import("./initialize-account-theme");

            await command({ buildContext: getBuildContext({ projectDirPath }) });
        }
    });

program
    .command({
        name: "copy-keycloak-resources-to-public",
        description:
            "(Webpack/Create-React-App only) Copy Keycloak default theme resources to the public directory."
    })
    .task({
        skip,
        handler: async ({ projectDirPath }) => {
            const { command } = await import("./copy-keycloak-resources-to-public");

            await command({ buildContext: getBuildContext({ projectDirPath }) });
        }
    });

program
    .command({
        name: "update-kc-gen",
        description:
            "(Webpack/Create-React-App only) Create/update the kc.gen.ts file in your project."
    })
    .task({
        skip,
        handler: async ({ projectDirPath }) => {
            const { command } = await import("./update-kc-gen");

            await command({ buildContext: getBuildContext({ projectDirPath }) });
        }
    });

// Fallback to build command if no command is provided
{
    const [, , ...rest] = process.argv;

    if (
        rest.length === 0 ||
        (rest[0].startsWith("-") && rest[0] !== "--help" && rest[0] !== "-h")
    ) {
        const { status } = child_process.spawnSync(
            "npx",
            ["keycloakify", "build", ...rest],
            {
                stdio: "inherit"
            }
        );

        process.exit(status ?? 1);
    }
}