/*
    Парус 8 - Панели мониторинга
    Контекст: Приложение
*/

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

import React, { useReducer, createContext, useEffect, useContext, useCallback, useMemo } from "react"; //ReactJS
import PropTypes from "prop-types"; //Контроль свойств компонента
import { APP_AT, INITIAL_STATE, applicationReducer } from "./application_reducer"; //Редьюсер состояния
import { MessagingCtx } from "./messaging"; //Контекст отображения сообщений
import { BackEndCtx } from "./backend"; //Контекст взаимодействия с сервером

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

//Клиентский API "ПАРУС 8 Онлайн"
const P8O_API = window.top?.parus?.clientApi;

//Структура объекта с описанием ошибок
const APPLICATION_CONTEXT_ERRORS_SHAPE = PropTypes.shape({
    P8O_API_UNAVAILABLE: PropTypes.string.isRequired,
    P8O_API_UNSUPPORTED: PropTypes.string.isRequired
});

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

//Контекст приложения
export const ApplicationCtx = createContext();

//Провайдер контекста приложения
export const ApplicationContext = ({ errors, displaySizeGetter, guidGenerator, config, children }) => {
    //Подключим редьюсер состояния
    const [state, dispatch] = useReducer(applicationReducer, INITIAL_STATE(displaySizeGetter));

    //Подключение к контексту взаимодействия с сервером
    const { getConfig, getRespPayload, executeStored } = useContext(BackEndCtx);

    //Подключение к контексту отображения сообщений
    const { showMsgErr } = useContext(MessagingCtx);

    //Установка флага инициализированности приложения
    const setInitialized = () => dispatch({ type: APP_AT.SET_INITIALIZED });

    //Установка текущего размера экрана
    const setDisplaySize = displaySize => dispatch({ type: APP_AT.SET_DISPLAY_SIZE, payload: displaySize });

    //Установка базового URL приложения
    const setUrlBase = urlBase => dispatch({ type: APP_AT.SET_URL_BASE, payload: urlBase });

    //Установка списка панелей
    const setPanels = (panels, panelsSettings) => dispatch({ type: APP_AT.LOAD_PANELS, payload: { panels, panelsSettings } });

    //Установка заголовка в шапке приложения
    const setAppBarTitle = useCallback(appBarTitle => dispatch({ type: APP_AT.SET_APP_BAR_TITLE, payload: appBarTitle }), []);

    //Установка флага отображения шапки приложения
    const setAppBarShow = useCallback(appBarShow => dispatch({ type: APP_AT.SET_APP_BAR_SHOW, payload: appBarShow }), []);

    //Установка флага отображения настройки параметров в шапке приложения
    const setAppBarSettingsShow = useCallback(
        appBarSettingsShow => dispatch({ type: APP_AT.SET_APP_BAR_SETTINGS_SHOW, payload: appBarSettingsShow }),
        []
    );

    //Поиск раздела по имени
    const findPanelByName = name => state.panels.find(panel => panel.name == name);

    //Отображение закладки "ПАРУС 8 Онлайн" с указанным URL
    const pOnlineShowTab = useCallback(
        ({ id, url, caption, onClose }) => {
            if (P8O_API) {
                const _id = id || guidGenerator();
                P8O_API.ui.openTab({ id: _id, url, caption, onClose: () => (onClose ? onClose(_id) : null) });
                return _id;
            } else showMsgErr(errors.P8O_API_UNAVAILABLE);
        },
        [showMsgErr, guidGenerator, errors.P8O_API_UNAVAILABLE]
    );

    //Отображение раздела "ПАРУС 8 Онлайн"
    const pOnlineShowUnit = useCallback(
        ({ unitCode, showMethod = "main", inputParameters, modal = true }) => {
            if (P8O_API)
                modal
                    ? P8O_API.fn.openDocumentModal({ unitcode: unitCode, method: showMethod, inputParameters })
                    : P8O_API.fn.openDocument
                      ? P8O_API.fn.openDocument({ unitcode: unitCode, method: showMethod, inputParameters })
                      : showMsgErr(errors.P8O_API_UNSUPPORTED);
            else showMsgErr(errors.P8O_API_UNAVAILABLE);
        },
        [showMsgErr, errors.P8O_API_UNAVAILABLE, errors.P8O_API_UNSUPPORTED]
    );

    //Отображение документа "ПАРУС 8 Онлайн"
    const pOnlineShowDocument = useCallback(
        ({ unitCode, document, showMethod = "main", inRnParameter = "in_RN", modal = true }) => {
            if (P8O_API)
                modal
                    ? P8O_API.fn.openDocumentModal({
                          unitcode: unitCode,
                          method: showMethod,
                          inputParameters: [{ name: inRnParameter, value: document }]
                      })
                    : P8O_API.fn.openDocument
                      ? P8O_API.fn.openDocument({
                            unitcode: unitCode,
                            method: showMethod,
                            inputParameters: [{ name: inRnParameter, value: document }]
                        })
                      : showMsgErr(errors.P8O_API_UNSUPPORTED);
            else showMsgErr(errors.P8O_API_UNAVAILABLE);
        },
        [showMsgErr, errors.P8O_API_UNAVAILABLE, errors.P8O_API_UNSUPPORTED]
    );

    //Отображение словаря "ПАРУС 8 Онлайн"
    const pOnlineShowDictionary = useCallback(
        ({ unitCode, showMethod = "main", inputParameters, callBack }) => {
            if (P8O_API)
                P8O_API.fn.openDictionary({ unitcode: unitCode, method: showMethod, inputParameters }, res => (callBack ? callBack(res) : null));
            else showMsgErr(errors.P8O_API_UNAVAILABLE);
        },
        [showMsgErr, errors.P8O_API_UNAVAILABLE]
    );

    //Исполнение пользовательской процедуры "ПАРУС 8 Онлайн"
    const pOnlineUserProcedure = useCallback(
        ({ code, inputParameters, callBack }) => {
            if (P8O_API) P8O_API.fn.performUserProcedureSync({ code, inputParameters }, res => (callBack ? callBack(res) : null));
            else showMsgErr(errors.P8O_API_UNAVAILABLE);
        },
        [showMsgErr, errors.P8O_API_UNAVAILABLE]
    );

    //Исполнение пользовательского отчёта "ПАРУС 8 Онлайн"
    const pOnlineUserReport = useCallback(
        ({ code, inputParameters }) => {
            if (P8O_API) P8O_API.fn.performUserReport({ code, inputParameters });
            else showMsgErr(errors.P8O_API_UNAVAILABLE);
        },
        [showMsgErr, errors.P8O_API_UNAVAILABLE]
    );

    //Получение количества записей на странице
    const configSystemPageSize = useMemo(() => config.SYSTEM.PAGE_SIZE, [config.SYSTEM.PAGE_SIZE]);

    //Получение базового URL приложения
    const configUrlBase = useMemo(() => state.urlBase, [state.urlBase]);

    //Инициализация информации о параметрах панелей
    const initPanelsParams = useCallback(async () => {
        //Инициализируем параметры панелей
        try {
            const res = await executeStored({
                stored: "PKG_P8PANELS_SETTINGS.PANELS_INIT",
                respArg: "COUT",
                loader: false
            });
            return res;
        } catch (e) {
            showMsgErr(`Ошибка инициализации параметров панелей.`, null, e.message);
        }
    }, [executeStored, showMsgErr]);

    //Инициализация приложения
    const initApp = useCallback(async () => {
        //Читаем конфигурацию с сервера
        let res = await getConfig();
        //Сохраняем базовый URL  приложения
        setUrlBase(getRespPayload(res)?.Panels?.urlBase);
        //Инициализируем панели
        const panels = getRespPayload(res)?.Panels?.Panel;
        //Инициализируем информацию о параметрах панелей
        const panelsSettings = await initPanelsParams();
        //Сохраняем список панелей
        setPanels(panels, panelsSettings);
        //Установим флаг завершения инициализации
        setInitialized();
    }, [getConfig, getRespPayload, initPanelsParams]);

    //Обработка подключения контекста к странице
    useEffect(() => {
        if (!state.initialized) {
            //Слушаем изменение размеров окна
            window.addEventListener("resize", () => {
                if (displaySizeGetter) setDisplaySize(displaySizeGetter());
            });
            //Инициализируем приложение
            initApp();
        }
    }, [state.initialized, initApp, displaySizeGetter]);

    //Вернём компонент провайдера
    return (
        <ApplicationCtx.Provider
            value={{
                setAppBarTitle,
                setAppBarShow,
                setAppBarSettingsShow,
                findPanelByName,
                pOnlineShowTab,
                pOnlineShowUnit,
                pOnlineShowDocument,
                pOnlineShowDictionary,
                pOnlineUserProcedure,
                pOnlineUserReport,
                configSystemPageSize,
                configUrlBase,
                appState: state
            }}
        >
            {children}
        </ApplicationCtx.Provider>
    );
};

//Контроль свойств - Провайдер контекста приложения
ApplicationContext.propTypes = {
    errors: APPLICATION_CONTEXT_ERRORS_SHAPE.isRequired,
    displaySizeGetter: PropTypes.func,
    guidGenerator: PropTypes.func.isRequired,
    config: PropTypes.object.isRequired,
    children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node])
};
