import * as crypto from "crypto";
// @ts-ignore // does not have types
import mid from "node-machine-id";
import { getConfigFromWizard } from "./configuration/getConfigFromWizard.mjs";
import { getConfigFromCliArgs } from "./configuration/getConfigFromCliArgs.mjs";
import { getConfigTsFromConfigFile } from "./configuration/getConfigFromTsConfigFile.mjs";
import { configFileDefaultName } from "./constants.mjs";
import { ResourceDiscovery } from "./resourceDiscovery.mjs";
import { Logger } from "./logger.mjs";
let config;
const lambdas = {};
let lambdasList = undefined;
/**
 * Read configuration from CLI args, config file or wizard
 */
async function readConfig() {
    const supportedFrameworks = ResourceDiscovery.getSupportedFrameworksNames();
    const configFromCliArgs = await getConfigFromCliArgs(supportedFrameworks);
    Configuration.setConfig(configFromCliArgs); // not complete config
    const currentFramework = await ResourceDiscovery.getCurrentFrameworkName();
    Logger.setVerbose(configFromCliArgs.verbose === true);
    const configFileName = configFromCliArgs.config || configFileDefaultName;
    const configFromConfigFile = (await getConfigTsFromConfigFile(configFileName))
        ?.default;
    if (configFromCliArgs.wizard) {
        const configFromWizard = await getConfigFromWizard({
            configFromCliArgs,
            supportedFrameworks,
            currentFramework,
            currentConfig: configFromConfigFile,
        });
        const debuggerId = await generateDebuggerId(!!configFromWizard.observable);
        setConfig({
            ...configFromWizard,
            debuggerId,
            start: false, // don't start the debugger after the wizard
        });
    }
    else {
        const configMerged = { ...configFromCliArgs, ...configFromConfigFile };
        const debuggerId = await generateDebuggerId(!!configMerged.observable);
        setConfig({
            ...configMerged,
            debuggerId,
            start: true,
        });
    }
}
/**
 * Generate a unique debugger id based on the current environment, args and directory
 * @param observableMode If the debugger is in observable mode then the id is always the same
 * @returns
 */
async function generateDebuggerId(observableMode) {
    if (observableMode) {
        // if we are in observable mode, we don't need to generate a unique id
        return "OBSERVABLE_MODE";
    }
    const args = process.argv;
    const argsString = args.join(" ");
    const deviceId = await mid.machineId();
    // get the current directory
    const currentDir = process.cwd();
    // combine and hash the args and the device id
    const hash = crypto.createHash("md5");
    hash.update(argsString);
    hash.update(deviceId);
    hash.update(currentDir);
    return hash.digest("hex");
}
/**
 * Add a Lambda to the configuration
 * @param props
 */
function addLambda(props) {
    lambdas[props.functionName] = {
        functionId: props.functionName,
        ...props,
    };
}
/**
 * Get a Lambda by functionId
 * @param functionId
 * @returns
 */
async function getLambda(functionId) {
    const lambda = lambdas[functionId];
    if (lambda)
        return lambda;
    throw new Error(`Lambda not found: ${functionId}`);
}
/**
 * Get all Lambdas
 * @returns
 */
function getLambdas() {
    return Object.values(lambdas);
}
/**
 * Discover Lambdas
 */
async function discoverLambdas() {
    let lambdasListNew = await ResourceDiscovery.getLambdas(config);
    let noFramework = false;
    if (lambdasListNew === undefined) {
        noFramework = true;
    }
    if (config.getLambdas) {
        lambdasListNew = await config.getLambdas(lambdasListNew);
    }
    if (!lambdasListNew) {
        if (noFramework) {
            throw new Error(`No framework was found, or Lambdas provided via ${configFileDefaultName} config file. If you are using monorepo, you should configure a subfolder via config/cli parameter or run the Lambda Live Debugger from a subfolder that contains the framework project.`);
        }
        else {
            throw new Error(`No Lambdas were provided via ${configFileDefaultName} config file, but some were found via the framework. Check the settings 'getLambdas' or remove it.`);
        }
    }
    // is resourcesTemp different than resources in content
    saveDiscoveredLambdas(lambdasListNew);
}
/**
 * Save discovered Lambdas
 * @param lambdasListNew
 */
function saveDiscoveredLambdas(lambdasListNew) {
    if (!lambdasList ||
        JSON.stringify(lambdasListNew) !== JSON.stringify(lambdasList)) {
        lambdasList = lambdasListNew;
        for (const lambda of lambdasListNew) {
            addLambda(lambda);
        }
        Logger.log("Found the following Lambdas to debug:");
        Logger.log(` - ${getLambdas()
            .map((f) => `${f.functionName} code: ${f.codePath}`)
            .join("\n - ")}`);
    }
}
/**
 * Set the configuration
 * @param newConfig
 */
function setConfig(newConfig) {
    config = newConfig;
}
export const Configuration = {
    readConfig,
    get config() {
        if (!config) {
            throw new Error("Config not initialized. Call readConfig() first.");
        }
        return config;
    },
    discoverLambdas,
    getLambda,
    getLambdas,
    setConfig,
};
