/*
    Парус 8 - Панели мониторинга - Редакторы панелей
    Пользовательские хуки компонента "Источник данных"
*/

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

import { useState, useContext, useEffect, useRef } from "react"; //Классы React
import client from "../../core/client"; //Клиент взаимодействия с сервером приложений
import { ERRORS } from "../../../app.text"; //Общие текстовые ресурсы
import { formatErrorMessage } from "../../core/utils"; //Общие вспомогательные функции
import { BackEndCtx } from "../../context/backend"; //Контекст взаимодействия с сервером
import { P8P_DATA_SOURCE_TYPE, P8P_DATA_SOURCE_ARGUMENT_DATA_TYPE } from "./p8p_data_source_common"; //Общие ресурсы источника данных
import { getConditionsValues } from "./p8p_component_condition/util"; //Вспомогательные ресурсы условий
import { getHandlersByActions } from "./p8p_component_action/util"; //Вспомогательные ресурсы действий
import { ApplicationCtx } from "../../context/application"; //Контекст приложения

//-----------
//Тело модуля
//-----------

//Описание пользовательской процедуры
const useUserProcDesc = ({ code, refresh }) => {
    //Собственное состояние - флаг загрузки
    const [isLoading, setLoading] = useState(false);

    //Собственное состояние - данные
    const [data, setData] = useState(null);

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

    //При необходимости обновить данные компонента
    useEffect(() => {
        //Загрузка данных с сервера
        const loadData = async () => {
            try {
                setLoading(true);
                const data = await executeStored({
                    stored: "PKG_P8PANELS_PE.USERPROCS_DESC",
                    args: { SCODE: code },
                    respArg: "COUT",
                    isArray: name => name === "arguments",
                    loader: false
                });
                setData(data?.XUSERPROC || null);
            } finally {
                setLoading(false);
            }
        };
        //Если надо обновить и есть для чего получать данные
        if (refresh > 0)
            if (code) loadData();
            else setData(null);
    }, [refresh, code, executeStored]);

    //Возвращаем интерфейс хука
    return [data, isLoading];
};

//Получение данных из источника
const useDataSource = ({ dataSource, values, componentRespArg = "" }) => {
    //Контроллер для прерывания запросов
    const abortController = useRef(null);

    //Собственное состояние - параметры исполнения
    const [state, setState] = useState({ stored: null, storedArgs: [], respArg: null, reqSet: false });

    //Собственное состояние - флаг загрузки
    const [isLoading, setLoading] = useState(false);

    //Собственное состояние - данные
    const [data, setData] = useState({ componentData: {}, init: false });

    //Собственное состояние - ошибка получения данных
    const [error, setError] = useState(null);

    //Собственное состояние - наличие настроек
    const [haveConfing, setHaveConfig] = useState(false);

    //Собственное состояние - наличие данных
    const [haveData, setHaveData] = useState(false);

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

    //При необходимости обновление информации о наличии данных
    useEffect(() => {
        setHaveData(data.init === true && !error ? true : false);
    }, [data.init, error]);

    //При необходимости обновить данные
    useEffect(() => {
        //Загрузка данных с сервера
        const loadData = async () => {
            try {
                setLoading(true);
                abortController.current?.abort?.();
                abortController.current = new AbortController();
                const data = await executeStored({
                    stored: state.stored,
                    args: { ...(state.storedArgs ? state.storedArgs : {}) },
                    respArg: state.respArg,
                    loader: false,
                    signal: abortController.current.signal,
                    showErrorMessage: false
                });
                setError(null);
                setData({ componentData: { ...data[componentRespArg] }, init: true });
            } catch (e) {
                if (e.message !== client.ERR_ABORTED) {
                    setError(formatErrorMessage(e.message).text);
                    setData({ componentData: {}, init: false });
                }
            } finally {
                setLoading(false);
            }
        };
        if (state.reqSet) {
            if (state.stored) loadData();
        } else setData({ componentData: {}, init: false });
        return () => abortController.current?.abort?.();
    }, [state.stored, state.storedArgs, state.respArg, state.reqSet, executeStored, componentRespArg]);

    //При изменении свойств
    useEffect(() => {
        //Устанавливаем признак наличия настроек
        setHaveConfig(dataSource?.stored ? true : false);
        //Устанавливаем параметры исполнения
        setState(pv => {
            if (dataSource?.type == P8P_DATA_SOURCE_TYPE.USER_PROC) {
                const { stored, respArg } = dataSource;
                let reqSet = true;
                const storedArgs = {};
                dataSource.arguments.forEach(argument => {
                    let v = argument.valueSource ? values[argument.valueSource] : argument.value;
                    storedArgs[argument.name] =
                        argument.dataType == P8P_DATA_SOURCE_ARGUMENT_DATA_TYPE.NUMB
                            ? isNaN(parseFloat(v))
                                ? null
                                : parseFloat(v)
                            : argument.dataType == P8P_DATA_SOURCE_ARGUMENT_DATA_TYPE.DATE
                              ? new Date(v)
                              : String(v === undefined ? "" : v);
                    if (argument.req === true && [undefined, null, ""].includes(storedArgs[argument.name])) reqSet = false;
                });
                if (pv.stored != stored || pv.respArg != respArg || JSON.stringify(pv.storedArgs) != JSON.stringify(storedArgs)) {
                    if (!reqSet) {
                        setError(ERRORS.DATA_SOURCE_NO_REQ_ARGS);
                        setData({ componentData: {}, init: false });
                    }
                    return { stored, respArg, storedArgs, reqSet };
                } else return pv;
            } else return pv;
        });
    }, [dataSource, values]);

    //Возвращаем интерфейс хука
    return [data.componentData, error, haveConfing, haveData, isLoading];
};

