import { UserInput } from "./../input/input.js";
import { DefaultCommandOptions, DefaultCommandValues } from "./constants.js";
import { getDependencies, getPackageVersion } from "./../utils/fs.js";
import { printBanner } from "./../output/common.js";
import { LoggedClass, LoggedEnvironment, Logging, } from "@decaf-ts/logging";
/**
 * @class Command
 * @abstract
 * @template I - The type of input options for the command.
 * @template R - The return type of the command execution.
 * @memberOf module:utils
 * @description Abstract base class for command implementation.
 * @summary Provides a structure for creating command-line interface commands with input handling, logging, and execution flow.
 *
 * @param {string} name - The name of the command.
 * @param {CommandOptions<I>} [inputs] - The input options for the command.
 * @param {string[]} [requirements] - The list of required dependencies for the command.
 */
export class Command extends LoggedClass {
    constructor(name, inputs = {}, requirements = []) {
        super();
        this.name = name;
        this.inputs = inputs;
        this.requirements = requirements;
        if (!Command.log) {
            Object.defineProperty(Command, "log", {
                writable: false,
                value: Logging.for(Command.name),
            });
        }
        this.inputs = Object.assign({}, DefaultCommandOptions, inputs);
    }
    /**
     * @protected
     * @async
     * @description Checks if all required dependencies are present.
     * @summary Retrieves the list of dependencies and compares it against the required dependencies for the command.
     * @returns {Promise<void>} A promise that resolves when the check is complete.
     *
     * @mermaid
     * sequenceDiagram
     *   participant Command
     *   participant getDependencies
     *   participant Set
     *   Command->>getDependencies: Call
     *   getDependencies-->>Command: Return {prod, dev, peer}
     *   Command->>Set: Create Set from prod, dev, peer
     *   Set-->>Command: Return unique dependencies
     *   Command->>Command: Compare against requirements
     *   alt Missing dependencies
     *     Command->>Command: Add to missing list
     *   end
     *   Note over Command: If missing.length > 0, handle missing dependencies
     */
    async checkRequirements() {
        const { prod, dev, peer } = await getDependencies();
        const missing = [];
        const fullList = Array.from(new Set([...prod, ...dev, ...peer]).values()).map((d) => d.name);
        for (const dep of this.requirements)
            if (!fullList.includes(dep))
                missing.push(dep);
        if (!missing.length)
            return;
    }
    /**
     * @protected
     * @description Provides help information for the command.
     * @summary This method should be overridden in derived classes to provide specific help information.
     * @param {ParseArgsResult} args - The parsed command-line arguments.
     * @returns {void}
     */
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    help(args) {
        return this.log.info(`This is help. I'm no use because I should have been overridden.`);
    }
    /**
     * @async
     * @description Executes the command.
     * @summary This method handles the overall execution flow of the command, including parsing arguments,
     * setting up logging, checking for version or help requests, and running the command.
     * @returns {Promise<R | string | void>} A promise that resolves with the command's result.
     *
     * @mermaid
     * sequenceDiagram
     *   participant Command
     *   participant UserInput
     *   participant Logging
     *   participant getPackageVersion
     *   participant printBanner
     *   Command->>UserInput: parseArgs(inputs)
     *   UserInput-->>Command: Return ParseArgsResult
     *   Command->>Command: Process options
     *   Command->>Logging: setConfig(options)
     *   alt version requested
     *     Command->>getPackageVersion: Call
     *     getPackageVersion-->>Command: Return version
     *   else help requested
     *     Command->>Command: help(args)
     *   else banner requested
     *     Command->>printBanner: Call
     *   end
     *   Command->>Command: run(args)
     *   alt error occurs
     *     Command->>Command: Log error
     *   end
     *   Command-->>Command: Return result
     */
    async execute() {
        const args = UserInput.parseArgs(this.inputs);
        const env = LoggedEnvironment.accumulate(DefaultCommandValues).accumulate(args.values);
        const { version, help, banner } = env;
        if (version) {
            return getPackageVersion();
        }
        if (help) {
            return this.help(args);
        }
        if (banner)
            printBanner(this.log.for(printBanner, {
                timestamp: false,
                style: false,
                context: false,
                logLevel: false,
            }));
        let result;
        // eslint-disable-next-line no-useless-catch
        try {
            result = await this.run(env);
        }
        catch (e) {
            throw e;
        }
        return result;
    }
}
//# sourceMappingURL=command.js.map