/**
 * Скрипт генерирующий файлы с патронами для стрельб
 * Запускается командой "npm run ammo"
 * Результатом выполнение является файл cartridges.ammo,
 * который появится в корне проекта
 */

/* eslint no-console: "off" */

const fs = require('fs');
const path = require('path');

const stressConfig = require('../server/config/default/stress');

const sslRootCas = require('../server/node_modules/ssl-root-cas/ssl-root-cas');
const request = require('../server/node_modules/request/index');

const template = require('../server/lib/template');

const LINE_BREAK = '\r\n';

const DIRECTORY_URL = stressConfig.api.directory;

const PAGE_SIZE = 500;

const CURRENT_USER_ID = '1130000023163373';

// Как сгенерировать session_id для стрессового окружения
// Зайти на sas1-1bbad87c2615.qloud-c.yandex.net
// и запустить - curl 'https://adm.yandex-team.ru/api/makesession?uids=1130000023163373&policy=longer'
const SESSION_ID = '3:1493721589.5.0.1493721589317:fNi6G6pAAADEAAIMuAYCKg:3B.101|1130000023163373.-1.2.0:3|' +
    '163004.289592.LH_BO5jPz85MRnQdCTwHls0993M';
const AUTHORIZATION_COOKIE = `Session_id=${SESSION_ID};sessionid2=${SESSION_ID}`;

const DEFAULT_HEADERS = {
    'User-Agent': 'Yandex Tank',
    'x-user-ip': '127.0.0.1',
    'x-forwarded-for': '127.0.0.1',
    'x-forwarded-host': 'portal-stress.ws.yandex.ru',
    Host: 'portal-stress.ws.yandex.ru',
    Cookie: AUTHORIZATION_COOKIE,
};

const AMMO_CONFIG = {
    /**
     * Cartridges for html pages
     */
    dashboardPage: {
        type: 'get',
        path: '/portal/home',
        weight: 1,
    },

    staffPage: {
        type: 'get',
        path: '/portal/staff',
        weight: 1,
    },
    adminPage: {
        type: 'get',
        path: '/portal/admin',
        weight: 1,
    },

    /**
     * Cartridges for api requests
     */
    departmentRoot: {
        type: 'get',
        path: '/portal/api/department?extension=deep',
        weight: 1,
    },
    departmentDeep: {
        type: 'get',
        path: '/portal/api/department?id=${departmentId}&extension=deep',
        weight: 1,
    },
    userDeep: {
        type: 'get',
        path: '/portal/api/user?id=${uid}&extension=deep',
        weight: 1,
    },
    groupList: {
        type: 'get',
        path: '/portal/api/group?extension=deep&type=generic',
        weight: 1,
    },
    groupDeep: {
        type: 'get',
        path: '/portal/api/group?id=${groupId}&extension=deep&type=generic',
        weight: 1,
    },
};

function sendDirectoryApiRequest(type, apiPath, params) {
    return new Promise((resolve, reject) => {
        request[type](`${DIRECTORY_URL}${apiPath}`, params, (error, response, body) => {
            if (error || response.statusCode >= 400) {
                return reject(error || response);
            }

            try {
                body = JSON.parse(body);
            } catch (e) {
                console.log('Parse body error', e, body);
            }

            resolve(body);
        });
    });
}

function formatHeaderName(headerName) {
    return headerName.split('-')
        .map(name => name[0].toUpperCase() + name.slice(1).toLowerCase())
        .join('-');
}

function generateCartridgeString(name, data) {
    const strings = [];
    const headers = data.headers;

    // Первая строка содержит тип запроса, путь и версию протокола
    // GET /users/ HTTP/1.1weight
    strings.push(`${data.type.toUpperCase()} ${data.path} HTTP/1.1`);

    for (const headerName in headers) {
        strings.push(`${formatHeaderName(headerName)}: ${headers[headerName]}`);
    }

    strings.push(LINE_BREAK);
    const requestText = strings.join(LINE_BREAK);

    // добавляем в начало размер запроса и тэг
    strings.unshift(`${Buffer.byteLength(requestText, 'utf8')} ${name}`);

    return strings.join(LINE_BREAK);
}

function generateFileForCartridges(cartridges) {
    const cartridgeStrings = cartridges.map(cartridge => generateCartridgeString(cartridge.name, cartridge));

    fs.writeFileSync(path.join(__dirname, '../cartridges.ammo'), cartridgeStrings.join(''), 'utf8');
    console.log('Success');
}

