const FSWrapper = require('@sisense-ps/fs-wrapper/index');
const windowsServiceHelper = require('../helpers/WindowsServiceHelper');

const BaseInstaller = require('./BaseInstaller')
const MESSAGES = require('../constants/Messages');
const PSLogger = require('@sisense-ps/ps-logger/index');
const CONSTANTS = require('../constants/index');
const ValidationHelper = require('../helpers/ValidationHelper');

class WindowsInstaller extends BaseInstaller {
    constructor() {
        super()

        this._mapArgumentToConstKey = {
            "b": "BUILD",
            "q": "QUERY",
            "a": "APP",
            "s": "SINGLE"
        }
    }

    //#region before_actions

    async __beforeRemoveSources(fromToMap) {
        let deleteResult = { isSuccess: true };
        if (!fromToMap.microservice) {
            return deleteResult;
        }

        await fromToMap.microservice.reduce(async (promise, microserviceMember) => {
            await promise;

            const queryResult = await windowsServiceHelper.queryServiceByName(microserviceMember);
            let stopResult = {};
            if (!this._isServiceNotRegistered(queryResult) && queryResult.data.STATE.includes('RUNNING')) {
                stopResult = await windowsServiceHelper.stopService(microserviceMember);
            }

            if (stopResult.isSuccess || (queryResult.isSuccess && queryResult.data.STATE.includes('STOPPED'))) {
                deleteResult = await windowsServiceHelper.deleteService(microserviceMember);
            }
        }, Promise.resolve());

        return deleteResult;
    }

    async __afterRemoveSources(_) {
        return this._restartGateway();
    }

    async _restartGateway() {
        let queryResult = await windowsServiceHelper.queryServiceConfigurationByName({ serviceName: 'Sisense.Gateway' });
        if (queryResult.isSuccess && !queryResult.data.START_TYPE.includes('DISABLED')) {
            queryResult = await windowsServiceHelper.restartService({ serviceName: 'Sisense.Gateway' });
        }
        return queryResult;

    }

    async __beforeUpdate(fromToMap) {
        return this.__beforeRemoveSources(fromToMap);
    }

    //#endregion 


    //#region actions

    __install(fromToMap, component, member) {
        FSWrapper.copyFolderContentsRecursiveSync(member.source, member.destination, '', '');
        return { isSuccess: true };
    }

    __remove(fromToMap, component, member) {
        const { destination } = member;
        if (ValidationHelper.isByNameRemove(component)) {
            FSWrapper.recursiveSearch(destination, new RegExp(this.addonName), undefined, undefined, (foundPath) => {
                FSWrapper.rmContentSync(foundPath, '');
            });

            return { isSuccess: true };
        }

        FSWrapper.rmDirSync(destination, '');
        return { isSuccess: true };
    }

    __check(fromToMap, component, member, skipMessage) {
        const resultMap = {};

        if (ValidationHelper.isByNameRemove(component)) {
            FSWrapper.readDirSync(fromToMap.translations[0].source, '').forEach((translationFileName) => {
                const isExist = FSWrapper.isPathExistSync(FSWrapper.combinePath([member.destination, translationFileName]), '');
                resultMap[isExist] = 1;
            });
        } else {
            const isExist = FSWrapper.isPathExistSync(member.destination, '');
            resultMap[isExist] = 1;
        }

        if (!skipMessage) {
            PSLogger.log(resultMap.hasOwnProperty(false) ? MESSAGES.messageMap.CHECK_PATH_ERROR : MESSAGES.messageMap.CHECK_PATH_SUCCCESS);
        }
        return { isSuccess: true, data: Object.keys(resultMap) };
    }