//Изменение данных компонента с учетом условий
const useConditions = ({ componentData, conditions }) => {
    //Собственное состояние - текущие условия компонента
    const [currentConditions, setCurrentConditions] = useState([]);

    //Собственное состояние - данные
    const [data, setData] = useState();

    //При обновлении условий компонента
    useEffect(() => {
        //Если условия изменились
        if (JSON.stringify(currentConditions) != JSON.stringify(conditions)) {
            //Устанавливаем новые условия
            setCurrentConditions(conditions);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [conditions]);

    //При обновлении данных или условий компонента
    useEffect(() => {
        //Если есть текущие условия
        if (currentConditions.length !== 0) {
            //Устанавливаем данные с учетом условий
            setData({ ...componentData, ...getConditionsValues(componentData, currentConditions) });
        } else {
            //Оставляем данные компонента
            setData({ ...componentData });
        }
    }, [currentConditions, componentData]);

    //Возвращаем интерфейс хука
    return [data];
};

//Получение обработчиков компонента
const useComponentHandlers = ({ actions = [], onValuesChange = null, getCustomTypeValue = null }) => {
    //Контроллер для текущего состояния действий
    const currentActions = useRef([]);

    //Собственное состояние - обработчики компонента
    const [handlers, setHandlers] = useState({});

    //Подключение к контексту приложения
    const { configUrlBase, pOnlineShowTab, pOnlineShowUnit } = useContext(ApplicationCtx);

    //При необходимости обновления информации об обработчиках
    useEffect(() => {
        //Если изменились действия или параметры
        if (JSON.stringify(currentActions.current) != JSON.stringify(actions)) {
            //Считываем обработчики компонента
            setHandlers(getHandlersByActions(actions, pOnlineShowUnit, configUrlBase, pOnlineShowTab, onValuesChange, getCustomTypeValue));
            //Устанавливаем контроллер текущих действий
            currentActions.current = actions;
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [actions]);

    //Возвращаем интерфейс хука
    return [handlers];
};

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

export { useUserProcDesc, useDataSource, useConditions, useComponentHandlers };
