//#region import
import { config, dotTaonFolder, taonPackageName, Helpers__NS__commandOutputAsString, Helpers__NS__createSymLink, Helpers__NS__error, Helpers__NS__execute, Helpers__NS__exists, Helpers__NS__info, Helpers__NS__isFolder, Helpers__NS__log, Helpers__NS__logInfo, Helpers__NS__logWarn, Helpers__NS__openFolderInFileExplorer, Helpers__NS__questionYesNo, Helpers__NS__readFile, Helpers__NS__readJson5, Helpers__NS__remove, Helpers__NS__removeFileIfExists, Helpers__NS__run, Helpers__NS__taskDone, Helpers__NS__taskStarted, Helpers__NS__warn, Helpers__NS__writeFile, Helpers__NS__writeJson, Helpers__NS__writeJsonC } from 'tnp-core/lib-prod';
import { UtilsTerminal__NS__wait } from 'tnp-core/lib-prod';
import { CoreModels__NS__parentLocation } from 'tnp-core/lib-prod';
import { CLI } from 'tnp-core/lib-prod';
import { path, crossPlatformPath } from 'tnp-core/lib-prod';
import { fse, chalk } from 'tnp-core/lib-prod';
import { ___NS__cloneDeep, ___NS__first, ___NS__isNumber, ___NS__isString, ___NS__isUndefined, ___NS__kebabCase } from 'tnp-core/lib-prod';
import { Utils__NS__getFreePort, Utils__NS__uniqArray } from 'tnp-core/lib-prod';
import { CoreProject, LinkedProject, HelpersTaon__NS__checkIfNameAllowedForTaonProj, HelpersTaon__NS__copy, HelpersTaon__NS__copyFile, HelpersTaon__NS__filterDontCopy, HelpersTaon__NS__filterOnlyCopy, HelpersTaon__NS__getValueFromJSON, HelpersTaon__NS__getValueFromJSONC, HelpersTaon__NS__git__NS__originSshToHttp, HelpersTaon__NS__setValueToJSON, HelpersTaon__NS__setValueToJSONC, UtilsTaonWorker__NS__getUniqueForTask, UtilsTypescript__NS__formatAllFilesInsideFolder, UtilsTypescript__NS__formatFile, UtilsVSCode__NS__openFolder } from '../../index';
import { BaseProjectResolver } from './base-project-resolver';
//#endregion
export class BaseProject {
    //#region static
    //#region static / instance of resovle
    static ins = new BaseProjectResolver(BaseProject, () => 'taon');
    //#endregion
    //#endregion
    //#region fields
    cache = {};
    static cache = {};
    get globalCache() {
        return BaseProject.cache;
    }
    type = 'unknow';
    /**
     * Unique free port for project instance
     * only available after executing *this.assignFreePort()*
     */
    port;
    libraryBuild;
    npmHelpers;
    fileFoldersOperations;
    get packageJson() {
        return this.npmHelpers.packageJson;
    }
    get nodeModules() {
        return this.npmHelpers.nodeModules;
    }
    linkedProjects;
    vsCodeHelpers;
    releaseProcess;
    git;
    linter;
    ignoreHide;
    quickFixes;
    staticPages;
    javaJdk;
    docker;
    //#endregion
    __location;
    get location() {
        return this.__location;
    }
    set location(v) {
        this.__location = crossPlatformPath(v);
    }
    //#region constructor
    //#region @backend
    // @ts-ignore
    constructor(
    /**
     * doesn't need to be real path -> can be link
     */
    location) {
        this.location = location;
        //#region @backend
        // @ts-ignore
        this.libraryBuild = new (require('./base-library-build')
            .BaseLibraryBuild)(this);
        this.npmHelpers = new (require('./base-npm-helpers')
            .BaseNpmHelpers)(this);
        this.linkedProjects = new (require('./base-linked-projects')
            .BaseLinkedProjects)(this);
        this.fileFoldersOperations = new (require('./base-file-folders-operations')
            .BaseFileFoldersOperations)(this);
        this.git = new (require('./base-git').BaseGit)(this);
        this.linter = new (require('./base-linter')
            .BaseLinter)(this);
        this.ignoreHide = new (require('./base-ignore-hide')
            .BaseIgnoreHideHelpers)(this);
        this.javaJdk = new (require('./base-java-jdk')
            .BaseJavaJdk)(this);
        this.docker = new (require('./base-docker')
            .BaseDocker)(this);
        this.vsCodeHelpers = new (require('./base-vscode')
            .BaseVscodeHelpers)(this);
        this.staticPages = new (require('./base-static-pages')
            .BaseStaticPages)(this);
        this.releaseProcess = new (require('./base-release-process')
            .BaseReleaseProcess)(this);
        //#endregion
    }
    //#endregion
    //#endregion
    //#region methods & getters / is monorepo
    /**
     * TODO move this to framework
     * Linked projects are actual inside repo
     * not in separated folders/git sub repos
     */
    get isMonorepo() {
        return false;
    }
    //#endregion
    //#region getters & methods / order core projects
    orderCoreProjects(coreProjects) {
        const projectMap = new Map();
        // Initialize the project map
        for (const project of coreProjects) {
            projectMap.set(project, []);
        }
        // Populate the project map with dependencies
        for (const project of coreProjects) {
            if (!projectMap.has(project.extends)) {
                projectMap.set(project.extends, []);
            }
            projectMap.get(project.extends).push(project);
        }
        const orderedProjects = [];
        const visited = new Set();
        const visit = (project) => {
            if (!visited.has(project)) {
                visited.add(project);
                const dependencies = projectMap.get(project);
                if (dependencies) {
                    for (const dep of dependencies) {
                        visit(dep);
                    }
                }
                orderedProjects.push(project);
            }
        };
        // Visit each project
        for (const project of coreProjects) {
            visit(project);
        }
        return orderedProjects.reverse();
    }
    //#endregion
    //#region methods & getters / core
    get core() {
        if (!___NS__isUndefined(this.cache['core'])) {
            return this.cache['core'];
        }
        // Helpers__NS__taskStarted(`Detecting core project for ${this.genericName}`);
        let coreProjects = CoreProject.coreProjects.filter(p => p.recognizedFn(this));
        coreProjects = this.orderCoreProjects(coreProjects);
        // Helpers__NS__taskDone(`Core project detected for ${this.genericName}`);
        // console.log('CoreProject.coreProjects', CoreProject.coreProjects.map(c => c.name));
        this.cache['core'] = ___NS__first(coreProjects);
        return this.cache['core'];
    }
    //#endregion
    //#region getters & methods / link project exited
    get linkedProjectsExisted() {
        //#region @backendFunc
        return this.linkedProjects.linkedProjects
            .map(f => {
            const proj = this.ins.From(this.pathFor(f.relativeClonePath));
            return proj;
        })
            .filter(f => !!f);
        //#endregion
    }
    //#endregion
    //#region methods & getters / set type
    setType(type) {
        // @ts-ignore
        this.type = type;
    }
    //#endregion
    //#region methods & getters / type is
    typeIs(...types) {
        return this.type && types.includes(this.type);
    }
    //#endregion
    //#region methods & getters / type is not
    typeIsNot(...types) {
        return !this.typeIs(...types);
    }
    //#endregion
    //#region methods & getters / basename
    /**
     * project folder basename
     */
    get basename() {
        //#region @websqlFunc
        return path.basename(this.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / parent basename
    /**
     * project folder parent basename
     */
    get parentBasename() {
        //#region @websqlFunc
        return path.basename(path.dirname(this.location));
        //#endregion
    }
    //#endregion
    //#region methods & getters / name
    /**
     * name from package.json
     */
    get name() {
        return this.packageJson?.name || this.nameFromPomXML;
    }
    get nameForCli() {
        return this.name?.endsWith('-cli') ? this.name : `${this.name}-cli`;
    }
    get nameForNpmPackage() {
        return this.name;
    }
    get nameFromPomXML() {
        const artifactIdPattern = /<artifactId>([^<]+)<\/artifactId>/;
        const match = (this.readFile('pom.xml') || '').match(artifactIdPattern);
        if (match && match[1]) {
            return match[1];
        }
        return '';
    }
    //#endregion
    //#region methods & getters / get folder for possible project chhildrens
    getFoldersForPossibleProjectChildren() {
        //#region @backendFunc
        const isDirectory = source => fse.lstatSync(source).isDirectory();
        const getDirectories = source => fse
            .readdirSync(source)
            .map(name => path.join(source, name))
            .filter(isDirectory);
        let subdirectories = getDirectories(this.location).filter(f => {
            const folderName = path.basename(f);
            return HelpersTaon__NS__checkIfNameAllowedForTaonProj(folderName);
        });
        // if (this.isTnp' && fse.existsSync(path.join(this.location, '../taon-projects'))) {
        //   subdirectories = subdirectories.concat(getDirectories(path.join(this.location, '../taon-projects'))
        //     .filter(f => {
        //       const folderName = path.basename(f);
        //       return Helpers.checkIfNameAllowedForTaonProj(folderName);
        //     }))
        // }'
        return subdirectories;
        //#endregion
    }
    //#endregion
    //#region methods & getters / get all childrens
    getAllChildren() {
        //#region @backendFunc
        const subdirectories = this.getFoldersForPossibleProjectChildren();
        let res = subdirectories
            .map(dir => {
            // console.log('child:', dir)
            return this.ins.From(dir);
        })
            .filter(c => !!c);
        return res;
        //#endregion
    }
    //#endregion
    //#region methods & getters / children
    /**
     * alias to getAllChildren
     */
    get children() {
        //#region @websqlFunc
        return this.getAllChildren();
        //#endregion
    }
    //#endregion
    //#region methods & getters / get child
    getChildBy(nameOrBasename, errors = true) {
        //#region @websqlFunc
        const c = this.children.find(c => c.name === nameOrBasename || c.basename === nameOrBasename);
        if (errors && !c) {
            // TODO @LAST FIX THIS FOR DEEEP ARGGUMENTS
            Helpers__NS__warn(`Project doesnt contain child with name or basename: ${nameOrBasename}`);
        }
        return c;
        //#endregion
    }
    //#endregion
    //#region methods & getters / parent
    get parent() {
        //#region @websqlFunc
        let location = this.location;
        const absExternalPathToChildren = this.pathFor([
            dotTaonFolder,
            CoreModels__NS__parentLocation,
        ]);
        if (Helpers__NS__exists(absExternalPathToChildren)) {
            const externalLocation = Helpers__NS__readFile(absExternalPathToChildren);
            if (externalLocation && Helpers__NS__exists(externalLocation)) {
                return this.ins.From(externalLocation);
            }
        }
        if (!___NS__isString(location) || location.trim() === '') {
            return void 0;
        }
        return this.ins.From(path.join(location, '..'));
        //#endregion
    }
    //#endregion
    //#region methods & getters / parent
    get nearestParent() {
        //#region @websqlFunc
        if (this.cache['nearestParent']) {
            return this.cache['nearestParent'];
        }
        const nearestParent = this.ins.nearestTo([this.location, '..']);
        if (nearestParent) {
            // console.log(`
            //   nearest parent for ${this.genericName} is ${nearestParent.genericName}
            //   `);
            this.cache['nearestParent'] = nearestParent;
        }
        return nearestParent;
        //#endregion
    }
    //#endregion
    //#region methods & getters / grandpa
    get grandpa() {
        //#region @websqlFunc
        if (!___NS__isString(this.location) || this.location.trim() === '') {
            return void 0;
        }
        const grandpa = this.ins.From(path.join(this.location, '..', '..'));
        return grandpa;
        //#endregion
    }
    //#endregion
    //#region methods & getters / generic name
    get allNpmPackagesNames() {
        return [this.name];
    }
    get titleBarName() {
        const allPackagesNames = this.allNpmPackagesNames.filter(f => f !== this.basename).length === 0
            ? ''
            : `(${this.allNpmPackagesNames.filter(f => f !== this.basename).join(',')})`;
        // if (this.parent) {
        //   return (
        //     `${path.basename(path.dirname(this.parent.location))}/${this.parent.name}/` +
        //     `${this.basename} ${allPackagesNames} [ \${activeEditorShort} ]`
        //   );
        // }
        return ((this.parent ? `${path.basename(path.dirname(this.location))}/` : '') +
            `${this.basename} (${allPackagesNames}) [ \${activeEditorShort}]`);
    }
    get genericName() {
        //#region @websqlFunc
        if (!___NS__isUndefined(this.cache['genericName'])) {
            return this.cache['genericName'];
        }
        let nameFromPackageJson = this.name;
        //#region @backend
        nameFromPackageJson = `${chalk.bold.gray(this.name)}`;
        //#endregion
        const sliceMinus = 8;
        const shortPath = '/' +
            crossPlatformPath(this.location)
                .split('/')
                .slice(-1 * sliceMinus)
                .join('/');
        const result = (shortPath.includes(crossPlatformPath(this.location)) ? '/' : '(..)') +
            this.checkAndBoldenPath(this.location)
                .split('/')
                .slice(-1 * sliceMinus)
                .join('/') +
            `(${nameFromPackageJson})`;
        this.cache['genericName'] = result.replace(/\/\//g, '/');
        return this.cache['genericName'];
        //#endregion
    }
    //#endregion
    //#region methods & getters / check and bolden path
    checkAndBoldenPath(fullPath) {
        const parts = fullPath.split('/');
        const result = parts.map((part, index) => {
            const pathTocheck = parts.slice(0, index + 1).join('/');
            // console.log('pathTocheck', pathTocheck);
            //#region @backend
            if (this.ins.From(pathTocheck)) {
                return chalk.underline.italic.bold(part);
            }
            //#endregion
            return part;
        });
        return result.join('/');
    }
    //#endregion
    //#region methods & getters / path exits
    /**
     * same has project.hasFile();
     */
    pathExists(relativePath) {
        return this.hasFile(relativePath);
    }
    //#endregion
    //#region methods & getters / has file
    /**
     * same as project.pathExists();
     */
    hasFile(relativePath) {
        //#region @backendFunc
        // TODO prevent absolute path
        return Helpers__NS__exists(this.pathFor(relativePath));
        //#endregion
    }
    hasFolder(relativePath) {
        //#region @backendFunc
        return (Helpers__NS__exists(this.pathFor(relativePath)) &&
            fse.lstatSync(this.pathFor(relativePath)).isDirectory());
        //#endregion
    }
    //#endregion
    //#region methods & getters / relative
    /**
     * Function will return relative path from project location
     */
    relative(absoultePath) {
        return crossPlatformPath(path.relative(crossPlatformPath(this.location), crossPlatformPath(absoultePath)));
    }
    //#endregion
    //#region methods & getters / create folder
    /**
     *
     * @param relativePath
     * @param override it will remove ex
     */
    createFolder(relativePath, options) {
        //#region @backendFunc
        const { override } = options || {};
        try {
            fse.unlinkSync(this.pathFor(relativePath));
        }
        catch (error) { }
        if (this.hasFolder(relativePath)) {
            if (override) {
                this.remove(relativePath, true);
            }
            return;
        }
        fse.mkdirSync(this.pathFor(relativePath), { recursive: true });
        //#endregion
    }
    //#endregion
    //#region methods & getters / format file(s) with prettier
    formatFile(specificRelativePath) {
        //#region @backendFunc
        const absPathToFile = this.pathFor(specificRelativePath);
        UtilsTypescript__NS__formatFile(absPathToFile);
        //#endregion
    }
    formatAllFiles() {
        //#region @backendFunc
        Helpers__NS__info(`Formatting all files in ${this.genericName}`);
        UtilsTypescript__NS__formatAllFilesInsideFolder(this.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / contains file
    /**
     * same as project.pathhasFileExists();
     * but with path.resolve
     */
    containsFile(fileRelativeToProjectPath) {
        const fullPath = path.resolve(path.join(this.location, fileRelativeToProjectPath));
        return Helpers__NS__exists(fullPath);
    }
    //#endregion
    //#region methods & getters / path for
    /**
     * absolute path:
     * concated project location with relative path
     */
    pathFor(relativePath) {
        //#region @backendFunc
        if (Array.isArray(relativePath)) {
            relativePath = crossPlatformPath(relativePath);
        }
        if (path.isAbsolute(relativePath)) {
            Helpers__NS__error(`Cannot join relative path with absolute: ${relativePath}`);
        }
        return crossPlatformPath([this.location, relativePath]);
        //#endregion
    }
    //#endregion
    //#region methods & getters / write json
    writeJson(relativePath, json) {
        //#region @backendFunc
        if (path.isAbsolute(relativePath)) {
            Helpers__NS__error(`Cannot join relative path with absolute: ${relativePath}`);
        }
        Helpers__NS__writeJson(crossPlatformPath([this.location, relativePath]), json);
        //#endregion
    }
    //#endregion
    //#region methods & getters / write json
    writeJsonC(relativePath, json) {
        //#region @backendFunc
        if (path.isAbsolute(relativePath)) {
            Helpers__NS__error(`Cannot join relative path with absolute: ${relativePath}`);
        }
        Helpers__NS__writeJsonC(crossPlatformPath([this.location, relativePath]), json);
        //#endregion
    }
    //#endregion
    //#region methods & getters / run
    /**
     * @deprecated us execute instead
     * use output from or more preciese crafted api
     */
    run(command, options) {
        let opt;
        //#region @backend
        options = ___NS__cloneDeep(options) || {};
        Helpers__NS__log(`command: ${command}`);
        if (___NS__isUndefined(options.showCommand)) {
            options.showCommand = false;
        }
        opt = options;
        if (!opt.cwd) {
            opt.cwd = this.location;
        }
        if (opt.showCommand) {
            Helpers__NS__info(`[${CLI.chalk.underline('Executing shell command')}]  "${command}" in [${opt.cwd}]`);
        }
        else {
            Helpers__NS__log(`[${CLI.chalk.underline('Executing shell command')}]  "${command}" in [${opt.cwd}]`);
        }
        //#endregion
        return Helpers__NS__run(command, opt);
    }
    //#endregion
    /**
     * Open project location (or relative path to folder inside project)
     * in default editor
     */
    async openInEditor(relativePath, specyficEditor) {
        //#region @backendFunc
        await UtilsVSCode__NS__openFolder(relativePath ? this.pathFor(relativePath) : this.location, specyficEditor);
        //#endregion
    }
    //#region methods & getters / execute
    /**
     * same as run but async
     */
    async execute(command, options) {
        //#region @backendFunc
        options = options || {};
        if (___NS__isUndefined(options.showCommand)) {
            options.showCommand = true;
        }
        if (!options) {
            options = {};
        }
        const cwd = this.location;
        if (options.showCommand) {
            Helpers__NS__logInfo(`[${CLI.chalk.underline('Executing shell command')}]  "${command}" in [${cwd}]`);
        }
        else {
            Helpers__NS__log(`[${CLI.chalk.underline('Executing shell command')}]  "${command}" in [${cwd}]`);
        }
        return await Helpers__NS__execute(command, cwd, options);
        //#endregion
    }
    //#endregion
    //#region methods & getters / try run sync command
    /**
     * try run but continue when it fails
     * @param command
     * @param options
     * @returns
     */
    tryRunSync(command, 
    //#region @backend
    options) {
        //#region @backendFunc
        try {
            this.run(command, options).sync();
        }
        catch (error) {
            Helpers__NS__warn(`Not able to execute: ${command}`);
        }
        //#endregion
    }
    //#endregion
    //#region methods & getters / output from command
    outputFrom(command, 
    //#region @backend
    options) {
        //#region @backendFunc
        return Helpers__NS__commandOutputAsString(command, this.location, options);
        //#endregion
    }
    //#endregion
    //#region methods & getters / remove file
    removeFile(fileRelativeToProjectPath) {
        //#region @backendFunc
        fileRelativeToProjectPath = crossPlatformPath(fileRelativeToProjectPath);
        const fullPath = path.resolve(crossPlatformPath([this.location, fileRelativeToProjectPath]));
        return Helpers__NS__removeFileIfExists(fullPath);
        //#endregion
    }
    //#endregion
    //#region methods & getters / read file
    readFile(fileRelativeToProjectPath) {
        //#region @backendFunc
        fileRelativeToProjectPath = crossPlatformPath(fileRelativeToProjectPath);
        const fullPath = path.resolve(crossPlatformPath([this.location, fileRelativeToProjectPath]));
        // console.log('fullPath', fullPath);
        return Helpers__NS__readFile(fullPath);
        //#endregion
    }
    //#endregion
    //#region methods & getters / read file
    /**
     * read json file
     * handle all json files
     * jsonc, json5 , json
     * @param fileRelativeToProjectPath
     * @returns
     */
    readJson(fileRelativeToProjectPath) {
        //#region @backendFunc
        const fullPath = crossPlatformPath(path.resolve(this.location, crossPlatformPath(fileRelativeToProjectPath)));
        return Helpers__NS__readJson5(fullPath);
        //#endregion
    }
    //#endregion
    //#region methods & getters / copy files or folders
    /**
     * @deprecated TODO replace whit with create copyFileTo function
  
     * copy files or folders from
     * project to destination
     */
    copy(filesOrFolderRelativePathes) {
        return {
            to: (destination) => {
                //#region @backendFunc
                destination = crossPlatformPath(destination);
                for (let index = 0; index < filesOrFolderRelativePathes.length; index++) {
                    const source = this.pathFor(filesOrFolderRelativePathes[index]);
                    const dest = crossPlatformPath([
                        destination,
                        filesOrFolderRelativePathes[index],
                    ]);
                    if (Helpers__NS__isFolder(source)) {
                        HelpersTaon__NS__copy(source, dest, { recursive: true });
                    }
                    else {
                        HelpersTaon__NS__copyFile(source, dest);
                    }
                }
                //#endregion
            },
        };
    }
    //#endregion
    //#region methods & getters / remove (fiel or folder)
    remove(relativePath, exactPath = true) {
        //#region @backendFunc
        if (Array.isArray(relativePath)) {
            relativePath = crossPlatformPath(relativePath);
        }
        relativePath = relativePath.replace(/^\//, '');
        if (path.basename(relativePath) === config.folder.node_modules) {
            Helpers__NS__info('Removing node_modules folder...');
        }
        Helpers__NS__remove([this.location, relativePath], exactPath);
        //#endregion
    }
    //#endregion
    //#region methods & getters / remove folder by relative path
    removeFolderByRelativePath(relativePathToFolder) {
        //#region @backend
        relativePathToFolder = relativePathToFolder.replace(/^\//, '');
        const location = this.location;
        const p = path.join(location, relativePathToFolder);
        Helpers__NS__remove(p, true);
        //#endregion
    }
    //#endregion
    get isAngularLib() {
        //#region @backendFunc
        if (this.hasFile('ng-package.json')) {
            return true;
        }
        const angularJson = this.parent?.readJson('angular.json') || {};
        const projects = angularJson?.projects || {};
        if (projects && projects[this.basename]?.projectType === 'library') {
            return true;
        }
        return false;
        //#endregion
    }
    async refreshChildrenProjects(options) {
        //#region @backendFunc
        options = options || {};
        const linkedProjects = LinkedProject.detect(this.location, {
            checkAlsoNonRepos: true,
        }).filter(linkedProj => this.ins.From([this.location, linkedProj.relativeClonePath]));
        if (options.askUserAboutUpdate
            ? await Helpers__NS__questionYesNo(`

    Detected projects:
${linkedProjects.map(l => `- ${l.relativeClonePath}`).join('\n')}

Would you like to update current project configuration?`)
            : true) {
            const { prefix, resetOnlyChildren, skipRecrusivePush } = this.linkedProjects.getLinkedProjectsConfig();
            this.linkedProjects.setLinkedProjectsConfig({
                projects: [],
                prefix,
                resetOnlyChildren,
                skipRecrusivePush,
            });
            this.linkedProjects.addLinkedProjects(linkedProjects.map(c => {
                c.repoUrl = HelpersTaon__NS__git__NS__originSshToHttp(c.repoUrl);
                return c;
            }), { skipFormat: true });
            this.linkedProjects.formatConfigFile();
        }
        await this.init();
        Helpers__NS__info(`Linked projects updated`);
        //#endregion
    }
    //#region methods & getters / get unique for task
    getUniqueForTask(task) {
        return UtilsTaonWorker__NS__getUniqueForTask(task, this.location);
    }
    //#endregion
    //#region methods & getters / register and assign port
    async registerAndAssignPort(taskName, options = {}) {
        //#region @backendFunc
        taskName = this.getUniqueForTask(taskName);
        options = options || {};
        // console.log('taskNmae', taskName);
        const ctrl = await this.ins.portsWorker.getRemoteControllerFor({
            methodOptions: {
                calledFrom: `${taskName} -> registerAndAssignPort`,
                // skipWaitingForWorkerProcessPortToBeSaved: true,
            },
        });
        const showErrorAfterAttempts = 3;
        let attempts = 0;
        while (true) {
            attempts++;
            try {
                const data = await ctrl
                    .registerAndAssignPort(encodeURIComponent(taskName), ___NS__isNumber(options.startFrom) ? options.startFrom : void 0)
                    .request();
                return data.body.json.port;
            }
            catch (error) {
                if (config.frameworkName === taonPackageName) {
                    console.log(error);
                }
                Helpers__NS__logWarn(`[${config.frameworkName}-helpers] Error while registering port for task "${taskName}":`);
                const errorMsg = `[${config.frameworkName}-helpers] Retrying to assign port for task "${taskName}"...`;
                if (attempts >= showErrorAfterAttempts) {
                    Helpers__NS__warn(errorMsg);
                }
                else {
                    Helpers__NS__logWarn(errorMsg);
                }
                // Helpers__NS__logError(error, true, true);
                await UtilsTerminal__NS__wait(2);
            }
        }
        //#endregion
    }
    //#endregion
    //#region methods & getters / assign free port to project instance
    /**
     * @deprecated use this.registerAndAssignPort() or Utils__NS__getFreePort() instead
     */
    async assignFreePort(startFrom = 4200) {
        //#region @backendFunc
        return Utils__NS__getFreePort({ startFrom });
        //#endregion
    }
    //#endregion
    //#region methods & getters / define property
    /**
     * Purpose: not initializing all classes at the beginning
     * Only for BaseFeatureForProject class
     */
    defineProperty(variableName, classFn, options) {
        //#region @backendFunc
        const { customInstanceReturn } = options || {};
        const that = this;
        // @ts-ignore
        const prefixedName = `__${variableName}`;
        Object.defineProperty(this, variableName, {
            get: function () {
                if (!that[prefixedName]) {
                    if (typeof classFn === 'function') {
                        if (!!customInstanceReturn) {
                            that[prefixedName] = customInstanceReturn();
                        }
                        else {
                            that[prefixedName] = new classFn(that);
                        }
                    }
                    else {
                        Helpers__NS__warn(`[taon-helpers] Cannot create dynamic instance of class "${___NS__kebabCase(prefixedName.replace('__', ''))}".`);
                    }
                    // }
                }
                return that[prefixedName];
            },
            set: function (v) {
                that[prefixedName] = v;
            },
        });
        //#endregion
    }
    //#endregion
    //#region methods & getters / filter only copy
    /**
     * fs.copy option filter function for copying only selected folders from project
     */
    filterOnlyCopy(basePathFoldersOnlyToInclude) {
        //#region @backendFunc
        return HelpersTaon__NS__filterOnlyCopy(basePathFoldersOnlyToInclude, this.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / filter don't copy
    /**
     * fs.copy option filter function for copying only not selected folders from project
     */
    filterDontCopy(basePathFoldersTosSkip) {
        //#region @backendFunc
        return HelpersTaon__NS__filterDontCopy(basePathFoldersTosSkip, this.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / get main branches
    /**
     * main/default hardcoded branches
     */
    getMainBranches() {
        return ['master', 'develop', 'stage', 'prod', 'test'];
    }
    //#endregion
    //#region methods & getters / use feature in branch name for tests
    useFeatureInBranchNameForTests() {
        return false;
    }
    //#endregion
    //#region methods & getters / reset process
    async resetProcess(overrideBranch, options) {
        //#region @backend
        // debugger;
        options = options || {};
        options.commandActionType = options.commandActionType || 'only-this';
        // console.log(`CORE PROJECT BRANCH ${this.name}: ${this.core?.branch}, overrideBranch: ${overrideBranch}`)
        Helpers__NS__taskStarted(`

    Starting reset process for ${this.name || this.basename}

    `);
        this.git._beforeAnyActionOnGitRoot();
        let branchToReset = overrideBranch ||
            this.core?.branch ||
            this.git.getDefaultDevelopmentBranch();
        Helpers__NS__info(`fetch data in ${this.genericName}`);
        this.git.fetch();
        Helpers__NS__logInfo(`reseting hard  in ${this.genericName}`);
        this.git.resetHard();
        Helpers__NS__logInfo(`checking out branch "${branchToReset}" in ${this.genericName}`);
        this.git.checkout(branchToReset);
        Helpers__NS__logInfo(`pulling current branch in ${this.genericName}`);
        await this.git.pullCurrentBranch({ askToRetry: true });
        Helpers__NS__logInfo(`initing (struct) in ${this.genericName}`);
        try {
            await this.struct();
        }
        catch (error) { }
        Helpers__NS__taskDone(`RESET DONE BRANCH: ${chalk.bold(branchToReset)} in ${this.genericName}`);
        const childCommandActionType = options.commandActionType === 'first-level'
            ? 'only-this'
            : options.commandActionType;
        const children = Utils__NS__uniqArray([
            ...(this.linkedProjects?.linkedProjects || [])
                .map(lp => this.ins.From(this.pathFor([lp.relativeClonePath])))
                .filter(p => !!p),
            ...this.children,
        ], 'location');
        if (options.commandActionType !== 'only-this') {
            // console.log(`Reseting children ...`);
            for (const child of children) {
                await child.resetProcess(child.linkedProjects.resetLinkedProjectsOnlyToCoreBranches()
                    ? void 0
                    : branchToReset, {
                    commandActionType: childCommandActionType,
                });
            }
        }
        //#endregion
    }
    //#endregion
    //#region methods & getters / link project to
    linkTo(destPackageLocation) {
        //#region @backend
        Helpers__NS__createSymLink(this.location, destPackageLocation);
        //#endregion
    }
    //#endregion
    //#region methods & getters / set value to json
    setValueToJSON(relativePath, lodashGetPath, value) {
        //#region @backendFunc
        if (Array.isArray(relativePath)) {
            relativePath = crossPlatformPath(relativePath);
        }
        HelpersTaon__NS__setValueToJSON(this.pathFor(relativePath), lodashGetPath, value);
        //#endregion
    }
    //#endregion
    //#region methods & getters / set value to json
    setValueToJSONC(relativePath, lodashGetPath, value) {
        //#region @backendFunc
        if (Array.isArray(relativePath)) {
            relativePath = crossPlatformPath(relativePath);
        }
        HelpersTaon__NS__setValueToJSONC(this.pathFor(relativePath), lodashGetPath, value);
        //#endregion
    }
    //#endregion
    //#region methods & getters / set value to json
    setValueToJSON5(relativePath, lodashGetPath, value) {
        //#region @backendFunc
        if (Array.isArray(relativePath)) {
            relativePath = crossPlatformPath(relativePath);
        }
        HelpersTaon__NS__setValueToJSONC(this.pathFor(relativePath), lodashGetPath, value);
        //#endregion
    }
    //#endregion
    //#region methods & getters / get value from json
    getValueFromJSON(relativePath, lodashGetPath, defaultValue = void 0) {
        //#region @backendFunc
        if (Array.isArray(relativePath)) {
            relativePath = crossPlatformPath(relativePath);
        }
        return HelpersTaon__NS__getValueFromJSON(this.pathFor(relativePath), lodashGetPath, defaultValue);
        //#endregion
    }
    //#endregion
    //#region methods & getters / get value from jsonc
    getValueFromJSONC(relativePath, lodashGetPath, defaultValue = void 0) {
        //#region @backendFunc
        if (Array.isArray(relativePath)) {
            relativePath = crossPlatformPath(relativePath);
        }
        return HelpersTaon__NS__getValueFromJSONC(this.pathFor(relativePath), lodashGetPath, defaultValue);
        //#endregion
    }
    //#endregion
    //#region methods & getters / write file
    writeFile(relativePath, content, options) {
        //#region @backend
        options = options || {};
        if (Array.isArray(relativePath)) {
            relativePath = crossPlatformPath(relativePath);
        }
        Helpers__NS__writeFile([this.location, relativePath], content);
        if (options.formatWithPrettier) {
            this.formatFile(relativePath);
        }
        //#endregion
    }
    //#endregion
    //#region getters & methods / run command and get string
    runCommandGetString(command) {
        //#region @backendFunc
        return Helpers__NS__commandOutputAsString(command, this.location, {
            biggerBuffer: false,
        });
        //#endregion
    }
    //#endregion
    //#region getters & methods / to string
    toString = () => {
        return `${this.name}=>${this.location}`;
    };
    //#endregion
    //#region getters & methods / find partners names
    get parentsNames() {
        //#region @backendFunc
        return this.findParentsNames();
        //#endregion
    }
    //#region getters & methods / open location
    openLocation(relativeFolderPath) {
        //#region @backendFunc
        Helpers__NS__openFolderInFileExplorer(this.pathFor(relativeFolderPath));
        //#endregion
    }
    //#endregion
    findParentsNames(project, parent, result = []) {
        //#region @backendFunc
        if (!project && !parent) {
            project = this;
        }
        if (!project && parent) {
            return result.reverse();
        }
        if (project && project.parent) {
            result.push(project.parent.basename, project.parent.name);
        }
        return this.findParentsNames(project.parent, project, result);
        //#endregion
    }
    //#endregion
    //#region getters & methods / kill all instance
    tryKillAllElectronInstances() {
        //#region @backendFunc
        Helpers__NS__taskStarted('Killing all app instances');
        try {
            if (process.platform === 'win32') {
                Helpers__NS__run(`taskkill /f /im ${this.name}.exe`).sync();
            }
            else {
                Helpers__NS__run(`fkill -f ${this.name}`).sync();
            }
        }
        catch (error) { }
        Helpers__NS__taskDone('Done kill all app instances');
        //#endregion
    }
    //#endregion
    //#region getters & methods / init
    /**
     * init project files structure and depedencies
     */
    async init(initOptions) {
        throw new Error('TODO IMPLEMENT');
    }
    //#endregion
    //#region getters & methods / link
    /**
     * globally link npm as package
     */
    async link() {
        throw new Error('TODO IMPLEMENT');
    }
    //#endregion
    //#region getters & methods / struct
    /**
     * init project files structure without depedencies
     */
    async struct(initOptions) {
        throw new Error('TODO IMPLEMENT');
    }
    //#endregion
    //#region getters & methods / build
    /**
     * init and build() project
     */
    async test(testOptions) {
        throw new Error('TODO IMPLEMENT');
    }
    //#endregion
    //#region getters & methods / build
    /**
     * init and build() project
     */
    async build(buildOptions) {
        throw new Error('TODO IMPLEMENT');
    }
    //#endregion
    //#region getters & methods / publish
    /**
     * publish project to npm
     */
    async publish(publishOptions) {
        await this.npmHelpers.publishToNpmRegistry(publishOptions);
    }
    //#endregion
    //#region getters & methods / build
    async release(releaseOptions) {
        throw new Error('TODO IMPLEMENT');
    }
    //#endregion
    //#region getters & methods / lint
    /**
     * lint porject
     */
    async lint(lintOptions) {
        Helpers__NS__info(`

    COMMIT LINT NOT IMPLEMENTED

    `);
    }
    //#endregion
    //#region getters & methods / info
    /**
     * get info about porject
     */
    async info() {
        //#region @backendFunc
        const proj = this;
        return (`

  name: ${proj?.name}
  type: ${proj?.type}
  parent: ${proj?.parent?.genericName || '< none >'}
  core project name: '${proj?.core?.name}'
  embedded project: ${proj?.linkedProjects.embeddedProject?.genericName || '< none >'}
  children (${proj?.children.length}): ${!proj || !proj.children.length ? '< none >' : ''}
${proj?.children.map(c => '+' + c.genericName).join('\n')}

  libraries (${proj?.libraryBuild?.libraries?.length}): ${!proj || !proj.libraryBuild?.libraries?.length ? '< none >' : ''}
${proj?.libraryBuild?.libraries?.map(c => '+' + c.genericName).join('\n')}
` +
            `
linked porject prefix: "${this.linkedProjects.linkedProjectsPrefix}"

linked projects from json (${this.linkedProjects.linkedProjects?.length || 0}):
${(this.linkedProjects.linkedProjects || [])
                .map(c => {
                const proj = this.ins.From(this.pathFor(c.relativeClonePath));
                return '- ' + proj ? proj?.genericName : c.relativeClonePath;
            })
                .join('\n')}

  `);
        // linked projects detected (${this.detectedLinkedProjects?.length || 0}):
        // ${(this.detectedLinkedProjects || []).map(c => '- ' + c.relativeClonePath).join('\n')}
        //#endregion
    }
}
//# sourceMappingURL=c:/Users/darek/projects/npm/taon-dev/tnp-helpers/dist-nocutsrc-prod/lib/base/classes/base-project.js.map