"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaonBuildObserver = void 0;
const lib_1 = require("tnp-core/lib");
const lib_2 = require("tnp-helpers/lib");
const constants_1 = require("../../../../../constants");
const dev_mode_models_1 = require("../../../../abstract/taon-worker/dev-mode/dev-mode.models");
const build_leader_process_1 = require("./build-leader-process");
const taon_build_state_machine_1 = require("./taon-build-state-machine");
//#endregion
// @ts-ignore TODO weird inheritance problem
class TaonBuildObserver extends lib_2.BaseFeatureForProject {
    // TODO remove anyStd from exitprocess
    // TODO when in watch mode exit -> delete first all subprocesses (keep them here)
    leader;
    toNotifyLeaderPort;
    //#region fields & getters / allowed state map
    allowedStateMap = new Map([
        [
            dev_mode_models_1.DevMode.ProjectBuildStatus.NOT_STARTED,
            [dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING],
        ],
        [
            dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING,
            [
                dev_mode_models_1.DevMode.ProjectBuildStatus.DONE_BUILDING_SUCCESS,
                dev_mode_models_1.DevMode.ProjectBuildStatus.COMPILATION_ERROR,
                dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING_BUT_CANCELLED,
                dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING_WITH_REBUILD_AT_THE_END,
            ],
        ],
        [
            dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING_BUT_CANCELLED,
            [dev_mode_models_1.DevMode.ProjectBuildStatus.BUILD_CANCEL_DONE],
        ],
        [
            dev_mode_models_1.DevMode.ProjectBuildStatus.BUILD_CANCEL_DONE,
            [dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING],
        ],
        [
            dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING_WITH_REBUILD_AT_THE_END,
            [dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING],
        ],
        [
            dev_mode_models_1.DevMode.ProjectBuildStatus.COMPILATION_ERROR,
            [
                dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING,
                dev_mode_models_1.DevMode.ProjectBuildStatus.DONE_BUILDING_SUCCESS, // TODO_QUICK_FIX
            ],
        ],
        [
            dev_mode_models_1.DevMode.ProjectBuildStatus.DONE_BUILDING_SUCCESS,
            [dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING],
        ],
    ]);
    //#endregion
    //#region fields & getters / effect
    effect = (buildType, nextState, currentState, debugMode) => {
        if (currentState !== nextState) {
            if (nextState === dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING_BUT_CANCELLED) {
                this.project.watcher.cancelAndSetAsReadyForRebuildTrigger(buildType);
            }
            if (nextState === dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING) {
                lib_1.Helpers.logInfo(`Start build for ${buildType}`);
                this.project.watcher.triggerRebuildOf(buildType);
            }
            if (nextState === dev_mode_models_1.DevMode.ProjectBuildStatus.DONE_BUILDING_SUCCESS) {
                const buildWatcherTypeKey = lib_1.CoreModels.buildTypeToWatcherFn(buildType);
                this.dirtyBuild.set(buildWatcherTypeKey, false);
                if (this.toNotifyLeaderPort) {
                    this.notifyLeaderAboutBuildDone(buildType);
                }
            }
            this.writeBuildStatus();
        }
        this.debouceUpdate();
    };
    //#endregion
    dirtyBuild = new Map([
        ['backend-watcher', false],
        ['browser-watcher', false],
        ['websql-watcher', false],
    ]);
    writeBuildStatus = lib_1._.throttle(() => {
        //#region @backend
        if (!constants_1.debugModeTaonLightWeightWatchMode) {
            return;
        }
        this.project.writeFile(constants_1.TaonGeneratedFiles.BUILD_STATUS_MD, `# CURRENT BUILD STATUS (build port=${this.project.ins.currentActionPort})
### Backend
**CommonJS** : ${this.buildStatusInfo['backend-cjs']} <br>
**ESM**:  ${this.buildStatusInfo['backend-esm']}<br>
**Maps**: ${this.buildStatusInfo['backend-js-maps']}<br>
ERROR: ${this.buildStatusInfo['backend-watcher-error'] ? `${this.buildStatusInfo['backend-watcher-error']}` : '-'}
### Client
**browser**: ${this.buildStatusInfo['browser']}<br>
ERROR: ${this.buildStatusInfo['browser-watcher-error'] ? `${this.buildStatusInfo['browser-watcher-error']}` : '-'}<br>
**websql**: ${this.buildStatusInfo['websql']}<br>
ERROR: ${this.buildStatusInfo['websql-watcher-error'] ? `${this.buildStatusInfo['websql-watcher-error']}` : '-'}<br>

      `);
        //#endregion
    }, 300);
    //#region fields & getters / notifye build leader about child build done
    async notifyLeaderAboutBuildDone(toNotifyBuildType) {
        //#region @backend
        lib_1.Helpers.log(`Notifying leader that build status changed ${toNotifyBuildType}`);
        try {
            const devBuildControllerForProj = await this.project.ins.taonProjectsWorker.getDevBuildControllerForPort(this.toNotifyLeaderPort);
            await devBuildControllerForProj.unlockLeaderQueue(toNotifyBuildType)
                .request();
            lib_1.Helpers.log(`Done notifying leader that build status changed ${toNotifyBuildType} (port=${this.toNotifyLeaderPort})`);
        }
        catch (error) {
            lib_1.Helpers.error(`Not able to notify build leader about status chagne of ${toNotifyBuildType}`);
        }
        //#endregion
    }
    //#endregion
    //#region fields & getters / modify state before setting
    modifyStateBeforeSetting = (buildType, nextState, currentState) => {
        //#region handle BUILD_CANCEL_DONE after build
        if ([
            dev_mode_models_1.DevMode.ProjectBuildStatus.DONE_BUILDING_SUCCESS,
            dev_mode_models_1.DevMode.ProjectBuildStatus.COMPILATION_ERROR,
        ].includes(nextState) &&
            currentState === dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING_BUT_CANCELLED) {
            return dev_mode_models_1.DevMode.ProjectBuildStatus.BUILD_CANCEL_DONE;
        }
        //#endregion
        //#region external state __CANCEL_BUILD__
        if (nextState === dev_mode_models_1.DevMode.ProjectBuildStatus.__CANCEL_BUILD__) {
            if (currentState === dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING) {
                return dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING_BUT_CANCELLED;
            }
            return currentState;
        }
        //#endregion
        //#region handle BUILDING_WITH_REBUILD_AT_THE_END when done building
        if (currentState ===
            dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING_WITH_REBUILD_AT_THE_END &&
            [
                dev_mode_models_1.DevMode.ProjectBuildStatus.DONE_BUILDING_SUCCESS,
                dev_mode_models_1.DevMode.ProjectBuildStatus.COMPILATION_ERROR,
            ].includes(nextState)) {
            return dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING;
        }
        //#endregion
        //#region external state __SET_READY_FOR_REBUILD__
        if (nextState === dev_mode_models_1.DevMode.ProjectBuildStatus.__START_BUILD__) {
            if (currentState === dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING) {
                return dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING_WITH_REBUILD_AT_THE_END;
            }
            return dev_mode_models_1.DevMode.ProjectBuildStatus.BUILDING;
        }
        //#endregion
        return nextState;
    };
    //#endregion
    //#region fields & getters / debouce update
    debouceUpdate = lib_1._.debounce(async () => {
        await this.updateAction();
    }, constants_1.DEBOUNCE_debouceUpdate);
    //#endregion
    //#region fields & getters / machine state
    //#region fields & getters / machine state / backend state
    backendCjsState = new taon_build_state_machine_1.TaonStateMachine({
        defaultValue: dev_mode_models_1.DevMode.ProjectBuildStatus.NOT_STARTED,
        debugMode: constants_1.debugModeTaonLightWeightWatchMode,
        modifyStateBeforeSetting: (next, prev) => this.modifyStateBeforeSetting('backend-cjs', next, prev),
        allowedStateMap: this.allowedStateMap,
        effect: (nextState, previousState, debugMode) => {
            debugMode &&
                console.log(`Backend commonjs state changed: ${previousState} -> ${nextState}`);
            this.effect('backend-cjs', nextState, previousState, debugMode);
        },
    });
    backendEsmState = new taon_build_state_machine_1.TaonStateMachine({
        defaultValue: dev_mode_models_1.DevMode.ProjectBuildStatus.NOT_STARTED,
        debugMode: constants_1.debugModeTaonLightWeightWatchMode,
        modifyStateBeforeSetting: (next, prev) => this.modifyStateBeforeSetting('backend-esm', next, prev),
        allowedStateMap: this.allowedStateMap,
        effect: (nextState, previousState, debugMode) => {
            debugMode &&
                console.log(`Backend esm state changed: ${previousState} -> ${nextState}`);
            this.effect('backend-esm', nextState, previousState, debugMode);
        },
    });
    backendJsMapsState = new taon_build_state_machine_1.TaonStateMachine({
        defaultValue: dev_mode_models_1.DevMode.ProjectBuildStatus.NOT_STARTED,
        debugMode: constants_1.debugModeTaonLightWeightWatchMode,
        modifyStateBeforeSetting: (next, prev) => this.modifyStateBeforeSetting('backend-js-maps', next, prev),
        allowedStateMap: this.allowedStateMap,
        effect: (nextState, previousState, debugMode) => {
            debugMode &&
                console.log(`Backend js maps state changed: ${previousState} -> ${nextState}`);
            this.effect('backend-js-maps', nextState, previousState, debugMode);
        },
    });
    //#endregion
    //#region fields & getters / machine state / browser state
    browserState = new taon_build_state_machine_1.TaonStateMachine({
        defaultValue: dev_mode_models_1.DevMode.ProjectBuildStatus.NOT_STARTED,
        debugMode: constants_1.debugModeTaonLightWeightWatchMode,
        modifyStateBeforeSetting: (next, prev) => this.modifyStateBeforeSetting('browser', next, prev),
        allowedStateMap: this.allowedStateMap,
        effect: (nextState, previousState, debugMode) => {
            debugMode &&
                console.log(`Browser state changed: ${previousState} -> ${nextState}`);
            this.effect('browser', nextState, previousState, debugMode);
        },
    });
    //#endregion
    //#region fields & getters / machine state / websql state
    websqlState = new taon_build_state_machine_1.TaonStateMachine({
        defaultValue: dev_mode_models_1.DevMode.ProjectBuildStatus.NOT_STARTED,
        debugMode: constants_1.debugModeTaonLightWeightWatchMode,
        modifyStateBeforeSetting: (next, prev) => this.modifyStateBeforeSetting('websql', next, prev),
        allowedStateMap: this.allowedStateMap,
        effect: (nextState, previousState, debugMode) => {
            debugMode &&
                console.log(`Websql state changed: ${previousState} -> ${nextState}`);
            this.effect('websql', nextState, previousState, debugMode);
        },
    });
    //#endregion
    //#region fields & getters / machine state / copy manager state
    // public copyManagerState = new TaonStateMachine<DevMode.ProjectBuildStatus>({
    //   defaultValue: DevMode.ProjectBuildStatus.NOT_STARTED,
    //   debugMode: debugModeTaonLightWeightWatchMode,
    //   modifyStateBeforeSetting: (next, prev) =>
    //     this.modifyStateBeforeSetting('copy-manager', next, prev),
    //   allowedStateMap: this.allowedStateMap,
    //   effect: (nextState, previousState, debugMode) => {
    //     debugMode &&
    //       console.log(
    //         `Copy manager state changed: ${previousState} -> ${nextState}`,
    //       );
    //     this.effect('copy-manager', nextState, previousState, debugMode);
    //   },
    // });
    //#endregion
    //#region fields & getters / machine state / isomorphic state
    // public isomorphicCompilationState =
    //   new TaonStateMachine<DevMode.ProjectBuildStatus>({
    //     defaultValue: DevMode.ProjectBuildStatus.NOT_STARTED,
    //     debugMode: debugModeTaonLightWeightWatchMode,
    //     modifyStateBeforeSetting: (next, prev) =>
    //       this.modifyStateBeforeSetting('isomorphic', next, prev),
    //     allowedStateMap: this.allowedStateMap,
    //     effect: (nextState, previousState, debugMode) => {
    //       debugMode &&
    //         console.log(
    //           `Isomorphic compilation state changed: ${previousState} -> ${nextState}`,
    //         );
    //       this.effect('isomorphic', nextState, previousState, debugMode);
    //     },
    //   });
    //#endregion
    //#region fields & getters / machine state / error backend
    errorBackend = new taon_build_state_machine_1.TaonStateMachine({
        debugMode: constants_1.debugModeTaonLightWeightWatchMode,
        defaultValue: void 0,
    });
    //#endregion
    //#region fields & getters / machine state / error browser
    errorBrowser = new taon_build_state_machine_1.TaonStateMachine({
        defaultValue: void 0,
        debugMode: constants_1.debugModeTaonLightWeightWatchMode,
    });
    //#endregion
    //#region fields & getters / machine state / error websql
    errorWebsql = new taon_build_state_machine_1.TaonStateMachine({
        defaultValue: void 0,
        debugMode: constants_1.debugModeTaonLightWeightWatchMode,
    });
    //#endregion
    //#region fields & getters / machine state / states
    states = new Map([
        ['backend-cjs', this.backendCjsState],
        ['backend-esm', this.backendEsmState],
        ['backend-js-maps', this.backendJsMapsState],
        ['browser', this.browserState],
        ['websql', this.websqlState],
        // ['isomorphic', this.isomorphicCompilationState],
        // ['copy-manager', this.copyManagerState],
    ]);
    stateError = new Map([
        ['backend-watcher-error', this.errorBackend],
        ['browser-watcher-error', this.errorBrowser],
        ['websql-watcher-error', this.errorWebsql],
    ]);
    //#endregion
    //#endregion
    //#region fields & getters / build status info
    get buildStatusInfo() {
        return {
            'backend-cjs': this.backendCjsState.currentValue,
            'backend-esm': this.backendEsmState.currentValue,
            'backend-js-maps': this.backendJsMapsState.currentValue,
            browser: this.browserState.currentValue,
            websql: this.websqlState.currentValue,
            // isomorphic: this.isomorphicCompilationState.currentValue,
            // 'copy-manager': this.copyManagerState.currentValue,
            'backend-watcher-error': this.errorBackend.currentValue,
            'browser-watcher-error': this.errorBrowser.currentValue,
            'websql-watcher-error': this.errorWebsql.currentValue,
        };
    }
    //#endregion
    //#region constructor
    constructor(project) {
        super(project);
        this.leader = new build_leader_process_1.BuildLeader(project, this);
    }
    //#endregion
    //#region private methods / merge status
    mergeStatus(info) {
        if (!lib_1._.isObject(info)) {
            return;
        }
        Object.keys(this.buildStatusInfo).forEach(key => {
            if (!lib_1._.isUndefined(info[key])) {
                if (key.includes(constants_1.watcherPrefix)) {
                    this.stateError
                        .get(key)
                        .set(info[key]);
                }
                else {
                    this.states.get(key).set(info[key]);
                }
            }
        });
    }
    //#endregion
    //#region public method / update action
    /**
     * errors or non-watch mode needs to for instant predictable updates
     */
    async updateAction(info) {
        //#region @backendFunc
        this.mergeStatus(info);
        try {
            await this.project.ins.devBuildRepository.updatePool({
                buildStatusInfo: this.buildStatusInfo,
            });
        }
        catch (error) {
            lib_1.config.frameworkName === lib_1.tnpPackageName &&
                console.error('update action', error);
            lib_1.Helpers.warn(`ERORRO: Not able to connect to main worker...`);
        }
        //#endregion
    }
    //#endregion
    //#region public methods / start
    start(envOptions) {
        //#region @backendFunc
        lib_1.Helpers.info(`

      USING TAON NEW BUILD MODE
(to use old add flag ${constants_1.oldBuildModePrefix} or short version ${constants_1.oldBuildModePrefixShort})

      `);
        this.leader.recreateNotifiers();
        //#region handle code changes
        const handleCodeChanges = (buildType) => {
            this.project.watcher
                .getTsCodeObservableFor(buildType)
                // .pipe()
                .subscribe(() => {
                if (!this.leader.isAllowedForLeadBuild) {
                    //#region handle error before allowed lead build
                    if (buildType === 'backend-watcher' && this.anyBackendError) {
                        if (this.backendCjsState.currentValue ===
                            dev_mode_models_1.DevMode.ProjectBuildStatus.COMPILATION_ERROR) {
                            this.backendCjsState.set(dev_mode_models_1.DevMode.ProjectBuildStatus.__START_BUILD__);
                        }
                        if (this.backendEsmState.currentValue ===
                            dev_mode_models_1.DevMode.ProjectBuildStatus.COMPILATION_ERROR) {
                            this.backendEsmState.set(dev_mode_models_1.DevMode.ProjectBuildStatus.__START_BUILD__);
                        }
                        if (this.backendJsMapsState.currentValue ===
                            dev_mode_models_1.DevMode.ProjectBuildStatus.COMPILATION_ERROR) {
                            this.backendJsMapsState.set(dev_mode_models_1.DevMode.ProjectBuildStatus.__START_BUILD__);
                        }
                    }
                    else if (buildType === 'browser-watcher' &&
                        this.anyBrowserError) {
                        this.browserState.set(dev_mode_models_1.DevMode.ProjectBuildStatus.__START_BUILD__);
                    }
                    else if (buildType === 'websql-watcher' && this.anyWebsqlError) {
                        this.websqlState.set(dev_mode_models_1.DevMode.ProjectBuildStatus.__START_BUILD__);
                    }
                    //#endregion
                    return;
                }
                this.dirtyBuild.set(buildType, true);
                this.leader.trigerLeadBuilding();
            });
        };
        //#endregion
        if (envOptions.build.watch) {
            for (const watcherType of lib_1.CoreModels.BuildWatcherTypeArr) {
                handleCodeChanges(watcherType);
            }
        }
        lib_1.chokidar.watch(this.project.taonJson.path).on('change', () => {
            this.debouceUpdate();
        });
        //#endregion
    }
    //#endregion
    //#region private methods / any errors checks
    get anyBackendError() {
        return (this.backendCjsState.currentValue ===
            dev_mode_models_1.DevMode.ProjectBuildStatus.COMPILATION_ERROR ||
            this.backendEsmState.currentValue ===
                dev_mode_models_1.DevMode.ProjectBuildStatus.COMPILATION_ERROR ||
            this.backendJsMapsState.currentValue ===
                dev_mode_models_1.DevMode.ProjectBuildStatus.COMPILATION_ERROR);
    }
    get anyBrowserError() {
        return (this.browserState.currentValue ===
            dev_mode_models_1.DevMode.ProjectBuildStatus.COMPILATION_ERROR);
    }
    get anyWebsqlError() {
        return (this.websqlState.currentValue ===
            dev_mode_models_1.DevMode.ProjectBuildStatus.COMPILATION_ERROR);
    }
}
exports.TaonBuildObserver = TaonBuildObserver;
//# sourceMappingURL=taon-build-observer.js.map