"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
const fs = require('fs');
const path = require('path');
const { Schedule } = require('../statistic/src/utils/schedule');
let schedule = null;
const StatEventEmitter = require('events');
const netStat = require('./src/netStat');
const sessionsStat = require('./src/sessions');
class statEmitterClass extends StatEventEmitter {
}
const statEmitter = new statEmitterClass();
class statisticsEntry {
    constructor() {
        this.hoursStart = +new Date();
        this.sended = false;
        this.device = deviceId;
        this.langs = [];
        this.themes = [];
        this.sessions = [];
        this.menu = [];
        this.categories = [];
        this.objects = [];
        this.events = [];
        this.workTime = [];
        this.videophone = [];
        this.takemobile = [];
        this.neuroAd = [];
        this.registrations = [];
        this.searchQueries = [];
    }
}
let initialized = false;
let isStatisticsSent = false;
let isStatisticsCollected = false;
// test address: 94.26.226.9 | https://test.neuro-ad.ru
let address = '';
let port = '';
let idleInterval = 0;
let workStatInterval;
let statisticsEntries = [];
let deviceId = '5bf052030ed4151cde08d0bf';
let place = '5c110f69d1b4de28b0b5f29e';
let currentLogsPath = 'C://logs/statistics';
let sendInterval = 10000;
let playback = false;
let startSchedule = 0;
let endSchedule = 0;
let isNetStatCollected = true;
let stopped = true;
const workTimes = [];
const on = (e, action) => statEmitter.on(e, (data) => action(data));
netStat.onNetStart('error', (error) => {
    statEmitter.emit('error', error);
});
netStat.onNetStart('info', (info) => {
    statEmitter.emit('info', info);
});
sessionsStat.onSession('error', (error) => {
    statEmitter.emit('error', error);
});
sessionsStat.onSession('info', (info) => {
    statEmitter.emit('info', info);
});
/** Инициализация модуля */
const init = ({ device, serverAddress, serverPort, sendStatistics, collectStatistics, logsPath, interval, devicePlace, idleTime, workInterval, collectNetStat, startPeriod, endPeriod, }) => {
    statEmitter.emit('info', 'Инициализация модуля сбора статистики');
    isStatisticsSent = sendStatistics ? sendStatistics : false;
    isStatisticsCollected = collectStatistics ? collectStatistics : false;
    address = serverAddress ? serverAddress : address;
    port = serverPort ? serverPort : port;
    startSchedule = startPeriod ? startPeriod : startSchedule;
    endSchedule = endPeriod ? endPeriod : endSchedule;
    deviceId = device ? device : deviceId;
    // Проверить в каком виде приходят данные с расписания
    place = devicePlace ? devicePlace : place;
    sendInterval = interval ? interval * 1000 : sendInterval;
    currentLogsPath = logsPath ? logsPath : currentLogsPath;
    idleInterval = idleTime ? idleTime : idleInterval;
    workStatInterval = workInterval ? workInterval * 1000 : null;
    isNetStatCollected =
        collectNetStat === undefined ? isNetStatCollected : collectNetStat;
    if (!workInterval) {
        statEmitter.emit('info', 'Не указан интервал отчета о работе');
    }
    if (!serverAddress) {
        statEmitter.emit('info', 'Адрес сервера статистики не указан, данные не будут отправляться');
        port = '';
        isStatisticsSent = false;
    }
    if (isStatisticsSent) {
        sendOld();
    }
    syncStatistics();
    initialized = true;
    statEmitter.emit('info', `Модуль статистики успешно инициализирован с параметрами: адрес сервера: ${address.concat(port ? ':' + port : '')}, сбор статистики включен: ${isStatisticsCollected}`);
    // Передаём во сколько по расписанию, у нас включается/выключается устройство и период
    // Если не передано, то startSchedule с 0:00 текущего дня, а endSchedule в 23:59 текущего дня
    schedule = new Schedule(startSchedule, endSchedule, sendInterval);
    schedule.on('interval', collectAndSend);
    // schedule.on('interval', (data: string) =>
    //     console.log(new Date().toISOString(), data)
    // );
    // schedule.on('schedule-end', () => console.log('Schedule ended'));
};
/** Функция запускает сбор статистики */
const start = () => {
    if (!initialized) {
        statEmitter.emit('error', 'Модуль статистики не инициализирован.');
        return;
    }
    if (!isStatisticsCollected) {
        statEmitter.emit('info', 'Сбор статистики отключен');
        return;
    }
    if (isNetStatCollected) {
        netStat.initNetStart({
            checkInterval: 5000,
            initHost: address + (port ? ':' + port : ''),
        });
        netStat.startNetStart();
    }
    stopped = false;
    //инициализация и запуск модуля сетевой статистики
    reduceWorkTimes();
    checkWork();
    statEmitter.emit('info', 'Модуль статистики запущен');
    schedule.start();
};
/** Останавливает сбор статистики. Прекращает отправку данных на сервер и выключает необходимые модули */
function stop() {
    if (stopped) {
        statEmitter.emit('error', 'Отправка статистики не запущена');
        return;
    }
    stopped = true;
    netStat.stopNetStart();
    statEmitter.emit('info', 'Модуль статистики остановлен');
    schedule.stop();
}
/** Вызывает функции reduceLangs и reduceThemes для уменьшения статистических данных. */
const reduceStatistics = () => {
    reduceLangs();
    reduceThemes();
};
/** Уменьшает данные о языках в статистических записях, группируя их по языкам и подсчитывая количество запросов и сессий для каждого языка. */
const reduceLangs = () => {
    const currentLangs = statisticsEntries[statisticsEntries.length - 1].langs;
    if (!currentLangs) {
        return;
    }
    const tmp = [];
    currentLangs.forEach((lang) => {
        const langEntry = tmp.find((el) => el.name === lang.name);
        if (langEntry) {
            langEntry.queryCount += lang.queryCount;
            langEntry.sessionCount++;
        }
        else {
            tmp.push(lang);
        }
    });
    currentLangs.length = 0;
    statisticsEntries[statisticsEntries.length - 1].langs = [...tmp];
};
/** Уменьшает данные о темах в статистических записях, группируя их по темам и подсчитывая количество запросов и общее время сессий для каждой темы. */
const reduceThemes = () => {
    const currentThemes = statisticsEntries[statisticsEntries.length - 1].themes;
    if (!currentThemes) {
        return;
    }
    const tmp = [];
    currentThemes.forEach((theme) => {
        const langEntry = tmp.find((el) => el.name === theme.name);
        if (langEntry) {
            langEntry.queryCount += theme.queryCount;
            langEntry.sessionTime += theme.sessionTime;
        }
        else {
            tmp.push(theme);
        }
    });
    currentThemes.length = 0;
    currentThemes.push(...tmp);
};
/** Синхронизирует статистические данные с файловой системой, создает новую запись статистики при необходимости. */
const syncStatistics = () => {
    if (!fs.existsSync(currentLogsPath)) {
        fs.mkdirSync(currentLogsPath);
    }
    const fileName = getFullFileName();
    if (!fs.existsSync(fileName)) {
        fs.writeFileSync(fileName, JSON.stringify(statisticsEntries, null, '\t'), 'utf8');
    }
    else {
        let content = fs.readFileSync(fileName, 'utf8');
        if (content.length < 1) {
            content = '[]';
            fs.writeFileSync(fileName, '[]');
        }
        statisticsEntries = JSON.parse(content);
        if (!Array.isArray(statisticsEntries)) {
            statisticsEntries = [statisticsEntries];
        }
    }
    createStatisticsEntry();
};
/** Собирает, уменьшает и отправляет статистические данные. */
const collectAndSend = () => {
    if (!isStatisticsCollected) {
        statEmitter.emit('info', 'Сбор статистики отключен');
        return;
    }
    reduceStatistics();
    collect();
    if (!isStatisticsSent) {
        statEmitter.emit('info', 'Отправка статистики отключена');
        return;
    }
    send(statisticsEntries, getFullFileName(), true);
};
/** Собирает данные о времени пинга и скорости соединения, а также создает новую запись статистики. */
const collect = () => {
    setPing();
    setConnectionSpeeds();
    createStatisticsEntry();
};
/** Отправляет статистические данные на сервер. */
const send = (toSend, fileName, sendAll = false) => __awaiter(void 0, void 0, void 0, function* () {
    // Проверяем, нужно ли отправлять данные
    if (!shouldSendData(toSend, sendAll)) {
        statEmitter.emit('info', 'Нет неотправленной статистики');
        return;
    }
    // Строим адрес сервера
    const serverAddress = buildServerAddress();
    // Строим URL запроса
    const requestURL = buildRequestURL(serverAddress);
    // Итерируем по статистическим данным
    for (let i = 0; i <= toSend.length - 1; i++) {
        // Пропускаем уже отправленные данные
        if (toSend[i].sended) {
            continue;
        }
        let entry = Object.assign({}, toSend[i]);
        // Копируем статистическую запись и очищаем от пустых значений
        let cleanEntry = cleanUpEntry(entry);
        // Проверяем, нужно ли обрабатывать текущую запись
        if (!shouldProcessEntry(cleanEntry, toSend, i, sendAll)) {
            continue;
        }
        // Строим запрос
        const request = buildRequest(cleanEntry);
        try {
            // Отправляем запрос и обрабатываем ответ
            const response = yield fetch(requestURL, request);
            const json = yield response.json();
            handleResponse(json, toSend, i, serverAddress);
        }
        catch (error) {
            // Обрабатываем ошибку отправки
            handleSendError(error, serverAddress);
        }
        // Пауза между отправками
        yield sleep(1000);
    }
    // Сохраняем в файл
    saveToFile(fileName, toSend);
});
/** Проверяет, нужно ли отправлять статистические данные. */
const shouldSendData = (toSend, sendAll) => {
    return toSend.some((e, i) => !e.sended && (i !== toSend.length - 1)) && sendAll;
};
/** Строит адрес сервера. */
const buildServerAddress = () => {
    return `${address}${port ? ':' + port : ''}`;
};
/** Строит URL запроса. */
const buildRequestURL = (serverAddress) => {
    return `${serverAddress}/place/${place}/devices/statistic?device=${deviceId}`;
};
/** Проверяет, нужно ли обрабатывать текущую статистическую запись. */
const shouldProcessEntry = (cleanEntry, toSend, index, sendAll) => {
    if (!cleanEntry.sended && (sendAll || index !== toSend.length - 1)) {
        return true;
    }
    return false;
};
/** Очищает статистическую запись от пустых значений. */
const cleanUpEntry = (entry) => {
    const cleanedEntry = {};
    const cleanEntry = Object.fromEntries(Object.entries(entry).filter(([key, value]) => {
        return ["hoursStart", "sended", "device", "collectInterval"].includes(key) || (Array.isArray(value) && value.length !== 0);
        // return !(Array.isArray(value)) && value.length === 0 
    }));
    // Object.entries(entry).forEach(([key, value]) => {
    //     if (!(Array.isArray(value) || value.length === 0)) {
    //         cleanedEntry[key as keyof statisticsEntry] = value;
    //     }
    // });
    return cleanEntry;
};
/** Строит запрос для отправки на сервер. */
const buildRequest = (cleanEntry) => {
    const _a = Object.assign({}, cleanEntry), { sended } = _a, entryToSend = __rest(_a, ["sended"]);
    return {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify([entryToSend]),
    };
};
/** Сохраняет статистические данные в файл. */
const saveToFile = (fileName, toSend) => {
    fs.writeFileSync(fileName, JSON.stringify(toSend, null, '\t'), 'utf8');
};
/** Обрабатывает ошибку отправки статистических данных. */
const handleSendError = (error, serverAddress) => {
    if (error instanceof Error) {
        statEmitter.emit('error', `Не удалось отправить запись статистики на сервер ${serverAddress}. Error: ${JSON.stringify(error.message, null, '\t')}`);
    }
};
/** Обрабатывает ответ после успешной отправки статистических данных. */
const handleResponse = (json, toSend, index, serverAddress) => {
    var _a;
    if (json === null) {
        return;
    }
    if ((json.saved !== undefined &&
        !!json.saved) ||
        json.details.body.some((el) => el.message == 'statistic.exists')) {
        toSend[index].sended = true;
        statEmitter.emit('info', `Запись статистики от ${new Date(toSend[index].hoursStart)} успешно отправлена на сервер ${serverAddress}`);
        statEmitter.emit('data', JSON.stringify(json, null, '\t'));
    }
    else {
        if ((_a = json === null || json === void 0 ? void 0 : json.details) === null || _a === void 0 ? void 0 : _a.body.every((el) => el.message === 'statistic.exists')) {
            statEmitter.emit('error', json);
            statEmitter.emit('info', `Запись статистики была отправлена на сервер ${serverAddress} ранее`);
            toSend[index].sended = true;
        }
        else {
            statEmitter.emit('error', `Не удалось отправить запись статистики на сервер ${serverAddress}. Error: ${JSON.stringify(json, null, '\t')}`);
        }
    }
};
/** Создает новую запись статистики, если текущая запись устарела. */
const createStatisticsEntry = () => {
    const currentTime = +new Date();
    if (statisticsEntries.length >= 1) {
        const lastEntryIndex = statisticsEntries.length - 1;
        const isCurrentEntry = currentTime - statisticsEntries[lastEntryIndex].hoursStart <=
            sendInterval * 0.9;
        if (statisticsEntries.length > 0 && isCurrentEntry) {
            statEmitter.emit('info', 'Уже есть запись этого часа');
            return;
        }
    }
    const newEntry = new statisticsEntry();
    statisticsEntries.push(newEntry);
    statEmitter.emit('info', 'Новая запись успешно создана');
};
/** Задает данные о времени пинга. */
const setPing = () => {
    setMinMaxMid('ping');
};
/** Задает данные о скорости соединения. */
const setConnectionSpeeds = () => {
    setMinMaxMid('internetSpeed');
};
/** Начинает сессию и запускает проигрывание видео */
const startSession = (props) => {
    playback = true;
    // Проверка наличия запущенной отправки статистики
    if (stopped) {
        statEmitter.emit('error', 'Отправка статистики не запущена');
        return;
    }
    // Начало сессии и передача параметров сессии в sessionsStat
    sessionsStat.startSessionModule({
        startLang: props.startLang,
        startTheme: props.startTheme,
        idleInterval: idleInterval,
    });
};
/** Функция предназначена для завершения сессии и обновления статистики в соответствии с данными завершенной сессии. */
const endSession = () => {
    playback = false;
    const currentEntry = statisticsEntries[statisticsEntries.length - 1];
    const sessionStatistics = sessionsStat.endSessionModule();
    if (!sessionStatistics) {
        statEmitter.emit('info', 'Сессия не была начата');
        return;
    }
    Object.keys(sessionStatistics).forEach((key) => {
        const currentField = currentEntry[key];
        const tmp = sessionStatistics[key];
        if (Array.isArray(currentField)) {
            if (Array.isArray(tmp)) {
                currentField.push(...tmp);
                return;
            }
            else {
                currentField.push(tmp);
            }
        }
    });
    reduceStatistics();
    fs.writeFileSync(getFullFileName(), JSON.stringify(statisticsEntries, null, '\t'), 'utf8');
};
/** Функция из модуля, обрабатывает смену темы в рамках текущей сессии */
const changeTheme = (theme) => sessionsStat.handleThemeChange(theme);
/** Функция из модуля, обрабатывает смену языка в рамках текущей сессии */
const changeLang = (lang) => sessionsStat.handleLangChange(lang);
/** Функция из модуля, увеличивает счетчик запросов пути в рамках текущей сессии */
const addPathQuery = () => sessionsStat.handlePathQuery();
/** Функция из модуля, отмечает начало видеозвонка в рамках текущей сессии */
const startVideophone = () => sessionsStat.handleVideophoneStart();
/** Функция из модуля, завершает видеозвонок в рамках текущей сессии */
const endVideophone = () => sessionsStat.handleVideophoneEnd();
/** Функция из модуля, обрабатывает клики по элементам меню в рамках текущей сессии */
const addMenuClick = (menu) => sessionsStat.handleMenuClick(menu);
/** Функция из модуля, обрабатывает действия пользователя в категориях */
const addCategoryAction = (category, action) => sessionsStat.handleCategoryAction(category, action);
/** Функция из модуля, обрабатывает действия пользователя в событиях */
const addEventAction = (event, action) => sessionsStat.handleEventAction(event, action);
/** Функция из модуля, обрабатывает действия пользователя с приоритетными объектами */
const addObjectAction = (objectName, actionType, objectType) => sessionsStat.handlePriorityObjectAction(objectName, actionType, objectType);
/** Функция из модуля, обрабатывает действия связанные с "Takemobile" */
const addTakemobile = (clientNumber, storeType) => sessionsStat.handleTakemobile(clientNumber, storeType);
/** Функция из модуля, обрабатывает клики на кнопку "Takemobile" */
const addTakemobileClick = () => sessionsStat.handleTakemobileClick();
/** Обрабатывает и обновляет запись статистики о начале проигрывания рекламы */
const handleNeuroAdStart = (name, type) => {
    // Проверка выбранного типа проигрывания видео
    if (!type) {
        statEmitter.emit('error', 'Не выбран тип проигрывания видео');
        return;
    }
    // Получение последней записи статистики
    const currentEntry = statisticsEntries[statisticsEntries.length - 1];
    // Поиск записи о рекламе по названию
    const currentAd = currentEntry.neuroAd &&
        currentEntry.neuroAd.find((el) => el.name === name);
    // Определение типа проигрывания (сессия или простой)
    const isInSession = type === 'session';
    // Изменение типа проигрывания в зависимости от playback
    type = !playback ? 'idle' : 'session';
    // Если записи о рекламе не существует, создаем новую запись
    if (!currentAd && currentEntry.neuroAd) {
        currentEntry.neuroAd.push({
            type: type,
            name: name,
            clickCount: 0,
            playCount: isInSession ? 1 : 0,
            playCountIdle: isInSession ? 0 : 1,
            takeMobileClick: 0,
            takeMobileCount: 0,
            clients: [],
        });
        return;
    }
    // Увеличение счетчика проигрывания в зависимости от типа проигрывания
    if (isInSession) {
        currentAd && currentAd.playCount++;
    }
    else {
        currentAd && currentAd.playCountIdle++;
    }
    // Сохранение обновленных данных
    fs.writeFileSync(getFullFileName(), JSON.stringify(statisticsEntries, null, '\t'), 'utf8');
};
/** Обрабатывает и добавляет информацию о поисковом запросе в запись статистики */
const handleSearchQuery = (query, keywords, result) => {
    // Получение последней записи статистики
    const currentEntry = statisticsEntries[statisticsEntries.length - 1];
    // Получение массива поисковых запросов
    const searchQueries = currentEntry.searchQueries;
    // Добавление информации о поисковом запросе
    searchQueries === null || searchQueries === void 0 ? void 0 : searchQueries.push({ query: query, keywords: keywords, results: result });
};
/** Обрабатывает обновляет запись статистики о кликах на рекламе */
const handleNeuroAdClick = (name, type) => {
    // Получение последней записи статистики
    const currentEntry = statisticsEntries[statisticsEntries.length - 1];
    // Поиск записи о рекламе по названию
    const currentAd = currentEntry.neuroAd &&
        currentEntry.neuroAd.find((el) => el.name === name);
    // Если записи о рекламе не существует, создаем новую запись
    if (!currentAd) {
        currentEntry.neuroAd &&
            currentEntry.neuroAd.push({
                name: name,
                clickCount: type === 'takemobile' ? 0 : 1,
                playCount: 1,
                playCountIdle: 0,
                takeMobileClick: type === 'takemobile' ? 1 : 0,
                takeMobileCount: 0,
                clients: [],
            });
        // Сохранение обновленных данных
        fs.writeFileSync(getFullFileName(), JSON.stringify(statisticsEntries, null, '\t'), 'utf8');
        return;
    }
    // Увеличение счетчика кликов в зависимости от типа клика
    if (type === 'takemobile') {
        currentAd.takeMobileClick++;
    }
    else {
        currentAd.clickCount++;
    }
    // Сохранение обновленных данных
    fs.writeFileSync(getFullFileName(), JSON.stringify(statisticsEntries, null, '\t'), 'utf8');
};
/**
 * Обрабатывает и обновляет запись статистики о рекламе и телефонных кликах.
 * Если запись для указанной рекламы не существует, она создается.
 */
