import { UtilsFilesFoldersSync__NS__readFile, UtilsFilesFoldersSync__NS__writeFile } from 'tnp-core/lib-prod';
import { ___NS__isEqual, ___NS__times } from 'tnp-core/lib-prod';
import { Helpers__NS__log, Helpers__NS__logInfo, Helpers__NS__taskDone, Helpers__NS__taskStarted, UtilsTypescript__NS__exportsFromFile, UtilsTypescript__NS__setValueToVariableInTsFile } from 'tnp-helpers/lib-prod';
import { BaseDebounceCompilerForProject, } from 'tnp-helpers/lib-prod';
import { AngularJsonTaskName, appFromSrc, DEFAULT_PORT, FRONTEND_NORMAL_APP_PORT, FRONTEND_NORMAL_ELECTRON_PORT, FRONTEND_WEBSQL_APP_PORT, HOST_BACKEND_PORT, MIGRATION_CONST_PREFIX, migrationIndexAutogeneratedTsFileRelativeToSrcPath, srcMainProject, TaonCommands, TaonGeneratedFiles, THIS_IS_GENERATED_INFO_COMMENT, tmp_FRONTEND_NORMAL_APP_PORT, tmp_FRONTEND_NORMAL_ELECTRON_PORT, tmp_FRONTEND_WEBSQL_APP_PORT, tmp_HOST_BACKEND_PORT, USE_IN_HOST_CONFIG_FULL_CONTEXT_PATH, USE_MIGRATIONS_DATA_IN_HOST_CONFIG, } from '../../../../../constants';
import { EnvOptions, ReleaseArtifactTaon, } from '../../../../../options';
//#endregion
/**
 * handle dockers image in taon projects
 */