function getDefaultRequestParams(uid) {
    return {
        headers: {
            'x-uid': uid,
            Authorization: `Token ${process.env.CONNECT_TOKENS__AUTH}`,
            'x-user-ip': '127.0.0.1',
        },
    };
}

function prepareDirectoryData(uid) {
    const params = getDefaultRequestParams(uid);

    // получаем orgId для дальнейших запросов
    const organizationsPromise = sendDirectoryApiRequest('get', '/organizations/', params).then(organizations => {
        params.headers['x-org-id'] = organizations[0].id;

        return organizations;
    })
        .catch(e => console.error(e));

    // запрашиваем список департаментов
    const departmentsPromise = organizationsPromise
        .then(() => sendDirectoryApiRequest('get', `/departments/?per_page=${PAGE_SIZE}`, params))
        .catch(e => console.error(e));

    const usersPromise = organizationsPromise
        .then(() => sendDirectoryApiRequest('get', `/users/?per_page=${PAGE_SIZE}`, params))
        .catch(e => console.error(e));

    const groupsPromise = organizationsPromise
        .then(() => sendDirectoryApiRequest('get', `/groups/?per_page=${PAGE_SIZE}`, params))
        .catch(e => console.error(e));

    return Promise.all([
        organizationsPromise,
        departmentsPromise,
        usersPromise,
        groupsPromise,
    ])
        .then(data => {
            const organizations = data[0];
            const orgId = organizations[0].id;

            const departments = data[1].result;
            const departmentIds = departments.map(department => department.id);

            const users = data[2].result;
            const userIds = users.map(user => user.id);

            const groups = data[3].result;
            const groupIds = groups.map(group => group.id);

            return {
                orgId,
                departmentIds,
                userIds,
                groupIds,
            };
        });
}

function shuffleArray(array) {
    let currentIndex = array.length;
    let temporaryValue;
    let randomIndex;

    while (currentIndex !== 0) {
        randomIndex = Math.floor(Math.random() * currentIndex);
        currentIndex -= 1;

        temporaryValue = array[currentIndex];
        array[currentIndex] = array[randomIndex];
        array[randomIndex] = temporaryValue;
    }

    return array;
}

function getRandomElementFromArray(array) {
    const randomIndex = Math.floor(Math.random() * array.length);

    return array[randomIndex];
}

function getDefaultHeaders(organizationData) {
    const headers = Object.assign({}, DEFAULT_HEADERS);

    if (organizationData && organizationData.orgId) {
        headers['x-org-id'] = organizationData.orgId;
    }

    return headers;
}

function templateCartridge(cartridgeTemplate, data) {
    const cartridge = Object.assign({}, cartridgeTemplate);

    Object.keys(cartridge).forEach(fieldName => {
        if (typeof cartridge[fieldName] === 'string') {
            cartridge[fieldName] = template.build(cartridge[fieldName], data);
        }
    });

    return cartridge;
}

function generateCartridge(cartridgeTemplates) {
    prepareDirectoryData(CURRENT_USER_ID).then(organizationData => {
        const cartridges = cartridgeTemplates.map(cartridgeTemplate => {
            const itemData = {
                uid: getRandomElementFromArray(organizationData.userIds),
                departmentId: getRandomElementFromArray(organizationData.departmentIds),
                groupId: getRandomElementFromArray(organizationData.groupIds),
            };

            const cartridge = templateCartridge(cartridgeTemplate, itemData);

            cartridge.headers = getDefaultHeaders(organizationData);

            return cartridge;
        });

        generateFileForCartridges(cartridges);
    });
}

function generateCartridgesFromConfig(config) {
    let cartridgeTemplates = [];

    Object.keys(config).forEach(fieldName => {
        const weight = config[fieldName].weight;

        cartridgeTemplates = cartridgeTemplates.concat(Array(weight)
            .fill(Object.assign({ name: fieldName }, config[fieldName])));
    });

    generateCartridge(shuffleArray(cartridgeTemplates));
}

function initSslRootCas() {
    const rootCas = sslRootCas.create();

    require('https').globalAgent.options.ca = rootCas;
}

initSslRootCas();

generateCartridgesFromConfig(AMMO_CONFIG);
