/*
    Парус 8 - Панели мониторинга - ПУП - Работы проектов
    Панель мониторинга: Корневая панель работ проектов
*/

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

import React, { useContext, useState, useCallback, useEffect } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import {
    Drawer,
    Fab,
    Box,
    Grid,
    List,
    ListItemButton,
    ListItemText,
    ListItemIcon,
    Icon,
    Typography,
    Divider,
    ListItem,
    Button,
    Dialog,
    DialogContent,
    DialogActions,
    TextField,
    DialogTitle
} from "@mui/material"; //Интерфейсные элементы
import { BackEndCtx } from "../../context/backend"; //Контекст взаимодействия с сервером
import { MessagingCtx } from "../../context/messaging"; //Контекст сообщений
import { ApplicationCtx } from "../../context/application"; //Контекст приложения
import { formatDateJSONDateOnly } from "../../core/utils"; //Вспомогательные функции
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Заголовок страницы
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
import { P8P_GANTT_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
import { P8PGantt } from "../../components/p8p_gantt"; //Диаграмма Ганта
import { formatDateRF } from "../../core/utils"; //Вспомогательные функции
import { BUTTONS } from "../../../app.text"; //Текстовые ресурсы и константы
import { ResMon } from "./res_mon"; //Монитор ресурсов
import { taskAttributeRenderer } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов

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

//Стили
const STYLES = {
    PROJECTS_LIST_ITEM_NOJOBS: { backgroundColor: "#ff000045" },
    PROJECTS_LIST_ITEM_PRIMARY: { wordWrap: "break-word" },
    PROJECTS_LIST_ITEM_SECONDARY: { wordWrap: "break-word", fontSize: "0.5rem", textTransform: "uppercase" },
    PROJECTS_LIST_ITEM_SECONDARY_NOJOBS: { color: "red" },
    PROJECTS_LIST_ITEM_SECONDARY_NOEDIT: { color: "gray" },
    PROJECTS_LIST_ITEM_SECONDARY_CHANGED: { color: "green" },
    PROJECTS_BUTTON: { position: "absolute", top: `calc(${APP_BAR_HEIGHT} + 16px)`, left: "16px" },
    PROJECTS_DRAWER: { width: "250px", flexShrink: 0, [`& .MuiDrawer-paper`]: { width: "250px", boxSizing: "border-box", ...APP_STYLES.SCROLL } },
    GANTT_CONTAINER: { height: `calc(100vh - ${APP_BAR_HEIGHT})`, width: "100vw", paddingTop: "24px" },
    GANTT_TITLE: { paddingLeft: "150px", paddingRight: "150px" },
    PERIODS_BUTTON: { position: "absolute", top: `calc(${APP_BAR_HEIGHT} + 16px)`, right: "16px" },
    PERIODS_DRAWER: { width: "1200px", flexShrink: 0, [`& .MuiDrawer-paper`]: { width: "1200px", boxSizing: "border-box", ...APP_STYLES.SCROLL } }
};

//------------------------------------
//Вспомогательные функции и компоненты
//------------------------------------

//Диалог параметров инициализации панели
const InitPrmsDialog = ({ dateBegin, dateFact, onOk, onCancel }) => {
    //Собственное состояние - значения с-по
    const [values, setValues] = useState({ dateBegin: formatDateJSONDateOnly(dateBegin), dateFact: formatDateJSONDateOnly(dateFact) });

    //Отработка воода значения в фильтр
    const handleValueTextFieldChanged = e => setValues(prev => ({ ...prev, [e.target.name]: e.target.value }));

    //Генерация содержимого
    return (
        <Dialog
            open={true}
            aria-labelledby="init-dialog-title"
            aria-describedby="init-dialog-description"
            onClose={() => (onCancel ? onCancel() : null)}
        >
            <DialogTitle>Параметры инициализации</DialogTitle>
            <DialogContent>
                <TextField
                    style={{ padding: "10px" }}
                    name="dateBegin"
                    type="date"
                    value={values.dateBegin}
                    onChange={handleValueTextFieldChanged}
                    label="Начало (будет использован первый день месяца)"
                    variant="standard"
                    fullWidth
                />
                <TextField
                    style={{ padding: "10px" }}
                    name="dateFact"
                    type="date"
                    value={values.dateFact}
                    onChange={handleValueTextFieldChanged}
                    label="Факт на (будет использован последний день месяца)"
                    variant="standard"
                    fullWidth
                />
            </DialogContent>
            <DialogActions>
                <Button onClick={() => (onOk ? onOk({ dateBegin: new Date(values.dateBegin), dateFact: new Date(values.dateFact) }) : null)}>
                    {BUTTONS.OK}
                </Button>
                <Button onClick={() => (onCancel ? onCancel() : null)}>{BUTTONS.CANCEL}</Button>
            </DialogActions>
        </Dialog>
    );
};

//Контроль свойств - Диалог параметров инициализации панели
InitPrmsDialog.propTypes = {
    dateBegin: PropTypes.instanceOf(Date).isRequired,
    dateFact: PropTypes.instanceOf(Date).isRequired,
    onOk: PropTypes.func,
    onCancel: PropTypes.func
};

//Область параметров инициализации панели
const InitPrmsArea = ({ dateBegin, dateFact, durationMeasCode, labMeasCode, onClick }) => {
    return (
        <List>
            <ListItem>
                <ListItemText
                    secondary={
                        <>
                            <b>Начало: </b>
                            {formatDateRF(dateBegin)}
                            <br />
                            <b>Факт на: </b>
                            {formatDateRF(dateFact)}
                            <br />
                            <b>Длительность: </b>
                            {durationMeasCode}
                            <br />
                            <b>Трудоёмкость: </b>
                            {labMeasCode}
                        </>
                    }
                />
            </ListItem>
            <ListItem>
                <Button fullWidth variant="contained" startIcon={<Icon>refresh</Icon>} onClick={onClick ? onClick : null}>
                    Переформировать...
                </Button>
            </ListItem>
        </List>
    );
};

//Контроль свойств - Область параметров инициализации панели
InitPrmsArea.propTypes = {
    dateBegin: PropTypes.instanceOf(Date),
    dateFact: PropTypes.instanceOf(Date),
    durationMeasCode: PropTypes.string,
    labMeasCode: PropTypes.string,
    onClick: PropTypes.func
};

//Область сохранения изменений
const SaveChangesArea = ({ onClick }) => {
    return (
        <List>
            <ListItem>
                <Button fullWidth color="warning" variant="contained" startIcon={<Icon>save</Icon>} onClick={onClick}>
                    Сохранить
                </Button>
            </ListItem>
        </List>
    );
};

//Контроль свойств - Область сохранения изменений
SaveChangesArea.propTypes = {
    onClick: PropTypes.func
};

//Список проектов
const ProjectsList = ({ projects = [], selectedProject, onClick } = {}) => {
    //Подключение к контексту сообщений
    const { InlineMsgErr } = useContext(MessagingCtx);

    //Генерация содержимого
    return projects.length > 0 ? (
        <List>
            {projects.map(p => (
                <ListItemButton
                    key={p.NRN}
                    sx={p.NJOBS == 0 ? STYLES.PROJECTS_LIST_ITEM_NOJOBS : null}
                    selected={p.NRN === selectedProject}
                    onClick={() => (onClick ? onClick(p) : null)}
                >
                    <ListItemIcon>
                        <Icon title={p.NEDITABLE == 1 ? "Можно редактировать" : "Редактирование недоступно"}>
                            {p.NEDITABLE == 1 ? "edit" : "edit_off"}
                        </Icon>
                    </ListItemIcon>
                    <ListItemText
                        primary={<Typography sx={STYLES.PROJECTS_LIST_ITEM_PRIMARY}>{p.SNAME}</Typography>}
                        secondary={
                            <Typography
                                sx={{
                                    ...STYLES.PROJECTS_LIST_ITEM_SECONDARY,
                                    ...(p.NJOBS == 0
                                        ? STYLES.PROJECTS_LIST_ITEM_SECONDARY_NOJOBS
                                        : p.NCHANGED == 1
                                          ? STYLES.PROJECTS_LIST_ITEM_SECONDARY_CHANGED
                                          : STYLES.PROJECTS_LIST_ITEM_SECONDARY_NOEDIT)
                                }}
                            >
                                {p.NJOBS == 1
                                    ? p.NEDITABLE == 1
                                        ? p.NCHANGED == 1
                                            ? "Изменён"
                                            : "Не изменён"
                                        : "Редактирование недоступно"
                                    : "Работы не определены"}
                            </Typography>
                        }
                    />
                </ListItemButton>
            ))}
        </List>
    ) : (
        <InlineMsgErr okBtn={false} text={"Нет доступных проектов"} />
    );
};

//Контроль свойств - Список проектов
ProjectsList.propTypes = {
    projects: PropTypes.array,
    selectedProject: PropTypes.number,
    onClick: PropTypes.func
};

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

//Корневая панель работ проектов
const PrjJobs = () => {
    //Собственное состояние
    let [state, setState] = useState({
        needSave: false,
        showProjectsList: false,
        showPeriodsList: false,
        init: false,
        dateBegin: null,
        dateFact: null,
        durationMeas: null,
        durationMeasCode: null,
        labMeas: null,
        labMeasCode: null,
        resourceStatus: null,
        ident: null,
        projects: [],
        projectsLoaded: false,
        selectedProjectJobsLoaded: false,
        selectedProject: null,
        selectedProjectDocRn: null,
        gantt: {},
        showInitDialog: false
    });

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

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

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

    //Загрузка списка проектов
    const loadProjects = useCallback(
        async (force = false) => {
            if (!state.projectsLoaded || force) {
                const data = await executeStored({
                    stored: "PKG_P8PANELS_PROJECTS.JB_PRJCTS_LIST",
                    args: { NIDENT: state.ident },
                    respArg: "COUT",
                    isArray: name => name === "XPROJECTS"
                });
                setState(pv => ({ ...pv, projectsLoaded: true, projects: [...(data?.XPROJECTS || [])] }));
            }
        },
        [executeStored, state.ident, state.projectsLoaded]
    );

    //Загрузка списка работ проекта
    const loadProjectJobs = useCallback(
        async (tasksOnly = false) => {
            const data = await executeStored({
                stored: "PKG_P8PANELS_PROJECTS.JB_JOBS_LIST",
                args: { NIDENT: state.ident, NPRN: state.selectedProject, NINCLUDE_DEF: tasksOnly === false ? 1 : 0 },
                attributeValueProcessor: (name, val) =>
                    name == "numb" ? undefined : ["start", "end"].includes(name) ? formatDateJSONDateOnly(val) : val,
                respArg: "COUT"
            });
            setState(pv => ({
                ...pv,
                selectedProjectJobsLoaded: true,
                gantt: {
                    ...(tasksOnly === true ? { ...pv.gantt, tasks: [...data.XGANTT.tasks] } : data.XGANTT ? { ...data.XGANTT } : {})
                }
            }));
        },
        [executeStored, state.ident, state.selectedProject]
    );

    //Изменение работы в графике
    const modifyJob = useCallback(
        async (job, dateFrom, dateTo) => {
            let data = null;
            try {
                data = await executeStored({
                    stored: "PKG_P8PANELS_PROJECTS.JB_JOBS_MODIFY_PERIOD",
                    args: { NJB_JOBS: job, DDATE_FROM: dateFrom, DDATE_TO: dateTo }
                });
                if (data?.NRESOURCE_STATUS != -1) {
                    setState(pv => ({ ...pv, resourceStatus: data.NRESOURCE_STATUS, needSave: true }));
                    loadProjects(true);
                }
            } finally {
                loadProjectJobs(true);
            }
        },
        [executeStored, loadProjectJobs, loadProjects]
    );

    //Сохранение буфера балансировки в проекты
    const saveProjects = useCallback(async () => {
        const data = await executeStored({
            stored: "PKG_P8PANELS_PROJECTS.JB_SAVE",
            args: { NIDENT: state.ident },
            respArg: "COUT"
        });
        setState(pv => ({ ...pv, needSave: false, projects: [...(data?.XPROJECTS || [])] }));
    }, [executeStored, state.ident]);

    //Инициализация данных балансировки
    const initJobs = useCallback(async () => {
        if (!state.init) {
            const data = await executeStored({
                stored: "PKG_P8PANELS_PROJECTS.JB_INIT",
                args: {
                    DBEGIN: state.dateBegin ? state.dateBegin : null,
                    DFACT: state.dateFact ? state.dateFact : null,
                    NIDENT: state.ident
                }
            });
            setState(pv => ({
                ...pv,
                init: true,
                reInit: false,
                dateBegin: new Date(data.DBEGIN),
                dateFact: new Date(data.DFACT),
                durationMeas: data.NDURATION_MEAS,
                durationMeasCode: data.SDURATION_MEAS,
                labMeas: data.NLAB_MEAS,
                labMeasCode: data.SLAB_MEAS,
                resourceStatus: data.NRESOURCE_STATUS,
                ident: data.NIDENT
            }));
        }
    }, [state.init, state.dateBegin, state.dateFact, state.ident, executeStored]);

    //Грузим список проектов при смене идентификатора процесса
    useEffect(() => {
        if (state.ident) loadProjects();
    }, [state.ident, loadProjects]);

    //При смене выбранного проекта
    useEffect(() => {
        if (state.selectedProject) loadProjectJobs(false);
    }, [state.selectedProject, loadProjectJobs]);

    //При изменении флага инициализации
    useEffect(() => {
        initJobs();
    }, [state.init, initJobs]);

    //Выбор проекта
    const selectPoject = (project, projectDocRn) => {
        setState(pv => ({
            ...pv,
            selectedProject: project,
            selectedProjectDocRn: projectDocRn,
            selectedProjectJobsLoaded: false,
            gantt: {},
            showProjectsList: false
        }));
    };

    //Сброс выбора проекта
    const unselectProject = () =>
        setState(pv => ({
            ...pv,
            selectedProjectJobsLoaded: false,
            selectedProject: null,
            selectedProjectDocRn: null,
            gantt: {},
            showProjectsList: false
        }));

    //Обработка нажатия на элемент в списке проектов
    const handleProjectClick = project => {
        if (state.selectedProject != project.NRN) selectPoject(project.NRN, project.NPROJECT);
        else unselectProject();
    };

    //Отработка нажатия на заголовок плана-графика
    const handleTitleClick = () =>
        state.selectedProjectDocRn ? pOnlineShowDocument({ unitCode: "Projects", document: state.selectedProjectDocRn }) : null;

    //Обработка измненения сроков задачи в диаграмме Гантта
    const handleTaskDatesChange = ({ task, start, end, isMain }) => {
        if (isMain) modifyJob(task.rn, new Date(start), new Date(end));
    };

    //Отработка нажатия на отображения диалога параметров инициализации панели
    const handleShowInitDialogClick = () => setState(pv => ({ ...pv, showInitDialog: true }));

    //Отработка нажатия на "ОК" в диалоге параметров инициализации панели
    const handleOKInitDialogClick = values =>
        setState(pv => ({ ...pv, dateBegin: values.dateBegin, dateFact: values.dateFact, showInitDialog: false, init: false }));

    //Отработка нажатия на "Отмена" в диалоге параметров инициализации панели
    const handleCancelInitDialogClick = () => setState(pv => ({ ...pv, showInitDialog: false }));

    //Обработка нажатия на сохранение данных в проект
    const handleSaveToProjectsClick = () => saveProjects();

    //Обработка нажатия на проект в таблице детализации трудоёмкости по плану-графику монитора ресурсов
    const handlePlanJobsDtlProjectClick = ({ sender }) => {
        setState(pv => ({ ...pv, showPeriodsList: false }));
        if (state.selectedProject != sender.NJB_PRJCTS) selectPoject(sender.NJB_PRJCTS, sender.NPROJECT);
    };

    //Генерация содержимого
    return (
        <Box>
            {state.showInitDialog ? (
                <InitPrmsDialog
                    dateBegin={state.dateBegin}
                    dateFact={state.dateFact}
                    onOk={handleOKInitDialogClick}
                    onCancel={handleCancelInitDialogClick}
                />
            ) : null}
            <Fab variant="extended" sx={STYLES.PROJECTS_BUTTON} onClick={() => setState(pv => ({ ...pv, showProjectsList: !pv.showProjectsList }))}>
                Проекты
                {state.needSave ? (
                    <>
                        &nbsp;&nbsp;
                        <Icon sx={{ color: "orange" }}>save</Icon>
                    </>
                ) : null}
            </Fab>
            <Drawer
                anchor={"left"}
                open={state.showProjectsList}
                onClose={() => setState(pv => ({ ...pv, showProjectsList: false }))}
                sx={STYLES.PROJECTS_DRAWER}
            >
                {state.projectsLoaded ? (
                    <>
                        <InitPrmsArea
                            dateBegin={state.dateBegin}
                            dateFact={state.dateFact}
                            durationMeasCode={state.durationMeasCode}
                            labMeasCode={state.labMeasCode}
                            onClick={handleShowInitDialogClick}
                        />
                        <Divider />
                        {state.needSave ? (
                            <>
                                <SaveChangesArea onClick={handleSaveToProjectsClick} />
                                <Divider />
                            </>
                        ) : null}
                        <ProjectsList projects={state.projects} selectedProject={state.selectedProject} onClick={handleProjectClick} />
                    </>
                ) : null}
            </Drawer>
            <Fab variant="extended" sx={STYLES.PERIODS_BUTTON} onClick={() => setState(pv => ({ ...pv, showPeriodsList: !pv.showPeriodsList }))}>
                Ресурсы
                {[0, 1].includes(state.resourceStatus) ? (
                    <>
                        &nbsp;&nbsp;
                        <Icon sx={{ color: state.resourceStatus === 0 ? "green" : "red" }}>{state.resourceStatus === 0 ? "done" : "error"}</Icon>
                    </>
                ) : null}
            </Fab>
            <Drawer
                anchor={"right"}
                open={state.showPeriodsList}
                onClose={() => setState(pv => ({ ...pv, showPeriodsList: false }))}
                sx={STYLES.PERIODS_DRAWER}
            >
                {state.ident ? <ResMon ident={state.ident} onPlanJobsDtlProjectClick={handlePlanJobsDtlProjectClick} /> : null}
            </Drawer>
            {state.init == true ? (
                <Grid container>
                    <Grid item xs={12}>
                        {state.selectedProjectJobsLoaded ? (
                            <P8PGantt
                                {...P8P_GANTT_CONFIG_PROPS}
                                {...state.gantt}
                                containerStyle={STYLES.GANTT_CONTAINER}
                                titleStyle={STYLES.GANTT_TITLE}
                                onTitleClick={handleTitleClick}
                                onTaskDatesChange={handleTaskDatesChange}
                                taskAttributeRenderer={taskAttributeRenderer}
                            />
                        ) : !state.selectedProject ? (
                            <Box pt={3}>
                                <InlineMsgInfo okBtn={false} text={"Укажите проект для отображения его плана-графика"} />
                            </Box>
                        ) : null}
                    </Grid>
                </Grid>
            ) : null}
        </Box>
    );
};

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

export { PrjJobs };