export class AppHostsRecreateHelper extends BaseDebounceCompilerForProject {
    //#region constructor
    baseSrcFolder;
    lastTaonContexts = [];
    lastMigrationExported = [];
    constructor(project) {
        super(project, {
            folderPath: [project.pathFor([srcMainProject])],
            subscribeOnlyFor: ['ts', 'tsx'],
            followSymlinks: false,
            taskName: 'AppHostsTsRecreateHelper',
            notifyOnFileUnlink: true,
        });
        this.baseSrcFolder = project.pathFor(srcMainProject) + '/';
    }
    //#endregion
    //#region getters
    get envOptions() {
        return this.initialParams.envOptions;
    }
    //#endregion
    //#region rebuild
    async rebuild(changeOfFiles, asyncEvent) {
        //#region @backendFunc
        Helpers__NS__taskStarted(`Rebuilding app.hosts.ts`);
        // if (asyncEvent) {
        //   console.log('ASYNC EVNET changeOfFiles', changeOfFiles);
        //   // console.log('env release', this.envOptions.release);
        // } else {
        //   console.log('SYNC EVNET changeOfFiles', changeOfFiles);
        // }
        const taonContexts = this.project.framework.getAllDetectedTaonContexts();
        const migrationExported = USE_MIGRATIONS_DATA_IN_HOST_CONFIG
            ? UtilsTypescript__NS__exportsFromFile(this.project.pathFor([
                srcMainProject,
                migrationIndexAutogeneratedTsFileRelativeToSrcPath,
            ])).filter(c => c.type === 'const') || []
            : [];
        if (this.lastTaonContexts?.length > 0 &&
            ___NS__isEqual(taonContexts, this.lastTaonContexts) &&
            (USE_MIGRATIONS_DATA_IN_HOST_CONFIG
                ? ___NS__isEqual(migrationExported, this.lastMigrationExported)
                : true)) {
            Helpers__NS__logInfo(`No need for taon context update in app.hosts `);
            return;
        }
        this.lastTaonContexts = taonContexts;
        this.lastMigrationExported = migrationExported;
        this.writePortsToFile();
        await this.updatePortsInHosts(this.envOptions);
        Helpers__NS__taskDone(`Rebuilding app.hosts.ts Done`);
        //#endregion
    }
    async action({ changeOfFiles, asyncEvent, }) {
        //#region @backendFunc
        changeOfFiles = changeOfFiles.filter(c => {
            const relativePath = c.fileAbsolutePath.replace(this.baseSrcFolder, '');
            return this.project.framework.contextFilter(relativePath);
        });
        if (changeOfFiles.length === 0) {
            Helpers__NS__log(`No file detected that affects app.hosts.ts`);
            return;
        }
        await this.rebuild(changeOfFiles, asyncEvent);
        //#endregion
    }
    //#endregion
    //#region private methods / write ports to file
    writePortsToFile() {
        // #region @backend
        Helpers__NS__taskStarted('Writing hosts and ports config to app.hosts ...');
        //#region prepare data\
        const appHostsFile = this.project.pathFor([
            srcMainProject,
            TaonGeneratedFiles.APP_HOSTS_TS,
        ]);
        const filesWithContexts = {};
        const migrationExported = this.lastMigrationExported;
        const contexts = this.lastTaonContexts.filter(f => f.fileRelativePath.startsWith(`${appFromSrc}/`) ||
            f.fileRelativePath.startsWith(`${appFromSrc}.`));
        // console.log({ taonContexts });
        for (const context of contexts) {
            if (!filesWithContexts[context.fileRelativePath]) {
                filesWithContexts[context.fileRelativePath] = [];
            }
            filesWithContexts[context.fileRelativePath].push(context.contextName);
        }
        let counter = 0;
        const depecationMessage = `\n/** @deprecated avoid using HOST_CONFIG inside library code (src/lib/**) */\n`;
        //#endregion
        //#region contexts template
        const tempalte = (n) => {
            return (`
/**
 * Your context backend port
 * ${!n ? '@deprecated use HOST_BACKEND_PORT_n instead' : ''}
 */
export const HOST_BACKEND_PORT${n ? `_${n}` : ''} = ` +
                `${this.prefixVarTemplate('HOST_BACKEND_PORT', n) + (n ? `undefined` : 'HOST_BACKEND_PORT_1')};
/**
 * Angular website url with normal/nodejs backend
 * ${!n ? '@deprecated use FRONTEND_NORMAL_APP_PORT_n instead' : ''}
 */
export const FRONTEND_NORMAL_APP_PORT${n ? `_${n}` : ''} = ` +
                `${this.prefixVarTemplate('FRONTEND_NORMAL_APP_PORT', n, { sameAsFirstInDevMode: true }) + (n ? `undefined` : 'FRONTEND_NORMAL_APP_PORT_1')};

/**
 * @deprecated use FRONTEND_NORMAL_APP_PORT instead
*/
export const CLIENT_DEV_NORMAL_APP_PORT${n ? `_${n}` : ''} = FRONTEND_NORMAL_APP_PORT${n ? `_${n}` : ''};

/**
 * Angular website url with websql backend
 * ${!n ? '@deprecated use FRONTEND_WEBSQL_APP_PORT_n instead' : ''}
 */
export const FRONTEND_WEBSQL_APP_PORT${n ? `_${n}` : ''} = ` +
                `${this.prefixVarTemplate('FRONTEND_WEBSQL_APP_PORT', n, { sameAsFirstInDevMode: true }) + (n ? `undefined` : 'FRONTEND_WEBSQL_APP_PORT_1')};

/**
 * @deprecated use FRONTEND_WEBSQL_APP_PORT instead
*/
export const CLIENT_DEV_WEBSQL_APP_PORT${n ? `_${n}` : ''} = FRONTEND_WEBSQL_APP_PORT${n ? `_${n}` : ''};

/**
 * Electron/angular website url for electron app purpose (ipc backend)
 */
export const FRONTEND_NORMAL_ELECTRON_PORT${n ? `_${n}` : ''} = ${n ? `undefined` : 'FRONTEND_NORMAL_ELECTRON_PORT_1'};
// electron websql not supported yet
// export const FRONTEND_WEBSQL_ELECTRON_PORT${n ? `_${n}` : ''} = ${n ? `undefined` : 'FRONTEND_WEBSQL_ELECTRON_PORT_1'};
/**
 * Backend url - use as "host" inside your context
 * ${!n ? '@deprecated use HOST_URL_n instead' : ''}
 */
export const HOST_URL${n ? `_${n}` : ''} =  ${this.prefixVarTemplate('HOST_URL', n, { isURL: true })} ('http://localhost:' + HOST_BACKEND_PORT${n ? `_${n}` : ''});
/**
 * Frontend host url - use as "frontendHost" inside your context
 * ${!n ? '@deprecated use FRONTEND_HOST_URL_n instead' : ''}
 */
export const FRONTEND_HOST_URL${n ? `_${n}` : ''} = ${this.prefixVarTemplate('FRONTEND_HOST_URL', n, { isURL: true, sameAsFirstInDevMode: true })}  ( 'http://localhost:' +
  (isWebSQLMode ? FRONTEND_WEBSQL_APP_PORT${n ? `_${n}` : ''} : FRONTEND_NORMAL_APP_PORT${n ? `_${n}` : ''}));
/**
 * Frontend electron host url - use in app.electron.ts with win.loadURL(FRONTEND_HOST_URL_ELECTRON);
 */
export const FRONTEND_HOST_URL_ELECTRON${n ? `_${n}` : ''} = 'http://localhost:' + FRONTEND_NORMAL_ELECTRON_PORT${n ? `_${n}` : ''}
// electron websql not supported yet
// export const FRONTEND_HOST_URL_ELECTRON${n ? `_${n}` : ''} =
//  'http://localhost:' +
//  (isWebSQLMode ? FRONTEND_WEBSQL_ELECTRON_PORT${n ? `_${n}` : ''} : FRONTEND_NORMAL_ELECTRON_PORT${n ? `_${n}` : ''});

  `);
        };
        //#endregion
        //#region new data app hosts
        const newData = `
${THIS_IS_GENERATED_INFO_COMMENT}
${'imp' + 'ort'} { APP_ID, BUILD_BASE_HREF } from '${'./lib/build-info._auto-generated_'}';
${migrationExported
            .map(c => `import { ${c.name} } ` +
            `from './${migrationIndexAutogeneratedTsFileRelativeToSrcPath.replace(/\.ts$/, '')}';`)
            .join('\n')}

let isWebSQLMode: boolean = false;
//#${'reg' + 'ion'} @${'bac' + 'kend'}
isWebSQLMode = false;
//#${'end' + 'reg' + 'ion'}

//#${'reg' + 'ion'} @${'web' + 'sql' + 'Only'}
isWebSQLMode = true;
//#${'end' + 'reg' + 'ion'}

const nodeENV = (()=> {
  let env: any;
  //#${'reg' + 'ion'} @${'bac' + 'kend'}
  env = process.env || {};
  //#${'endr' + 'egion'}
  return env || {};
})();

const windowENV = (()=> {
  let env: any;
  //#${'reg' + 'ion'} @${'bro' + 'wser'}
  env = globalThis['ENV'] || {};
  //#${'endr' + 'egion'}
  return env || {};
})();

const argsENV = (()=> {
  let env: any;
  //#${'reg' + 'ion'} @${'bac' + 'kend'}
  env = require('minimist')(process.argv);
  //#${'endr' + 'egion'}
  return env || {};
})();

const transformURL = (url: string): string => {
  if (!url) {
    return url;
  }
  //#${'reg' + 'ion'} @${'bac' + 'kend'}
  if (url.startsWith('http://') || url.startsWith('https://')) {
    return url;
  }
  return 'http://' + url; // by default backend is http
  //#${'endr' + 'egion'}
  //#${'reg' + 'ion'} @${'bro' + 'wser'}
  if (url.startsWith('http://') || url.startsWith('https://')) {
    return url;
  }
  return globalThis?.location?.protocol + '//' + url;
  //#${'endr' + 'egion'}
};

const ACTIVE_CONTEXT: string | null = nodeENV['ACTIVE_CONTEXT'] || argsENV['ACTIVE_CONTEXT'] || windowENV['ACTIVE_CONTEXT'] || null;

${___NS__times(contexts.length, i => {
            return tempalte(i + 1);
        }).join('\n')}
${contexts.length > 0
            ? tempalte()
            : `

export const CLIENT_DEV_NORMAL_APP_PORT = void 0;
export const CLIENT_DEV_WEBSQL_APP_PORT = void 0;
  `}

export const HOST_CONFIG = {
${Object.keys(filesWithContexts)
            .sort((a, b) => a.localeCompare(b))
            .map((contextFileName, index) => {
            const contextsInFile = (filesWithContexts[contextFileName] || []).sort((a, b) => a.localeCompare(b));
            const contextIsInsideLibInsteadApp = contextFileName.startsWith('lib/');
            if (contextIsInsideLibInsteadApp) {
                return `// Context(s) "${contextsInFile.join(',')}" from /src/lib can't use HOST_CONFIG - only code from /src/app or scr/app.* can use it.`;
            }
            return `
    ${contextIsInsideLibInsteadApp ? depecationMessage : `\n/** Relative file path for context */\n`}
    ${!USE_IN_HOST_CONFIG_FULL_CONTEXT_PATH ? '//' : ''}'${contextFileName}' ${USE_IN_HOST_CONFIG_FULL_CONTEXT_PATH ? `: {` : ''}
${contextsInFile
                .map((contextName, i) => {
                const contextMigraions = migrationExported.find(c => c.name.replace(MIGRATION_CONST_PREFIX, '') === contextName)?.name;
                ++counter;
                return `
${contextIsInsideLibInsteadApp ? depecationMessage : `\n/** Name of context (var, let, const variable) inside *.ts file. */\n`}
        '${contextName}': {
        ${contextIsInsideLibInsteadApp ? depecationMessage : ''}
         activeContext: ACTIVE_CONTEXT,
        ${contextIsInsideLibInsteadApp ? depecationMessage : ''}
         contextName: '${contextName}',
        ${contextIsInsideLibInsteadApp ? depecationMessage : ''}
         hostPortNumber: Number(HOST_BACKEND_PORT_${counter}),
         ${contextIsInsideLibInsteadApp ? depecationMessage : ''}
         host: HOST_URL_${counter} + BUILD_BASE_HREF,
         ${contextIsInsideLibInsteadApp ? depecationMessage : ''}
         frontendHostPortNumber: Number(FRONTEND_NORMAL_APP_PORT_${counter}),
         ${contextIsInsideLibInsteadApp ? depecationMessage : ''}
         frontendHost: FRONTEND_HOST_URL_${counter} + BUILD_BASE_HREF,
         ${contextIsInsideLibInsteadApp ? depecationMessage : ''}
         appId: APP_ID,
        ${USE_MIGRATIONS_DATA_IN_HOST_CONFIG ? `//#${'reg' + 'ion'}  @${'web' + 'sql'}` : ''}
         ${USE_MIGRATIONS_DATA_IN_HOST_CONFIG && contextMigraions && contextIsInsideLibInsteadApp ? depecationMessage : ''}
         ${USE_MIGRATIONS_DATA_IN_HOST_CONFIG && contextMigraions
                    ? `migrations: { ...${contextMigraions} },`
                    : USE_MIGRATIONS_DATA_IN_HOST_CONFIG
                        ? `// no migrations detected  for this context`
                        : ''}
        ${USE_MIGRATIONS_DATA_IN_HOST_CONFIG ? `//#${'end' + 'reg' + 'ion'}` : ''}
      }`;
            })
                .map(c => c.trimStart())
                .join(',\n')}${!USE_IN_HOST_CONFIG_FULL_CONTEXT_PATH ? '' : '}'}`;
        })
            .join(',\n')}
}

