/*
    Парус 8 - Панели мониторинга - УДП - Доски задач
    Пользовательские хуки: Хуки событий
*/

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

import { useState, useContext, useEffect, useCallback } from "react"; //Классы React
import { BackEndCtx } from "../../../context/backend"; //Контекст взаимодействия с сервером
import { object2Base64XML } from "../../../core/utils"; //Вспомогательные функции
import { convertFilterValuesToArray } from "../layouts"; //Вспомогательные функции
import { useDictionary } from "./dict_hooks"; //Состояние открытия разделов

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

//Хук обработки перехода события
const useTasksFunctions = () => {
    //Состояние открытия раздела
    const { handleEventRoutesPointExecutersOpen, handleEventRoutesPointsPassessOpen } = useDictionary();

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

    //Выполнение направления события
    const handleSendExec = useCallback(
        //Выполняем финальное перенаправление события
        async ({ mainArgs, onReload = null }) => {
            await executeStored({
                stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SEND",
                args: { ...mainArgs }
            });
            //Если требуется перезагрузить данные
            onReload ? onReload() : null;
        },
        [executeStored]
    );

    //При направлении события
    const handleSend = useCallback(
        async ({ mainArgs, onReload = null, onNoteOpen = null }) => {
            //Если требуется добавить примечание
            if (onNoteOpen) {
                //Открываем примечание с коллбэком на направление события
                onNoteOpen(async note => {
                    //Выполняем изменение статуса
                    handleSendExec({ mainArgs: { ...mainArgs, SNOTE_HEADER: note.header, SNOTE: note.text }, onReload });
                });
            } else {
                //Выполняем изменение статуса
                handleSendExec({ mainArgs, onReload });
            }
        },
        [handleSendExec]
    );

    //По нажатию действия "Направить"
    const handleTaskSend = useCallback(
        async ({ nEvent, onReload = null, onNoteOpen = null }) => {
            //Выполняем инициализацию параметров
            const firstStep = await executeStored({
                stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_SEND",
                args: {
                    NSTEP: 1,
                    NEVENT: nEvent
                }
            });
            if (firstStep) {
                //Открываем раздел "Маршруты событий (исполнители в точках)" для выбора исполнителя
                handleEventRoutesPointExecutersOpen({
                    inputParameters: [
                        { name: "in_IDENT", value: firstStep.NIDENT },
                        { name: "in_EVENT", value: nEvent },
                        { name: "in_PERSON_CODE", value: firstStep.SSEND_PERSON },
                        { name: "in_USER_NAME", value: firstStep.SSEND_USER_NAME },
                        { name: "in_EVENT_TYPE", value: firstStep.SEVENT_TYPE },
                        { name: "in_EVENT_STAT", value: firstStep.SEVENT_STAT },
                        { name: "in_INIT_PERSON", value: firstStep.SINIT_PERSON },
                        { name: "in_INIT_AUTHNAME", value: firstStep.SINIT_AUTHNAME },
                        { name: "in_CLIENT_CLIENT", value: firstStep.SCLIENT_CLIENT },
                        { name: "in_CLIENT_PERSON", value: firstStep.SCLIENT_PERSON }
                    ],
                    callBack: sendPrms => {
                        //Собираем основные параметры направления события
                        const mainArgs = {
                            NIDENT: firstStep.NIDENT,
                            NSTEP: 2,
                            NEVENT: nEvent,
                            SSEND_CLIENT: sendPrms.outParameters.out_CLIENT_CODE,
                            SSEND_DIVISION: sendPrms.outParameters.out_DIVISION_CODE,
                            SSEND_POST: sendPrms.outParameters.out_POST_CODE,
                            SSEND_PERFORM: sendPrms.outParameters.out_POST_IN_DIV_CODE,
                            SSEND_PERSON: sendPrms.outParameters.out_PERSON_CODE,
                            SSEND_STAFFGRP: sendPrms.outParameters.out_STAFFGRP_CODE,
                            SSEND_USER_GROUP: sendPrms.outParameters.out_USER_GROUP_CODE,
                            SSEND_USER_NAME: sendPrms.outParameters.out_USER_NAME,
                            NSEND_PREDEFINED_EXEC: sendPrms.outParameters.out_PREDEFINED_EXEC,
                            NSEND_PREDEFINED_PROC: sendPrms.outParameters.out_PREDEFINED_PROC
                        };
                        //Перенаправляем событие
                        handleSend({ nEvent, mainArgs, onReload, onNoteOpen });
                    }
                });
            }
        },
        [executeStored, handleEventRoutesPointExecutersOpen, handleSend]
    );

    //Выполнение изменения статуса события
    const handleStateChangeExec = useCallback(
        async ({ mainArgs, onReload = null }) => {
            await executeStored({
                stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
                args: { ...mainArgs }
            });
            //Если требуется перезагрузить данные
            onReload ? onReload() : null;
        },
        [executeStored]
    );

    //При изменении статуса события
    const handleStateChange = useCallback(
        async ({ mainArgs, onReload = null, onNoteOpen = null }) => {
            //Если необходимо добавить примечание
            if (onNoteOpen) {
                //Открываем примечание с коллбэком на изменение статуса
                onNoteOpen(async note => {
                    //Выполняем изменение статуса
                    handleStateChangeExec({ mainArgs: { ...mainArgs, SNOTE_HEADER: note.header, SNOTE: note.text }, onReload });
                });
            } else {
                //Выполняем изменение статуса
                handleStateChangeExec({ mainArgs, onReload });
            }
        },
        [handleStateChangeExec]
    );

    //При выборе исполнителя
    const handleExecuterSelect = useCallback(
        async ({ nEvent, pointInfo, onReload = null, onNoteOpen = null }) => {
            //Если требуется выбрать получателя
            if (pointInfo.NSELECT_EXEC === 1) {
                //Открываем раздел "Маршруты событий (исполнители в точках)" для выбора исполнителя
                handleEventRoutesPointExecutersOpen({
                    inputParameters: [
                        { name: "in_IDENT", value: pointInfo.NIDENT },
                        { name: "in_EVENT", value: nEvent },
                        { name: "in_EVENT_TYPE", value: pointInfo.SEVENT_TYPE },
                        { name: "in_EVENT_STAT", value: pointInfo.SEVENT_STAT },
                        { name: "in_INIT_PERSON", value: pointInfo.SINIT_PERSON },
                        { name: "in_INIT_AUTHNAME", value: pointInfo.SINIT_AUTHNAME },
                        { name: "in_CLIENT_CLIENT", value: pointInfo.SCLIENT_CLIENT },
                        { name: "in_CLIENT_PERSON", value: pointInfo.SCLIENT_PERSON }
                    ],
                    callBack: sendPrms => {
                        const mainArgs = {
                            NIDENT: pointInfo.NIDENT,
                            NSTEP: 4,
                            NEVENT: nEvent,
                            SEVENT_STAT: pointInfo.SEVENT_STAT,
                            SSEND_CLIENT: sendPrms.outParameters.out_CLIENT_CODE,
                            SSEND_DIVISION: sendPrms.outParameters.out_DIVISION_CODE,
                            SSEND_POST: sendPrms.outParameters.out_POST_CODE,
                            SSEND_PERFORM: sendPrms.outParameters.out_POST_IN_DIV_CODE,
                            SSEND_PERSON: sendPrms.outParameters.out_PERSON_CODE,
                            SSEND_STAFFGRP: sendPrms.outParameters.out_STAFFGRP_CODE,
                            SSEND_USER_GROUP: sendPrms.outParameters.out_USER_GROUP_CODE,
                            SSEND_USER_NAME: sendPrms.outParameters.out_USER_NAME,
                            NSEND_PREDEFINED_EXEC: sendPrms.outParameters.out_PREDEFINED_EXEC,
                            NSEND_PREDEFINED_PROC: sendPrms.outParameters.out_PREDEFINED_PROC
                        };
                        //Выполняем изменение статуса
                        handleStateChange({ mainArgs, onReload, onNoteOpen });
                    }
                });
            } else {
                //Общие аргументы
                const mainArgs = {
                    NIDENT: pointInfo.NIDENT,
                    NSTEP: 4,
                    NEVENT: nEvent,
                    SEVENT_STAT: pointInfo.SEVENT_STAT
                };
                //Выполняем изменение статуса
                handleStateChange({ mainArgs, onReload, onNoteOpen });
            }
        },
        [handleEventRoutesPointExecutersOpen, handleStateChange]
    );

    //При выполнении третьего шага
    const handleMakeThirdStep = useCallback(
        async ({ nEvent, pointInfo, onReload = null, onNoteOpen = null }) => {
            //Выполняем переход на следующий шаг
            await executeStored({
                stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
                args: {
                    NIDENT: pointInfo.NIDENT,
                    NSTEP: 3,
                    NPASS: pointInfo.NPASS
                }
            });
            //Выполняем выбор исполнителя
            handleExecuterSelect({
                nEvent,
                pointInfo,
                onReload,
                onNoteOpen
            });
        },
        [executeStored, handleExecuterSelect]
    );

    //При выполнении второго шага
    const handleMakeSecondStep = useCallback(
        async ({ nEvent, pointInfo, onReload = null, onNoteOpen = null }) => {
            //Состояние параметров текущего действия
            let currentPointInfo = { ...pointInfo };
            //Выполняем переход на следующий шаг
            const secondStep = await executeStored({
                stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
                args: {
                    NIDENT: currentPointInfo.NIDENT,
                    NSTEP: 2,
                    NPASS: currentPointInfo.NPASS
                }
            });
            //Устанавливаем признак необходимости выбора исполнителя
            currentPointInfo.NSELECT_EXEC = secondStep.NSELECT_EXEC;
            //Выполняем третий шаг
            handleMakeThirdStep({ nEvent, pointInfo: currentPointInfo, onReload, onNoteOpen });
        },
        [executeStored, handleMakeThirdStep]
    );

    //При выборе следующей точки события
    const handleNextPointSelect = useCallback(
        ({ nEvent, pointInfo, onReload = null, onNoteOpen = null }) => {
            //Состояние параметров текущего действия
            let currentPointInfo = { ...pointInfo };
            //Открываем раздел "Маршруты событий (точки перехода)" для выбора следующей точки
            handleEventRoutesPointsPassessOpen({
                sEventType: currentPointInfo.SEVENT_TYPE,
                sEventStatus: currentPointInfo.SEVENT_STAT,
                nPoint: currentPointInfo.NPOINT,
                callBack: async point => {
                    //Устанавливаем полученную точку перехода
                    currentPointInfo.NPASS = point.outParameters.out_RN;
                    currentPointInfo.SEVENT_STAT = point.outParameters.out_NEXT_POINT;
                    //Выполняем второй шаг
                    handleMakeSecondStep({ nEvent, pointInfo: currentPointInfo, onReload, onNoteOpen });
                }
            });
        },
        [handleEventRoutesPointsPassessOpen, handleMakeSecondStep]
    );

    //По нажатию действия "Перейти"
    const handleTaskStateChange = useCallback(
        async ({ nEvent, sNextStat = null, onReload = null, onNoteOpen = null }) => {
            //Выполняем инициализацию параметров
            const eventInfo = await executeStored({
                stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_STATE_CHANGE",
                args: {
                    NSTEP: 1,
                    NEVENT: nEvent,
                    SNEXT_STAT: sNextStat
                }
            });
            //Если информация о события проинициализирована
            if (eventInfo) {
                //Если следующий статус неопределен
                if (!sNextStat) {
                    //Выполнение перехода с выбором точки
                    handleNextPointSelect({
                        nEvent,
                        pointInfo: eventInfo,
                        onReload,
                        onNoteOpen
                    });
                } else {
                    //Выполняем второй шаг
                    handleMakeSecondStep({ nEvent, pointInfo: eventInfo, onReload, onNoteOpen });
                }
            }
        },
        [executeStored, handleMakeSecondStep, handleNextPointSelect]
    );

    return { handleTaskStateChange, handleTaskSend };
};