    __backup(fromToMap, component, member, backupFolderName) {
        this._protectedFoldersSearch(member.destination,
            (protectedPath) => {
                const destObj = {
                    component,
                    member: member.name,
                    to: FSWrapper.combinePath(
                        [
                            FSWrapper.getRootPath(),
                            backupFolderName,
                            component,
                            member.name
                        ])
                };

                PSLogger.log(MESSAGES.messageMap.BACKUP_PATH, destObj);

                const pts = FSWrapper.combinePath(
                    [
                        FSWrapper.getRootPath(),
                        backupFolderName,
                        component,
                        member.name
                    ]);

                if (!FSWrapper.isPathExistSync(pts, '')) {
                    FSWrapper.createDirectorySync(pts, '');
                }

                FSWrapper.copyFolderContentsRecursiveSync(
                    protectedPath,
                    FSWrapper.combinePath([pts, protectedPath.split('\\').pop()]),
                    '',
                    ''
                );
            });
        return { isSuccess: true };
    }

    __restore(fromToMap, component, member, backupFolderName) {
        this._protectedFoldersSearch(member.destination,
            (protectedPath) => {
                const pts = FSWrapper.combinePath(
                    [
                        FSWrapper.getRootPath(),
                        backupFolderName,
                        component,
                        member.name
                    ]);

                if (!FSWrapper.isPathExistSync(pts, '')) {
                    FSWrapper.createDirectorySync(pts, '');
                }

                const destObj = {
                    component,
                    member: member.name,
                    from: FSWrapper.combinePath(
                        [
                            FSWrapper.getRootPath(),
                            backupFolderName,
                            component,
                            member.name
                        ]),
                    to: protectedPath
                };
                PSLogger.log(MESSAGES.messageMap.RESTORE_PATH, destObj);

                FSWrapper.copyFolderContentsRecursiveSync(
                    FSWrapper.combinePath([pts, protectedPath.split('\\').pop()]),
                    protectedPath,
                    '',
                    ''
                );
            });
        return { isSuccess: true };
    }

    //#endregion


    //#region after_actions

    async __afterCheck(fromToMap, actionResult) {
        const isAll = actionResult.find((componentResult) => {
            return componentResult.data.includes(false);
        });
        PSLogger.log(isAll ? MESSAGES.messageMap.CHECK_SOURCES_ERROR : MESSAGES.messageMap.CHECK_SOURCES_SUCCESS);

        return { isSuccess: true, data: null };
    }

    async __afterCopySources(fromToMap) {
        let result = { isSuccess: true, data: null };
        if (!fromToMap.microservice) return result;

        await fromToMap.microservice.reduce(async (promise, microserviceMember) => {
            await promise;
            const scResult = await windowsServiceHelper.queryServiceByName(microserviceMember);

            if (this._isServiceNotRegistered(scResult)) {
                const scRegister = await windowsServiceHelper.registerService(microserviceMember);
                await windowsServiceHelper.changeServiceDescription(microserviceMember);
                if (scRegister.isSuccess) {
                    result = await windowsServiceHelper.startService(microserviceMember);
                }
            }
        }, Promise.resolve());

        await this._restartGateway();
        return result;
    }

    async __afterUpdate(fromToMap) {
        return this.__afterCopySources(fromToMap);
    }

    //#endregion 

    _protectedFoldersSearch(destination, callback) {
        const { PROTECTED_FILES, PROTECTED_FOLDERS } = CONSTANTS;
        const pattern = PROTECTED_FILES.concat(PROTECTED_FOLDERS).join('|').replace(new RegExp('\/.', 'g'), '\/.');

        FSWrapper.recursiveSearch(
            destination,
            new RegExp(pattern),
            ['node_modules'],
            false,
            callback);
    }

    _isServiceNotRegistered(scResult) {
        // 1060 - Service not registered. Only way to check.
        return !scResult.isSuccess && scResult.status === 1060;
    }

    __modifySources(originalSources) {
        const modifiedSource = JSON.parse(JSON.stringify(originalSources));
        const availableList = CONSTANTS.HA_INSTALL_LIST[this._mapArgumentToConstKey[this.parsedArguments.value]];

        if (!availableList) {
            return modifiedSource;
        }

        Object.keys(modifiedSource).forEach((sourceKey) => {
            if (availableList.includes(sourceKey)) {
                return;
            }

            delete modifiedSource[sourceKey];
        });

        return modifiedSource;
    }
}

module.exports = new WindowsInstaller();
