/*
    Парус 8 - Панели мониторинга
    Контекст: Взаимодействие с API параметров
*/

//---------------------
//Подключение библиотек
//---------------------

import React, { createContext, useContext, useCallback, useState, useRef } from "react"; //ReactJS
import PropTypes from "prop-types"; //Контроль свойств компонента
import { MessagingCtx } from "./messaging"; //Контекст сообщений
import { P8PSettingsDialog } from "../components/p8p_settings_dialog"; //Диалог отображения параметров
import { useSettingsContext } from "./settings_hooks"; //Вспомогательные хуки параметров

//---------
//Константы
//---------

//Доступные виды параметров
const SETTINGS_KINDS = {
    SYSTEM: 0,
    PANEL: 1,
    USER: 2
};

//Доступные действия параметров
const SETTINGS_ACTIONS = {
    INIT: "INIT",
    GET: "GET",
    PUT: "PUT"
};

//Псевдокод панели для системных параметров
const SETTINGS_SYSTEM_PSEUDO = "__P8_SYSTEM";

//----------------
//Интерфейс модуля
//----------------

//Контекст взаимодействия с API параметров
export const SettingsCtx = createContext();

//Провайдер контекста взаимодействия с API параметров
export const SettingsContext = ({ children }) => {
    //Подключение к контексту сообщений
    const { showMsgErr } = useContext(MessagingCtx);

    //Собственное состояние - действия параметров
    const { putPanelSettings, getPanelSettings, initPanelSettings } = useSettingsContext(showMsgErr);
    //Собственное состояние - отображаемые параметры
    const [settings, setSettings] = useState({ isOpen: false, data: {}, panel: null });
    //Собственное состояние - колбэки для обработки событий
    const settingsCallbacks = useRef({ onOk: null, onCancel: null });

    //Формирование структуры параметров панели
    const buildPanelSettings = (kind, panel, settings) =>
        kind === SETTINGS_KINDS.SYSTEM ? { [SETTINGS_SYSTEM_PSEUDO]: { ...settings } } : { [panel]: { ...settings } };

    //Извлечение настроек из ответа сервера
    const extractSettings = (res, kind, panel) =>
        kind === SETTINGS_KINDS.SYSTEM ? { ...(res[SETTINGS_SYSTEM_PSEUDO] || {}) } : { ...(panel ? res[panel] : res) };

    //Валидация параметров настройки
    const validateSettingsParams = useCallback(
        ({ action, kind, panel }) => {
            if ([SETTINGS_KINDS.PANEL, SETTINGS_KINDS.USER].includes(kind) && !panel) {
                //Формируем текст ошибки
                const errMsg = `Ошибка ${
                    action === SETTINGS_ACTIONS.INIT ? "инициализации" : action === SETTINGS_ACTIONS.PUT ? "добавления" : "действия"
                } параметров. Для панельных/пользовательских параметров необходимо указать панель.`;
                //Отображаем ошибку
                showMsgErr(errMsg);
                throw new Error(errMsg);
            }
        },
        [showMsgErr]
    );

    //Добавление параметров панелей
    const putSettings = useCallback(
        async ({ kind, settings, panel = null, loader = true }) => {
            //Проверка корректности параметров
            validateSettingsParams({ action: SETTINGS_ACTIONS.PUT, kind, panel });
            //Оборачиваем параметры в панель
            const panelSettings = buildPanelSettings(kind, panel, settings);
            //Добавляем параметры панели
            const res = await putPanelSettings({ kind, panelSettings, loader });
            //Возвращаем результат
            return res;
        },
        [putPanelSettings, validateSettingsParams]
    );

    //Считывание параметров
    const getSettings = useCallback(
        async ({ kind, code = null, panel = null, full = false, loader = true }) => {
            //Считываем параметры панели
            const res = await getPanelSettings({ kind, code, panel, full, loader });
            //Возвращаем результат
            return extractSettings(res, kind, panel);
        },
        [getPanelSettings]
    );

    //Инициализация параметров
    const initSettings = useCallback(
        async ({ kind, settings, panel = null, full = false, loader = true }) => {
            //Проверка корректности параметров
            validateSettingsParams({ action: SETTINGS_ACTIONS.INIT, kind, panel });
            //Оборачиваем параметры в панель
            const panelSettings = buildPanelSettings(kind, panel, settings);
            //Добавляем параметры панели
            const res = await initPanelSettings({ kind, panelSettings, full, loader });
            //Возвращаем результат
            return extractSettings(res, kind, panel);
        },
        [initPanelSettings, validateSettingsParams]
    );

    //При нажатии "ОК" диалога параметров
    const handleOk = async newSettings => {
        //Признак наличия изменений параметров
        const isChanged = JSON.stringify(newSettings) !== JSON.stringify(settings.data);
        //Если параметры изменились - обновляем на сервере
        if (isChanged) await putPanelSettings({ kind: SETTINGS_KINDS.USER, panelSettings: newSettings });
        //Если есть колбэк на нажатие "ОК" - вызываем его
        if (settingsCallbacks.current.onOk)
            settingsCallbacks.current.onOk({ settings: settings.panel ? newSettings[settings.panel] : newSettings, isChanged });
        //Сбрасываем отображаемые данные
        setSettings({ isOpen: false, data: {}, panel: null });
    };

    //При нажатии "Отменить" диалога параметров
    const handleClose = () => {
        //Если есть колбэк на нажатие "Отменить" - вызываем его
        if (settingsCallbacks.current.onCancel) settingsCallbacks.current.onCancel();
        //Сбрасываем отображаемые данные
        setSettings({ isOpen: false, data: {}, panel: null });
    };

    //Открытие диалога параметров
    const showSettingsDialog = async ({ panel = null, onOk = null, onCancel = null } = {}) => {
        //Загружаем информацию о параметрах
        const panelSettings = await getPanelSettings({ kind: SETTINGS_KINDS.USER, panel, full: true });
        //Устанавливаем отображаемые данные
        setSettings({ isOpen: true, data: panelSettings, panel });
        //Устанавливаем колбэки
        settingsCallbacks.current = { onOk, onCancel };
    };

    //Вернём компонент провайдера
    return (
        <SettingsCtx.Provider
            value={{
                SETTINGS_KINDS,
                initSettings,
                getSettings,
                putSettings,
                showSettingsDialog
            }}
        >
            {settings.isOpen ? <P8PSettingsDialog settings={settings.data} panel={settings.panel} onOk={handleOk} onClose={handleClose} /> : null}
            {children}
        </SettingsCtx.Provider>
    );
};

//Контроль свойств - Провайдер контекста взаимодействия с API параметров
SettingsContext.propTypes = {
    children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node])
};
