"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TemplateSync = void 0;
/* istanbul ignore file */
const path_1 = __importDefault(require("path"));
const command_1 = require("./../command.cjs");
const utils_1 = require("./../../utils/index.cjs");
const input_1 = require("./../../input/index.cjs");
const fs_1 = __importDefault(require("fs"));
const logging_1 = require("@decaf-ts/logging");
const baseUrl = "https://raw.githubusercontent.com/decaf-ts/ts-workspace/master";
const options = {
    templates: [
        ".github/ISSUE_TEMPLATE/bug_report.md",
        ".github/ISSUE_TEMPLATE/feature_request.md",
        ".github/FUNDING.yml",
    ],
    workflows: [
        ".github/workflows/codeql-analysis.yml",
        ".github/workflows/jest-coverage.yaml",
        ".github/workflows/nodejs-build-prod.yaml",
        ".github/workflows/pages.yaml",
        ".github/workflows/publish-on-release.yaml",
        ".github/workflows/release-on-tag.yaml",
        ".github/workflows/snyk-analysis.yaml",
    ],
    ide: [
        ".idea/runConfigurations/All Tests.run.xml",
        ".idea/runConfigurations/build.run.xml",
        ".idea/runConfigurations/build_prod.run.xml",
        ".idea/runConfigurations/coverage.run.xml",
        ".idea/runConfigurations/docs.run.xml",
        ".idea/runConfigurations/drawings.run.xml",
        ".idea/runConfigurations/flash-forward.run.xml",
        ".idea/runConfigurations/Integration_Tests.run.xml",
        ".idea/runConfigurations/Bundling_Tests.run.xml",
        ".idea/runConfigurations/lint-fix.run.xml",
        ".idea/runConfigurations/release.run.xml",
        ".idea/runConfigurations/test_circular.run.xml",
        ".idea/runConfigurations/uml.run.xml",
        ".idea/runConfigurations/Unit Tests.run.xml",
        ".idea/runConfigurations/update-scripts.run.xml",
    ],
    docs: [
        "workdocs/tutorials/Contributing.md",
        "workdocs/tutorials/Documentation.md",
        "workdocs/tutorials/For Developers.md",
        "workdocs/2-Badges.md",
        "workdocs/jsdocs.json",
        "workdocs/readme-md.json",
    ],
    styles: [".prettierrc", "eslint.config.js"],
    scripts: [
        "bin/update-scripts.cjs",
        "bin/tag-release.cjs",
        "bin/build-scripts.cjs",
    ],
    tests: ["jest.config.ts", "workdocs/reports/jest.coverage.config.ts"],
    typescript: ["tsconfig.json"],
    docker: ["Dockerfile"],
    automation: [
        "workdocs/confluence/Continuous Integration-Deployment/GitHub.md",
        "workdocs/confluence/Continuous Integration-Deployment/Jira.md",
        "workdocs/confluence/Continuous Integration-Deployment/Teams.md",
    ],
};
const argzz = {
    // init attributes
    boot: {
        type: "boolean",
    },
    org: {
        type: "string",
        short: "o",
    },
    name: {
        type: "string",
        short: "n",
        default: undefined,
    },
    author: {
        type: "string",
        short: "a",
        default: undefined,
    },
    // update attributes
    all: {
        type: "boolean",
    },
    license: {
        type: "string",
        message: "Pick the license",
    },
    scripts: {
        type: "boolean",
    },
    styles: {
        type: "boolean",
    },
    docs: {
        type: "boolean",
    },
    ide: {
        type: "boolean",
    },
    workflows: {
        type: "boolean",
    },
    templates: {
        type: "boolean",
    },
    typescript: {
        type: "boolean",
    },
    docker: {
        type: "boolean",
    },
    pkg: {
        type: "boolean",
    },
    dependencies: {
        type: "boolean",
    },
    tests: {
        type: "boolean",
    },
    automation: {
        type: "boolean",
    },
};
/**
 * @class TemplateSync
 * @extends {Command}
 * @category scripts
 * @description A command-line tool for synchronizing project templates and configurations.
 * @summary This class provides functionality to download and update various project files and configurations from a remote repository.
 * It supports updating licenses, IDE configurations, scripts, styles, documentation, workflows, and templates.
 *
 * @param {CommandOptions} args - The command options for TemplateSync
 */
