const commandName = 'sc.exe';

class WindowsService {
    /**
     * Build command to register Service.
     * @param {CreateParameters} prms 
     * @returns {String[]} Command to execute in form of array, where each element is command argument.
     * Index 0 - application to run (sc.exe).
     * Index 1 - server name.
     * Index 2 - command type(create).
     */
    static create(prms) {
        const { servername } = prms;
        delete prms.servername;

        const prmsStr = WindowsService._buildCommand(prms);

        return [commandName, servername, 'create', ...prmsStr];
    }

    /**
     * Build command to query Service by its name.
     * @param {QueryParameters} prms 
     * @returns {String[]} Command to execute in form of array, where each element is command argument.
     * Index 0 - application to run (sc.exe).
     * Index 1 - server name.
     * Index 2 - command type(query).
     */
    static queryByName(prms) {
        const { servername } = prms;
        delete prms.servername;

        const prmsStr = WindowsService._buildCommand(prms);

        return [commandName, servername, 'query', ...prmsStr];
    }

    /**
     * Build command to query Service by its name.
     * @param {QueryParameters} prms 
     * @returns {String[]} Command to execute in form of array, where each element is command argument.
     * Index 0 - application to run (sc.exe).
     * Index 1 - server name.
     * Index 2 - command type(query).
     */
    static queryConfigurationByName(prms) {
        const { servername } = prms;
        delete prms.servername;

        const prmsStr = WindowsService._buildCommand(prms);

        return [commandName, servername, 'qc', ...prmsStr];
    }

    /**
     * Build command to delete Service.
     * @param {DeleteParameters} prms 
     * @returns {String[]} Command to execute in form of array, where each element is command argument.
     * Index 0 - application to run (sc.exe).
     * Index 1 - server name.
     * Index 2 - command type(delete).
     */
    static delete(prms) {
        const { servername } = prms;
        delete prms.servername;

        const prmsStr = WindowsService._buildCommand(prms);

        return [commandName, servername, 'delete', ...prmsStr];
    }

    /**
     * Build command to start already Service.
     * @param {StartParameters} prms 
     * @returns {String[]} Command to execute in form of array, where each element is command argument.
     * Index 0 - application to run (sc.exe).
     * Index 1 - server name.
     * Index 2 - command type(start).
     */
    static start(prms) {
        const { servername } = prms;
        delete prms.servername;

        const prmsStr = WindowsService._buildCommand(prms);
        return [commandName, servername, 'start', ...prmsStr];
    }

    /**
     * Build command to stop Service.
     * https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/cc742107(v=ws.11)
     * @param {StopParameters} prms 
     * @returns {String[]} Command to execute in form of array, where each element is command argument.
     * Index 0 - application to run (sc.exe).
     * Index 1 - server name.
     * Index 2 - command type(stop).
     */
    static stop(prms) {
        const { servername } = prms;
        delete prms.servername;

        const prmsStr = WindowsService._buildCommand(prms);
        return [commandName, servername, 'stop', ...prmsStr];
    }

    /**
     * Build command to change description of registered Service.
     * https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/cc742069(v=ws.11)
     * @param {DescriptionParameters} prms 
     * @returns {String[]} Command to execute in form of array, where each element is command argument.
     * Index 0 - application to run (sc.exe).
     * Index 1 - server name.
     * Index 2 - command type(description).
     */
    static description(prms) {
        const { servername, servicename, description } = prms;

        return [commandName, servername, 'description', servicename, description];
    }

    static _buildCommand(prms) {
        return Object.keys(prms).map((prmKey) => {
            const value = prms[prmKey];
            if (!value || !value.length) {
                return;
            }

            if (prmKey === 'servicename') {
                return `${value}`;
            }

            return ` ${prmKey}= ${value}`;
        });
    }

    /**
     * Parse windowse service response to object.
     * @param {String} response
     * @returns {Object}
     */
    static responseParser(response) {
        const result = JSON.parse(JSON.stringify(response));
        // console.dir(result)
        if (result.isSuccess) {
            result.data = {};
            const splitedResponse = response.data
                .replace(new RegExp('\r', 'g'), '')
                .split('\n')
                .filter((msgPart) => msgPart.length > 0);

            splitedResponse.forEach((prmt) => {
                if (prmt.indexOf(':') >= 0) {
                    const [key, value] = prmt.split(':');
                    result.data[key.trim()] = value.trim();
                }
            });
        } else {
            // response.error.output - used in execSync.
            let parsedMsg = '';
            if (response.error.output) {
                parsedMsg = response.error.output.toString('utf8');
            } else if (response.error.stdout) {
                const stdout = response.error.stdout;
                parsedMsg = response.error.stdout.message ? response.error.stdout.message : response.error.stdout;
            } else {
                parsedMsg = response.error;
            }

            const clearMsg = parsedMsg
                .split(':')
                .map((part) => {
                    return part.replace(new RegExp('^,|,$'), '').replace(new RegExp('\r|\n', 'g'), ' ').trim();
                })
                .join(': ');
            result.error = new WindowsServiceError(clearMsg, result.error.status ? result.error.status : result.error.code);
        }
        return result;
    }
}

class WindowsServiceError extends Error {
    constructor(msg, status) {
        super(msg)
        this.status = status;
    }
}

module.exports = WindowsService;