${THIS_IS_GENERATED_INFO_COMMENT}

      `;
        //#endregion
        const currentData = UtilsFilesFoldersSync__NS__readFile(appHostsFile);
        if (currentData?.trimEnd() !== newData.trimEnd()) {
            UtilsFilesFoldersSync__NS__writeFile(appHostsFile, newData);
            Helpers__NS__taskDone('Done writing ports and hosts to app.hosts.ts');
        }
        //#endregion
    }
    //#endregion
    //#region prefix var template
    prefixVarTemplate(varName, n, options) {
        //#region @backendFunc
        options = options || {};
        let { isURL, sameAsFirstInDevMode } = options;
        isURL = !!isURL;
        sameAsFirstInDevMode = !!sameAsFirstInDevMode;
        const IS_DEV_MODE = sameAsFirstInDevMode && n > 1
            ? `(!ACTIVE_CONTEXT ? ${varName}_1 : undefined ) ||`
            : '';
        return (`${IS_DEV_MODE} ${isURL ? 'transformURL' : ''}(nodeENV['${varName}${n ? `_${n}` : ''}']) || ` +
            `${isURL ? 'transformURL' : ''}(windowENV['${varName}${n ? `_${n}` : ''}']) || ` +
            `${isURL ? 'transformURL' : ''}(argsENV['${varName}${n ? `_${n}` : ''}']) || `);
        //#endregion
    }
    //#endregion
    //#region public methods / update ports in hosts
    async updatePortsInHosts(buildOptions) {
        //#region @backendFunc
        let contexts = [];
        const contextTemplate = (options) => {
            return `###  **${options.contextName}** (#${options.num})