const handleNeuroAdTakemobile = (name, client) => {
    // Получение последней записи статистики
    const currentEntry = statisticsEntries[statisticsEntries.length - 1];
    // Поиск записи о рекламе по названию
    const currentAd = currentEntry.neuroAd &&
        currentEntry.neuroAd.find((el) => el.name === name);
    // Если записи о рекламе не существует, создаем новую запись
    if (!currentAd) {
        currentEntry.neuroAd &&
            currentEntry.neuroAd.push({
                name: name,
                clickCount: 0,
                playCount: 1,
                playCountIdle: 0,
                takeMobileClick: 1,
                takeMobileCount: 1,
                clients: client ? [client] : [],
            });
        fs.writeFileSync(getFullFileName(), JSON.stringify(statisticsEntries, null, '\t'), 'utf8');
        return;
    }
    // Увеличение счетчика кликов на "Takemobile"
    currentAd.takeMobileCount++;
    // Проверка, что клиент еще не был добавлен в список клиентов
    if (currentAd.clients.find((el) => el === client)) {
        return;
    }
    // Добавление клиента в список клиентов
    client && currentAd.clients.push(client);
    // Сохранение обновленных данных
    fs.writeFileSync(getFullFileName(), JSON.stringify(statisticsEntries, null, '\t'), 'utf8');
};
/** Обрабатывает и добавляет информацию о регистрации в запись статистики.*/
const handleFlightRegistration = ({ ticketNumber, flightNumber, status, error }) => {
    // Получение последней записи статистики
    const currentEntry = statisticsEntries[statisticsEntries.length - 1];
    if (!ticketNumber || !flightNumber || !status) {
        statEmitter.emit("error", "Не указан номер рейса, билета или статус");
        return;
    }
    if (!error) {
        error = "";
    }
    // Проверка, что поле "registrations" не существует в записи, и создание его, если необходимо
    if (!currentEntry.registrations) {
        currentEntry.registrations = [];
    }
    // Получение текущей даты и времени
    const now = +new Date();
    // Добавление информации о регистрации в запись статистики
    currentEntry.registrations.push({ time: now, ticketNumber, flightNumber, status, error });
    // Сохранение обновленных данных
    fs.writeFileSync(getFullFileName(), JSON.stringify(statisticsEntries, null, '\t'), 'utf8');
};
const handleMail = ({ email, status, error }) => {
    const currentEntry = statisticsEntries[statisticsEntries.length - 1];
    if (!email || !status) {
        statEmitter.emit("error", "Не указан email или статус");
        return;
    }
    if (!error) {
        error = "";
    }
    if (!currentEntry.mails) {
        currentEntry.mails = [];
    }
    const now = +new Date();
    currentEntry.mails.push({
        email,
        time: now,
        status,
        error,
    });
    fs.writeFileSync(getFullFileName(), JSON.stringify(statisticsEntries, null, '\t'), 'utf8');
};
const handleSms = ({ phoneNumber, status, error }) => {
    const currentEntry = statisticsEntries[statisticsEntries.length - 1];
    if (!phoneNumber || !status) {
        statEmitter.emit("error", "Не указан номер телефона или статус");
        return;
    }
    if (!error) {
        error = "";
    }
    if (!currentEntry.sms) {
        currentEntry.sms = [];
    }
    const now = +new Date();
    currentEntry.sms.push({
        phoneNumber,
        time: now,
        status,
        error,
    });
    fs.writeFileSync(getFullFileName(), JSON.stringify(statisticsEntries, null, '\t'), 'utf8');
};
/** Утилитарная функция для записи минимального, среднего и максимального значения из
массива данных */
const setMinMaxMid = (key) => {
    let values = [];
    let emptyErrorText = '';
    let keyType = '';
    if (key === 'ping') {
        values = netStat.getStats('ping');
        emptyErrorText = 'Нет статистики по пингам';
        keyType = 'Ping';
    }
    if (key === 'internetSpeed') {
        values = netStat.getStats('speed');
        emptyErrorText = 'Нет статистики по скорости';
        keyType = 'Speed';
    }
    if (values.length === 0) {
        statEmitter.emit('error', emptyErrorText);
        return;
    }
    if (keyType === '') {
        statEmitter.emit('error', 'Not yet implemented');
        return;
    }
    if (!statisticsEntries || statisticsEntries.length === 0) {
        statEmitter.emit('error', 'Не создано ни одной записи статистики');
        return;
    }
    const start = values.reduce((a, e) => (e.hoursStart < a ? e.hoursStart : a), Infinity);
    const currentStatisticsEntry = statisticsEntries[statisticsEntries.length - 1];
    currentStatisticsEntry[key] = [];
    const result = currentStatisticsEntry[key];
    result[0] = {
        hoursStart: start,
        name: `max${keyType}`,
        value: Math.max(...values.map((e) => e.value)),
    };
    result[1] = {
        hoursStart: start,
        name: `min${keyType}`,
        value: Math.min(...values.map((e) => e.value)),
    };
    result[2] = {
        hoursStart: start,
        name: `middle${keyType}`,
        value: Math.round(values.map((e) => e.value).reduce((a, b) => a + b, 0) /
            values.length),
    };
    fs.writeFileSync(getFullFileName(), JSON.stringify(statisticsEntries, null, '\t'), 'utf8');
};
// const getSessionStats = () => sessionsStat.getSessionStats();
/** Возвращает полный путь к текущему файлу статистики. */
const getFullFileName = () => {
    const fileName = path.resolve(currentLogsPath, getFileName());
    return fileName;
};
/** Возвращает имя файла статистики в формате "год_месяц_день.stat". */
const getFileName = () => {
    const date = new Date();
    const year = date.getFullYear();
    const month = date.getMonth() + 1 < 10
        ? `0${date.getMonth() + 1}`
        : `${date.getMonth() + 1}`;
    const day = date.getDate() < 10 ? `0${date.getDate()}` : `${date.getDate()}`;
    return `${year}_${month}_${day}.stat`;
};
/** Асинхронно отправляет старые неотправленные файлы статистики. Перемещает отправленные файлы в папку "sentStats". */
const sendOld = () => __awaiter(void 0, void 0, void 0, function* () {
    const backupPath = path.resolve(currentLogsPath, 'sentStats');
    if (!fs.existsSync(backupPath)) {
        fs.mkdirSync(backupPath);
    }
    const files = fs
        .readdirSync(currentLogsPath)
        .filter((file) => file.endsWith('.stat'));
    for (const fileName of files) {
        if (fileName.endsWith('.stat') && fileName !== getFileName()) {
            const oldFileName = path.resolve(currentLogsPath, fileName);
            const entries = JSON.parse(fs.readFileSync(oldFileName));
            if (entries.length < 1) {
                continue;
            }
            if (entries.find((entry) => !entry.sended)) {
                yield send(entries, oldFileName, true);
            }
            else {
                const newFileName = path.resolve(backupPath, fileName);
                fs.rename(oldFileName, newFileName, (err) => {
                    if (err) {
                        throw err;
                    }
                });
            }
        }
        yield sleep(1000);
    }
});
/** Уменьшает интервалы рабочего времени и записывает их в текущую запись статистики. Вызывается каждые 10 минут. */
const reduceWorkTimes = () => {
    const currentEntry = statisticsEntries[statisticsEntries.length - 1];
    if (workTimes.length < 1) {
        setTimeout(reduceWorkTimes, 600000);
        return;
    }
    const newWorkTimeEntry = {
        hoursStart: Math.min(...workTimes.map((e) => e.hoursStart)),
        interval: workTimes.map((e) => e.interval).reduce((a, e) => a + e, 0),
    };
    currentEntry.workTime.push(newWorkTimeEntry);
    workTimes.length = 0;
    const fileName = getFullFileName();
    fs.writeFileSync(fileName, JSON.stringify(statisticsEntries, null, '\t'), 'utf8');
    setTimeout(reduceWorkTimes, 600000);
};
/** Запускает проверку рабочего времени через интервал времени workStatInterval и добавляет соответствующие записи в массив workTimes. */
const checkWork = () => {
    if (workStatInterval) {
        const currentWorkReport = {
            hoursStart: +Date.now(),
            interval: workStatInterval,
        };
        workTimes.push(currentWorkReport);
        setTimeout(checkWork, workStatInterval);
    }
};
/** Завершает учет рабочего времени, уменьшая интервалы и записывая их в текущую запись статистики. */
const finalizeWorkTimes = () => {
    reduceWorkTimes();
    const fileName = getFullFileName();
    fs.writeFileSync(fileName, JSON.stringify(statisticsEntries, null, '\t'), 'utf8');
};
/** Возвращает обещание (Promise), которое резолвится после заданной задержки в миллисекундах. */
const sleep = (ms) => {
    return new Promise((resolve) => setTimeout(resolve, ms));
};
module.exports = {
    on,
    init,
    start,
    stop,
    startSession,
    endSession,
    changeTheme,
    changeLang,
    addPathQuery,
    startVideophone,
    endVideophone,
    addMenuClick,
    addCategoryAction,
    addEventAction,
    addObjectAction,
    addTakemobile,
    addTakemobileClick,
    handleNeuroAdStart,
    handleSearchQuery,
    handleNeuroAdClick,
    handleNeuroAdTakemobile,
    handleFlightRegistration,
    finalizeWorkTimes,
    handleMail,
    handleSms
};