class TemplateSync extends command_1.Command {
    constructor() {
        super("TemplateSync", argzz);
        this.replacements = {};
        /**
         * @description Downloads style configuration files.
         * @returns {Promise<void>}
         */
        this.getStyles = () => this.downloadOption("styles");
        /**
         * @description Downloads template files.
         * @returns {Promise<void>}
         */
        this.getTemplates = () => this.downloadOption("templates");
        /**
         * @description Downloads workflow configuration files.
         * @returns {Promise<void>}
         */
        this.getWorkflows = () => this.downloadOption("workflows");
        /**
         * @description Downloads documentation files.
         * @returns {Promise<void>}
         */
        this.getDocs = () => this.downloadOption("docs");
        /**
         * @description Downloads typescript config files.
         * @returns {Promise<void>}
         */
        this.getTypescript = () => this.downloadOption("typescript");
        /**
         * @description Downloads automation documentation files.
         * @returns {Promise<void>}
         */
        this.getAutomation = () => this.downloadOption("automation");
        /**
         * @description Downloads automation documentation files.
         * @returns {Promise<void>}
         */
        this.getTests = () => this.downloadOption("tests");
        /**
         * @description Downloads docker image files.
         * @returns {Promise<void>}
         */
        this.getDocker = () => this.downloadOption("docker");
    }
    loadValuesFromPackage() {
        const p = process.cwd();
        const author = (0, utils_1.getPackage)(p, "author");
        const scopedName = (0, utils_1.getPackage)(p, "name");
        let name = scopedName;
        let org;
        if (name.startsWith("@")) {
            const split = name.split("/");
            name = split[1];
            org = split[0].replace("@", "");
        }
        ["Tiago Venceslau", "TiagoVenceslau", "${author}"].forEach((el) => (this.replacements[el] = author));
        ["TS-Workspace", "ts-workspace", "${name}"].forEach((el) => (this.replacements[el] = name));
        ["decaf-ts", "${org}"].forEach((el) => (this.replacements[el] = org || '""'));
        this.replacements["${org_or_owner}"] = org || name;
    }
    /**
     * @description Downloads files for a specific option category.
     * @summary This method downloads all files associated with a given option key from the remote repository.
     * @param {string} key - The key representing the option category to download
     * @returns {Promise<void>}
     * @throws {Error} If the specified option key is not found
     */
    async downloadOption(key) {
        if (!(key in options)) {
            throw new Error(`Option "${key}" not found in options`);
        }
        const files = options[key];
        for (const file of files) {
            this.log.info(`Downloading ${file}`);
            let data = await utils_1.HttpClient.downloadFile(`${baseUrl}/${file}`);
            data = (0, logging_1.patchString)(data, this.replacements);
            (0, utils_1.writeFile)(path_1.default.join(process.cwd(), file), data);
        }
    }
    /**
     * @description Downloads and sets up the specified license.
     * @summary This method downloads the chosen license file, saves it to the project, and updates the package.json license field.
     * @param {"MIT" | "GPL" | "Apache" | "LGPL" | "AGPL"} license - The license to download and set up
     * @returns {Promise<void>}
     */
    async getLicense(license) {
        this.log.info(`Downloading ${license} license`);
        const url = `${baseUrl}/workdocs/licenses/${license}.md`;
        let data = await utils_1.HttpClient.downloadFile(url);
        data = (0, logging_1.patchString)(data, this.replacements);
        (0, utils_1.writeFile)(path_1.default.join(process.cwd(), "LICENSE.md"), data);
        (0, utils_1.setPackageAttribute)("license", license);
    }
    /**
     * @description Downloads IDE configuration files.
     * @returns {Promise<void>}
     */
    async getIde() {
        fs_1.default.mkdirSync(path_1.default.join(process.cwd(), ".idea", "runConfigurations"), {
            recursive: true,
        });
        await this.downloadOption("ide");
    }
    /**
     * @description Update npm scripts
     * @returns {Promise<void>}
     */
    async getScripts() {
        await this.downloadOption("scripts");
        this.log.info("please re-run the command");
        process.exit(0);
    }
    async initPackage(pkgName, author, license) {
        try {
            const pkg = (0, utils_1.getPackage)();
            delete pkg[utils_1.SetupScriptKey];
            pkg.name = pkgName;
            pkg.version = "0.0.1";
            pkg.author = author;
            pkg.license = license;
            fs_1.default.writeFileSync("package.json", JSON.stringify(pkg, null, 2));
        }
        catch (e) {
            throw new Error(`Error fixing package.json: ${e}`);
        }
    }
    async updatePackageScrips() {
        try {
            const originalPkg = JSON.parse(await utils_1.HttpClient.downloadFile(`${baseUrl}/package.json`));
            const { scripts } = originalPkg;
            const pkg = (0, utils_1.getPackage)();
            Object.keys(pkg.scripts).forEach((key) => {
                if (key in scripts) {
                    const replaced = (0, logging_1.patchString)(scripts[key], this.replacements);
                    if (replaced !== scripts[key]) {
                        pkg.scripts[key] = replaced;
                    }
                }
            });
            pkg["exports"]["require"] = originalPkg["exports"]["require"];
            pkg["exports"]["import"] = originalPkg["exports"]["import"];
            pkg["types"] = originalPkg["types"];
            fs_1.default.writeFileSync("package.json", JSON.stringify(pkg, null, 2));
        }
        catch (e) {
            throw new Error(`Error fixing package.json scripts: ${e}`);
        }
    }
    async createTokenFiles() {
        const log = this.log.for(this.createTokenFiles);
        const gitToken = await input_1.UserInput.insistForText("token", "please input your github token", (res) => {
            return !!res.match(/^ghp_[0-9a-zA-Z]{36}$/g);
        });
        Object.values(utils_1.Tokens).forEach((token) => {
            try {
                let status;
                try {
                    status = fs_1.default.existsSync(token);
                    // eslint-disable-next-line @typescript-eslint/no-unused-vars
                }
                catch (e) {
                    log.info(`Token file ${token} not found. Creating a new one...`);
                    fs_1.default.writeFileSync(token, token === ".token" ? gitToken : "");
                    return;
                }
                if (!status) {
                    fs_1.default.writeFileSync(token, token === ".token" ? gitToken : "");
                }
            }
            catch (e) {
                throw new Error(`Error creating token file ${token}: ${e}`);
            }
        });
    }
    async getOrg() {
        const org = await input_1.UserInput.askText("Organization", "Enter the organization name (will be used to scope your npm project. leave blank to create a unscoped project):");
        const confirmation = await input_1.UserInput.askConfirmation("Confirm organization", "Is this organization correct?", true);
        if (!confirmation)
            return this.getOrg();
        return org;
    }
    async auditFix() {
        return await (0, utils_1.runCommand)("npm audit fix --force").promise;
    }
    patchFiles() {
        const files = [
            ...fs_1.default
                .readdirSync(path_1.default.join(process.cwd(), "src"), {
                recursive: true,
                withFileTypes: true,
            })
                .filter((entry) => entry.isFile())
                .map((entry) => path_1.default.join(entry.parentPath, entry.name)),
            ...fs_1.default
                .readdirSync(path_1.default.join(process.cwd(), "workdocs"), {
                recursive: true,
                withFileTypes: true,
            })
                .filter((entry) => entry.isFile() && entry.name.endsWith(".md"))
                .map((entry) => path_1.default.join(entry.parentPath, entry.name)),
            path_1.default.join(process.cwd(), ".gitlab-ci.yml"),
            path_1.default.join(process.cwd(), "workdocs", "jsdocs.json"),
        ];
        for (const file of files) {
            (0, utils_1.patchFile)(file, this.replacements);
        }
    }
    async updateDependencies() {
        try {
            const originalPkg = JSON.parse(await utils_1.HttpClient.downloadFile(`${baseUrl}/package.json`));
            const { devDependencies } = originalPkg;
            const pkg = (0, utils_1.getPackage)();
            Object.keys(pkg.scripts).forEach((key) => {
                if (key in devDependencies) {
                    const replaced = devDependencies[key];
                    if (replaced !== devDependencies[key]) {
                        pkg["devDependencies"] =
                            pkg["devDependencies"] || {};
                        pkg["devDependencies"][key] = replaced;
                    }
                }
            });
            fs_1.default.writeFileSync("package.json", JSON.stringify(pkg, null, 2));
            await (0, utils_1.runCommand)("npm install").promise;
        }
        catch (e) {
            throw new Error(`Error fixing package.json dependencies: ${e}`);
        }
    }
    /**
     * @description Runs the template synchronization process.
     * @summary This method orchestrates the downloading of various project components based on the provided arguments.
     * @param {ParseArgsResult} args - The parsed command-line arguments
     * @returns {Promise<void>}
     *
     * @mermaid
     * sequenceDiagram
     *   participant T as TemplateSync
     *   participant L as getLicense
     *   participant I as getIde
     *   participant S as getScripts
     *   participant St as getStyles
     *   participant D as getDocs
     *   participant W as getWorkflows
     *   participant Te as getTemplates
     *   T->>T: Parse arguments
     *   alt all flag is true
     *     T->>T: Set all component flags to true
     *   end
     *   alt license is specified
     *     T->>L: getLicense(license)
     *   end
     *   alt ide flag is true
     *     T->>I: getIde()
     *   end
     *   alt scripts flag is true
     *     T->>S: getScripts()
     *   end
     *   alt styles flag is true
     *     T->>St: getStyles()
     *   end
     *   alt docs flag is true
     *     T->>D: getDocs()
     *   end
     *   alt workflows flag is true
     *     T->>W: getWorkflows()
     *   end
     *   alt templates flag is true
     *     T->>Te: getTemplates()
     *   end
     */
    async run(args) {
        let { license } = args;
        const { boot } = args;
        let { all, scripts, styles, docs, ide, workflows, templates, docker, typescript, dependencies, tests, automation, pkg, } = args;
        if (scripts ||
            styles ||
            docs ||
            ide ||
            workflows ||
            templates ||
            docker ||
            typescript ||
            automation ||
            dependencies ||
            tests ||
            pkg)
            all = false;
        if (boot) {
            const org = await this.getOrg();
            const name = await input_1.UserInput.insistForText("Project name", "Enter the project name:", (res) => res.length > 1);
            const author = await input_1.UserInput.insistForText("Author", "Enter the author name:", (res) => res.length > 1);
            const pkgName = org ? `@${org}/${name}` : name;
            await this.initPackage(pkgName, author, license);
            await this.createTokenFiles();
            await this.auditFix();
            this.patchFiles();
        }
        if (all) {
            scripts = false;
            styles = true;
            docs = true;
            ide = true;
            workflows = true;
            templates = true;
            docker = true;
            typescript = true;
            pkg = true;
            dependencies = true;
            tests = true;
            automation = false;
        }
        if (typeof scripts === "undefined")
            scripts = await input_1.UserInput.askConfirmation("scripts", "Do you want to get scripts?", true);
        if (scripts)
            await this.getScripts();
        this.loadValuesFromPackage();
        if (!all && typeof license === "undefined") {
            const confirmation = await input_1.UserInput.askConfirmation("license", "Do you want to set a license?", true);
            if (confirmation)
                license = await input_1.UserInput.insistForText("license", "Enter the desired License (MIT|GPL|Apache|LGPL|AGPL):", (val) => !!val && !!val.match(/^(MIT|GPL|Apache|LGPL|AGPL)$/g));
        }
        if (typeof license !== "undefined")
            await this.getLicense(license);
        if (typeof ide === "undefined")
            ide = await input_1.UserInput.askConfirmation("ide", "Do you want to get ide configs?", true);
        if (ide)
            await this.getIde();
        if (typeof typescript === "undefined")
            typescript = await input_1.UserInput.askConfirmation("typescript", "Do you want to get typescript configs?", true);
        if (typescript)
            await this.getTypescript();
        if (typeof docker === "undefined")
            docker = await input_1.UserInput.askConfirmation("docker", "Do you want to get docker configs?", true);
        if (docker)
            await this.getDocker();
        if (typeof automation === "undefined")
            automation = await input_1.UserInput.askConfirmation("automation", "Do you want to get automation configs?", true);
        if (automation)
            await this.getAutomation();
        if (typeof styles === "undefined")
            styles = await input_1.UserInput.askConfirmation("styles", "Do you want to get styles?", true);
        if (styles)
            await this.getStyles();
        if (typeof docs === "undefined")
            docs = await input_1.UserInput.askConfirmation("docs", "Do you want to get docs?", true);
        if (docs)
            await this.getDocs();
        if (typeof workflows === "undefined")
            workflows = await input_1.UserInput.askConfirmation("workflows", "Do you want to get workflows?", true);
        if (workflows)
            await this.getWorkflows();
        if (typeof templates === "undefined")
            templates = await input_1.UserInput.askConfirmation("templates", "Do you want to get templates?", true);
        if (templates)
            await this.getTemplates();
        if (typeof pkg === "undefined")
            pkg = await input_1.UserInput.askConfirmation("pkg", "Do you want to update your package.json scripts?", true);
        if (pkg)
            await this.updatePackageScrips();
        if (typeof tests === "undefined")
            tests = await input_1.UserInput.askConfirmation("pkg", "Do you want to update your test configs?", true);
        if (tests)
            await this.getTests();
        if (typeof dependencies === "undefined")
            dependencies = await input_1.UserInput.askConfirmation("pkg", "Do you want to update dev dependencies?", true);
        if (dependencies)
            await this.updateDependencies();
    }
}
exports.TemplateSync = TemplateSync;
//# sourceMappingURL=update-scripts.js.map