- nodejs backend http://localhost:${options.nodeBeAppPort}
- normal frontend app for nodejs backend **http://localhost:${options.ngNormalAppPort}**
- websql app backend/frontend **http://localhost:${options.ngWebsqlAppPort}**
`;
        };
        const allDetectedContexts = this.project.framework
            .getAllDetectedTaonContexts()
            .filter(f => f.fileRelativePath.startsWith(`${appFromSrc}/`) ||
            f.fileRelativePath.startsWith(`${appFromSrc}.`));
        const ONLY_ONE_FRONTEND_IN_DEV = true;
        let ngWebsqlAppPortFirst;
        let ngNormalAppPortFirst;
        let ngWebsqlElectronPortFirst;
        let ngNormalElectronPortFirst;
        for (let i = 0; i < allDetectedContexts.length; i++) {
            const nodeBeAppPort = await this.NODE_BACKEND_PORT_UNIQ_KEY(buildOptions.clone({
                build: {
                    websql: false,
                },
                release: {
                    targetArtifact: ReleaseArtifactTaon.ANGULAR_NODE_APP,
                },
            }), i + 1);
            const ngWebsqlAppPort = ONLY_ONE_FRONTEND_IN_DEV && ngWebsqlAppPortFirst
                ? ngWebsqlAppPortFirst
                : await this.APP_NG_SERVE_ARTIFACT_PORT_UNIQ_KEY(buildOptions.clone({
                    build: {
                        websql: true,
                    },
                    release: {
                        targetArtifact: ReleaseArtifactTaon.ANGULAR_NODE_APP,
                    },
                }), {
                    num: i + 1,
                });
            ngWebsqlAppPortFirst = ngWebsqlAppPortFirst || ngWebsqlAppPort;
            const ngNormalAppPort = ONLY_ONE_FRONTEND_IN_DEV && ngNormalAppPortFirst
                ? ngNormalAppPortFirst
                : await this.APP_NG_SERVE_ARTIFACT_PORT_UNIQ_KEY(buildOptions.clone({
                    build: {
                        websql: false,
                    },
                    release: {
                        targetArtifact: ReleaseArtifactTaon.ANGULAR_NODE_APP,
                    },
                }), {
                    num: i + 1,
                });
            ngNormalAppPortFirst = ngNormalAppPortFirst || ngNormalAppPort;
            const ngWebsqlElectronPort = ONLY_ONE_FRONTEND_IN_DEV && ngWebsqlElectronPortFirst
                ? ngWebsqlElectronPortFirst
                : await this.APP_NG_SERVE_ARTIFACT_PORT_UNIQ_KEY(buildOptions.clone({
                    build: {
                        websql: true,
                    },
                    release: {
                        targetArtifact: ReleaseArtifactTaon.ELECTRON_APP,
                    },
                }), {
                    num: i + 1,
                });
            ngWebsqlElectronPortFirst =
                ngWebsqlElectronPortFirst || ngWebsqlElectronPortFirst;
            const ngNormalElectronPort = ONLY_ONE_FRONTEND_IN_DEV && ngNormalElectronPortFirst
                ? ngNormalElectronPortFirst
                : await this.APP_NG_SERVE_ARTIFACT_PORT_UNIQ_KEY(buildOptions.clone({
                    build: {
                        websql: false,
                    },
                    release: {
                        targetArtifact: ReleaseArtifactTaon.ELECTRON_APP,
                    },
                }), {
                    num: i + 1,
                });
            ngNormalElectronPortFirst =
                ngNormalElectronPortFirst || ngNormalElectronPort;
            contexts.push(contextTemplate({
                num: i + 1,
                contextName: allDetectedContexts[i].contextName,
                nodeBeAppPort,
                ngWebsqlAppPort,
                ngWebsqlElectronPort,
                ngNormalAppPort,
                ngNormalElectronPort,
            }));
            UtilsTypescript__NS__setValueToVariableInTsFile(this.project.pathFor('src/app.hosts.ts'), `${HOST_BACKEND_PORT}_` + (i + 1), this.prefixVarTemplate(HOST_BACKEND_PORT, i + 1) + nodeBeAppPort, {
                useRawStringValue: true,
            });
            this.project.writeFile(`${tmp_HOST_BACKEND_PORT}_${i + 1}`, '' + nodeBeAppPort);
            UtilsTypescript__NS__setValueToVariableInTsFile(this.project.pathFor('src/app.hosts.ts'), `${FRONTEND_WEBSQL_APP_PORT}_` + (i + 1), this.prefixVarTemplate(FRONTEND_WEBSQL_APP_PORT, i + 1) +
                ngWebsqlAppPort, {
                useRawStringValue: true,
            });
            this.project.writeFile(`${tmp_FRONTEND_WEBSQL_APP_PORT}_${i + 1}`, '' + ngWebsqlAppPort);
            UtilsTypescript__NS__setValueToVariableInTsFile(this.project.pathFor('src/app.hosts.ts'), `${FRONTEND_NORMAL_APP_PORT}_` + (i + 1), this.prefixVarTemplate(FRONTEND_NORMAL_APP_PORT, i + 1) +
                ngNormalAppPort, {
                useRawStringValue: true,
            });
            this.project.writeFile(`${tmp_FRONTEND_NORMAL_APP_PORT}_${i + 1}`, '' + ngNormalAppPort);
            // TODO electron websql not supported yet
            // UtilsTypescript__NS__setValueToVariableInTsFile(
            //   this.project.pathFor('src/app.hosts.ts'),
            //   'FRONTEND_WEBSQL_ELECTRON_PORT_' + (i + 1),
            //   ngWebsqlElectronPort,
            // );
            UtilsTypescript__NS__setValueToVariableInTsFile(this.project.pathFor(`${srcMainProject}/${TaonGeneratedFiles.APP_HOSTS_TS}`), `${FRONTEND_NORMAL_ELECTRON_PORT}_` + (i + 1), ngNormalElectronPort);
            this.project.writeFile(`${tmp_FRONTEND_NORMAL_ELECTRON_PORT}_${i + 1}`, '' + ngNormalElectronPort);
        }
        this.project.writeFile(TaonGeneratedFiles.BUILD_INFO_MD, `# CURRENT BUILD INFO