//Хук получения событий
const useTasks = (filterValues, ordersValues) => {
    //Состояние событий
    const [tasks, setTasks] = useState({
        loaded: false,
        rows: [],
        reload: false,
        accountsReload: false,
        loadedAccounts: []
    });

    //Состояние вспомогательных функций событий
    const { handleTaskStateChange } = useTasksFunctions();

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

    //Инициализация параметров события
    const initTask = (id, task, avatar = null) => {
        //Фильтруем доп. свойства
        let newDocProps = Object.keys(task)
            .filter(key => key.includes("DP_"))
            .reduce((prev, key) => ({ ...prev, [key]: task[key] }), {});
        //Возвращаем структуру события
        return {
            id: id,
            avatar: avatar,
            name: task.SPREF_NUMB,
            nRn: task.NRN,
            sCrn: "",
            nCrn: task.NCRN,
            nClosed: task.NCLOSED,
            sPrefix: task.SEVPREF,
            sNumber: task.SEVNUMB,
            sType: task.SEVTYPE_CODE,
            sStatus: task.SEVSTAT_NAME,
            sDescription: task.SEVDESCR,
            sClntClients: "",
            sClntClnperson: "",
            dchange_date: task.DCHANGE_DATE,
            dStartDate: task.DREG_DATE,
            dExpireDate: task.DEXPIRE_DATE,
            dPlanDate: task.DPLAN_DATE,
            sInitClnperson: task.SINIT_PERSON,
            sInitUser: "",
            sInitReason: "",
            sToCompany: "",
            sToDepartment: task.SSEND_DIVISION,
            sToClnpost: "",
            sToClnpsdep: "",
            sToClnperson: task.SSEND_PERSON,
            sToFcstaffgrp: "",
            sToUser: "",
            sToUsergrp: task.SSEND_USRGRP,
            sSender: task.SSENDER,
            sCurrentUser: "",
            sLinkedUnit: task.SLINKED_UNIT,
            nLinkedRn: task.NLINKED_RN,
            docProps: newDocProps
        };
    };

    //Взаимодействие с событием (через перенос)
    const onDragEnd = useCallback(
        ({ path, eventPoints, openNoteDialog, destCode }) => {
            //Определяем нужные параметры
            const { source, destination } = path;
            //Если путь не указан
            if (!destination) {
                return;
            }
            //Если происходит изменение статуса
            if (destination.droppableId !== source.droppableId) {
                //Конвертим ID переносимого события
                let nDraggableTaskId = parseInt(path.draggableId);
                //Считываем строку, у которой изменяется статус
                let task = tasks.rows.find(r => r.id === nDraggableTaskId);
                //Изменяем статус у события
                task.statusId = parseInt(path.destination.droppableId);
                //Получение настройки точки назначения
                const pointSettings = eventPoints.find(eventPoint => eventPoint.SEVPOINT === destCode);
                //Изменяем статус события с добавлением примечания
                handleTaskStateChange({
                    nEvent: task.nRn,
                    sNextStat: destCode,
                    onReload: () => setTasks(pv => ({ ...pv, reload: true, accountsReload: true })),
                    onNoteOpen: pointSettings.ADDNOTE_ONCHST ? openNoteDialog : null
                });
            }
        },
        [handleTaskStateChange, tasks.rows]
    );

    //При необходимости перезагрузки данных
    useEffect(() => {
        //Считывание данных с учетом фильтрации
        let getTasks = async () => {
            const data = await executeStored({
                stored: "PKG_P8PANELS_CLNTTSKBRD.CLNEVENTS_LOAD",
                args: {
                    CFILTERS: {
                        VALUE: object2Base64XML(convertFilterValuesToArray(filterValues), { arrayNodeName: "filters" }),
                        SDATA_TYPE: SERV_DATA_TYPE_CLOB
                    },
                    CORDERS: { VALUE: object2Base64XML(ordersValues, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
                    NINCLUDE_ACCOUNTS: tasks.accountsReload ? 1 : 0
                },
                isArray: name => name === "XAGENTS",
                respArg: "COUT"
            });
            //Считываем информацию о событиях
            let events = data.XCLNEVENTS.XDATA.XDATA_GRID;
            //Считываем иноформацию о контрагентах
            let accounts = tasks.accountsReload ? [...(data.XAGENTS_WITH_IMG.XAGENTS || [])] : tasks.loadedAccounts;
            //Инициализируем события
            let newRows = [];
            //Если есть события
            if (events.rows) {
                //Формируем структуру событий
                newRows = [...(events.rows || [])].reduce(
                    (prev, cur) => [...prev, initTask(prev.length, cur, accounts.find(agent => agent.SAGNABBR === cur.SSENDER)?.BIMAGE)],
                    []
                );
            }
            //Возвращаем информацию
            return { rows: [...newRows], loadedAccounts: accounts };
        };
        //Считывание данных
        let getData = async () => {
            //Считываем информацию о задачах
            let eventTasks = await getTasks();
            //Загружаем данные
            setTasks(pv => ({
                ...pv,
                loaded: true,
                rows: eventTasks.rows,
                loadedAccounts: eventTasks.loadedAccounts,
                reload: false,
                accountsReload: false
            }));
        };
        //Если необходимо загрузить данные и указан тип событий и загружены все необходимые вспомогательные данные
        if (tasks.reload) {
            //Загружаем данные
            getData();
        }
    }, [SERV_DATA_TYPE_CLOB, executeStored, filterValues, ordersValues, tasks.accountsReload, tasks.loadedAccounts, tasks.reload]);

    return [tasks, setTasks, onDragEnd];
};

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

export { useTasksFunctions, useTasks };