Project name: **${this.project.name}** <br>
Project npm name: **${this.project.nameForNpmPackage}**

Current build:
- last git message: ${this.project.git.lastCommitMessage()}
- last git hash: ${this.project.git.lastCommitHash()}
- last git date: ${this.project.git.lastCommitDate()}

## All contexts (detected = ${allDetectedContexts.length})

${contexts.join('\n')}
`);
        //#endregion
    }
    //#endregion
    //#region public methods / get ng server unique key
    async APP_NG_SERVE_ARTIFACT_PORT_UNIQ_KEY(buildOptions, options) {
        //#region @backendFunc
        options = options || {};
        const { num } = options;
        buildOptions = EnvOptions.from(buildOptions);
        const key = `${TaonCommands.NG} ${buildOptions.release.targetArtifact === ReleaseArtifactTaon.ELECTRON_APP
            ? AngularJsonTaskName.ELECTRON_APP
            : AngularJsonTaskName.ANGULAR_APP}` +
            ` (${buildOptions.build.websql ? 'websql' : 'normal'})` +
            ` # context=${num || 1} `;
        return await this.project.registerAndAssignPort(key, {
            startFrom: DEFAULT_PORT.APP_BUILD_LOCALHOST,
        });
        //#endregion
    }
    //#endregion
    //#region public methods / get node backend unique key
    async NODE_BACKEND_PORT_UNIQ_KEY(buildOptions, num) {
        buildOptions = EnvOptions.from(buildOptions);
        const key = `node backend${num ? ` (${num})` : ''}`;
        return await this.project.registerAndAssignPort(key, {
            startFrom: DEFAULT_PORT.SERVER_LOCALHOST,
        });
    }
}
//# sourceMappingURL=app-hosts-recreate-helper.js.map