import React, { useEffect, useState, useCallback, useRef } from 'react';
import { __, sprintf } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { isEmpty, head, pathOr } from 'ramda';

// store
import { storeSet } from '../../store';

// api
import FormsService from '../../service/forms-service';
import FlowService from '../../service/flow-service';

// tools
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';

// components
import { Button } from 'primereact/button';
import { Dropdown } from 'primereact/dropdown';
import { Messages } from 'primereact/messages';
import { confirmPopup, ConfirmPopup } from 'primereact/confirmpopup';
import { Toast } from 'primereact/toast';

const BandoFlowEdit = () => {
    const { id } = useParams();
    const navigate = useNavigate();

    const [flowStructure, setFlowStructure] = useState({
        initialForm: 0,
        finalForm: 0,
        flowData: [],
        flowEdges: [],
        chosenField: ''
    });

    const [forms, setForms] = useState([]);
    const [formOptions, setFormOptions] = useState([]);
    const [chosenMainFieldOptions, setChosenMainFieldOptions] = useState([]);
    const [mainFieldSuboptions, setMainFieldSubOptions] = useState([]);
    const [bandoStatus, setBandoStatus] = useState('');
    const [isFlowAllowed, setIsFlowAllowed] = useState(true);
    const flowMsgs = useRef(null);
    const toast = useRef(null);
    const itemRefs = useRef({});
    const itemContainerRef = useRef(null);

    const getBandoId = () => {
        const parsed = parseInt(id)
        return !isNaN(parsed) ? parsed : 0;
    }

    const goBack = () => {
        const bandoId = getBandoId();
        navigate(`/bandi/${bandoId}/forms`);
    }

    const confirmDelete = (event) => {
        confirmPopup({
            target: event.currentTarget,
            message: __('Sei sicuro di reset questo flow?', 'gepafin'),
            acceptLabel: __('Si', 'gepafin'),
            icon: 'pi pi-info-circle',
            defaultFocus: 'reject',
            acceptClassName: 'p-button-danger',
            accept: doDelete,
            reject: () => {
            }
        });
    };

    const doDelete = () => {
        if (flowMsgs.current) {
            flowMsgs.current.clear();
        }

        setFlowStructure({
            initialForm: 0,
            finalForm: 0,
            flowData: [],
            flowEdges: [],
            chosenField: ''
        })

        setIsFlowAllowed(false);
        setChosenMainFieldOptions([]);
    }

    const updateInitialForm = useCallback((value) => {
        const finalFormObj = head(forms.filter(o => o.id !== value));

        if (forms.length === 2 && finalFormObj) {
            setFlowStructure({
                ...flowStructure,
                initialForm: value,
                finalForm: finalFormObj.id
            });
        } else {
            setFlowStructure({
                ...flowStructure,
                initialForm: value
            });
        }
    }, [flowStructure])

    const updateFinalForm = useCallback((value) => {
        const filtered = flowStructure.flowData.filter(o => o.formId === flowStructure.initialForm);
        const flowEdges = buildFlowEdges(flowStructure.initialForm, value);

        setFlowStructure({
            ...flowStructure,
            flowEdges,
            flowData: filtered,
            finalForm: value
        });
    }, [flowStructure]);

    const updateChosenField = useCallback((value) => {
        setFlowStructure({
            ...flowStructure,
            chosenField: value
        });
    }, [flowStructure]);

    const addFlowData = useCallback((data) => {
        const initial = flowStructure.flowData;
        const exists = initial ? initial.filter(o => parseInt(o.formId) === parseInt(data.formId)) : [];
        let final = [];

        if (exists.length) {
            final = initial.map(o => parseInt(o.formId) === parseInt(data.formId) ? data : o);
        } else {
            final = [...initial, data];
        }

        setFlowStructure({
            ...flowStructure,
            flowData: final
        });
    }, [flowStructure]);

    const updateItermediateForm = (value, formId) => {
        const isUsed = flowStructure.flowData.map(o => o.chosenValue).filter(v => !isEmpty(v)).includes(value);
        if (!isUsed) {
            const data = {
                formId: parseInt(formId),
                chosenField: '',
                chosenValue: value
            }
            addFlowData(data);
        }
    }

    const displayChosenOptionValue = (id) => {
        const suboptionId = pathOr('', ['chosenValue'], head(flowStructure.flowData.filter(f => parseInt(f.formId) === parseInt(id))));
        return pathOr('', ['label'], head(mainFieldSuboptions.filter(o => o.name === suboptionId)));
    }

    const disabledOptionForIntermediateForm = (opt) => {
        return flowStructure.flowData.map(o => o.chosenValue).filter(v => !isEmpty(v)).includes(opt.name);
    }

    const shoudDisableSaving = useCallback(() => {
        const nonEmptyFlowItems = flowStructure.flowData.filter(o => isEmpty(o.chosenField)).filter(o => !isEmpty(o.chosenValue));

        /*if (forms.length > 2) {
            console.log('disable BTN:', nonEmptyFlowItems.length !== forms.length - 2, isEmpty(flowStructure.flowEdges), 'PUBLISH' === bandoStatus,
                isEmpty(flowStructure.initialForm), isEmpty(flowStructure.finalForm));
        } else {
            console.log('disable BTN (2 forms):', isEmpty(flowStructure.flowEdges), 'PUBLISH' === bandoStatus,
                isEmpty(flowStructure.initialForm), isEmpty(flowStructure.finalForm));
        }*/

        return forms.length > 2
            ? nonEmptyFlowItems.length !== forms.length - 2 || isEmpty(flowStructure.flowEdges) || 'PUBLISH' === bandoStatus
            || isEmpty(flowStructure.initialForm) || isEmpty(flowStructure.finalForm)
            : isEmpty(flowStructure.flowEdges) || 'PUBLISH' === bandoStatus
            || isEmpty(flowStructure.initialForm) || isEmpty(flowStructure.finalForm);
    }, [flowStructure, forms]);

    const doSave = () => {
        storeSet('setAsyncRequest');
        const bandoId = getBandoId();

        if (flowMsgs.current) {
            flowMsgs.current.clear();
        }

        FlowService.createFlow(bandoId, flowStructure, getFlowCreateCallback, errGetFlowCreateCallback);
    }

    const getFlowCreateCallback = (data) => {
        if (data.status === 'SUCCESS') {
            if (toast.current) {
                toast.current.show({
                    severity: 'success',
                    summary: '',
                    detail: __('Il flusso è stato aggiornato correttamente!', 'gepafin')
                });
            }
        }
        storeSet('unsetAsyncRequest');
    }

    const errGetFlowCreateCallback = (data) => {
        set404FromErrorResponse(data);
        storeSet('unsetAsyncRequest');
    }

    const getFormsCallback = (data) => {
        if (data.status === 'SUCCESS') {
            setForms(data.data);
            const formOptions = data.data.map(o => ({ label: o.label, value: o.id }))
            setFormOptions([{ label: '', value: '' }, ...formOptions]);
            const bandoId = getBandoId();

            storeSet('setAsyncRequest');
            FlowService.getFlow(bandoId, (resp) => getFlowCallback(resp, data.data), errGetFlowCallback);
        }
        storeSet('unsetAsyncRequest');
    }

    const errGetFormsCallback = (data) => {
        set404FromErrorResponse(data);
        storeSet('unsetAsyncRequest');
    }

    const getFlowCallback = (data, forms) => {
        if (data.status === 'SUCCESS' && data.data) {
            const chosenFieldItem = head(data.data.flowData.filter(o => !isEmpty(o.chosenField)));
            setBandoStatus(data.data.callStatus);

            if (chosenFieldItem) {
                setFlowStructure({
                    initialForm: data.data.initialForm,
                    finalForm: data.data.finalForm,
                    flowData: data.data.flowData,
                    flowEdges: data.data.flowEdges,
                    chosenField: chosenFieldItem.chosenField
                });
                const form = head(forms.filter(o => o.id === data.data.initialForm));
                const relevantFields = form
                    ? form.content
                        .filter(o => ['radio', 'select'].includes(o.name))
                        .map(o => {
                            const label = head(o.settings.filter(o => o.name === 'label'));
                            return { value: o.id, label: label ? label.value : o.label };
                        })
                    : [];
                setChosenMainFieldOptions(relevantFields);
                const field = form ? head(form.content.filter(o => o.id === chosenFieldItem.chosenField)) : null;
                if (field) {
                    const options = head(field.settings.filter(o => o.name === 'options'));
                    setMainFieldSubOptions(options.value);
                }
            } else {
                setFlowStructure({
                    initialForm: data.data.initialForm,
                    finalForm: data.data.finalForm,
                    flowData: data.data.flowData,
                    flowEdges: data.data.flowEdges,
                    chosenField: ''
                });
            }
        }
        storeSet('unsetAsyncRequest');
    }

    const errGetFlowCallback = (data) => {
        set404FromErrorResponse(data);
        storeSet('unsetAsyncRequest');
    }

    const setItemRef = (id, element) => {
        itemRefs.current[id] = element;
    };

    const buildFlowEdges = (initialForm, finalForm) => {
        let edges = [];

        if (!isEmpty(initialForm) && !isEmpty(finalForm)) {
            // eslint-disable-next-line
            forms.map(o => {
                const formId = String(o.id);

                if (formId !== String(initialForm) && formId !== String(finalForm)) {
                    edges.push({
                        id: `${initialForm}->${formId}`,
                        source: String(initialForm),
                        target: formId,
                        type: 'smoothstep'
                    });
                }
                if (formId !== String(initialForm) && formId !== String(finalForm) && String(finalForm) !== '0') {
                    edges.push({
                        id: `${formId}->${finalForm}`,
                        source: formId,
                        target: String(finalForm),
                        type: 'smoothstep'
                    });
                }
            });

            if (forms.length === 2 && initialForm && finalForm) {
                edges.push({
                    id: `${initialForm}->${finalForm}`,
                    source: String(initialForm),
                    target: String(finalForm),
                    type: 'smoothstep'
                });
            }
        }

        return edges;
    };

    useEffect(() => {
        const bandoId = getBandoId();
        storeSet('setAsyncRequest');
        FormsService.getFormsForCall(bandoId, getFormsCallback, errGetFormsCallback);
    }, [id]);

    useEffect(() => {
        if (flowMsgs.current && forms.length < 2) {
            flowMsgs.current.clear();
            flowMsgs.current.show([
                {
                    id: '1',
                    sticky: true, severity: 'info', summary: '',
                    detail: __('Devi creare almeno 2 form.', 'gepafin'),
                    closable: false
                }
            ]);
        } else {
            flowMsgs.current.clear();
            if (itemContainerRef.current) {
                itemContainerRef.current.dispatchEvent(new Event('scroll'));
            }
        }
    }, [forms]);

    useEffect(() => {
        const initialForm = flowStructure.initialForm;
        const finalForm = flowStructure.finalForm;
        const chosenField = flowStructure.chosenField;

        if (!isEmpty(initialForm) && !isEmpty(finalForm)) {
            const form = head(forms.filter(o => String(o.id) === String(initialForm)))
            const relevantFields = form
                ? form.content
                    .filter(o => ['radio', 'select'].includes(o.name))
                    .map(o => {
                        const label = head(o.settings.filter(o => o.name === 'label'));
                        return { value: o.id, label: label ? label.value : o.label };
                    })
                : [];
            setChosenMainFieldOptions([
                { label: isEmpty(relevantFields) ? __('Nessun scelta', 'gepafin') : '', value: '' },
                ...relevantFields]
            );

            if (forms.length === 2) {
                setIsFlowAllowed(true);
            }

            //const flowEdges = buildFlowEdges(initialForm, finalForm);

            if (!isEmpty(chosenField)) {
                const field = form ? head(form.content.filter(o => o.id === chosenField)) : null;
                let options = [];

                if (field) {
                    options = head(field.settings.filter(o => o.name === 'options'));
                }

                if (field && options.value && options.value.length === forms.length - 2) {
                    setIsFlowAllowed(true);
                    const suboptions = [
                        { label: __('Nessun scelta', 'gepafin'), name: '' },
                        ...options.value
                    ]

                    setMainFieldSubOptions(suboptions);

                    const data = {
                        formId: parseInt(initialForm),
                        chosenField: chosenField,
                        chosenValue: ''
                    }

                    addFlowData(data);

                    if (flowMsgs.current && !isEmpty(chosenField)) {
                        flowMsgs.current.clear();
                    }
                } else {
                    setIsFlowAllowed(false);

                    let msg = 'Non è possibile creare il flusso. Il campo principale deve avere esattamente %s opzioni.';

                    if (forms.length - 2 === 1) {
                        msg = 'Non è possibile creare il flusso. Il campo principale deve avere esattamente %s opzioni.';
                    }

                    if (flowMsgs.current && !isEmpty(chosenField)) {
                        flowMsgs.current.clear();
                        flowMsgs.current.show([
                            {
                                id: '1',
                                sticky: true, severity: 'error', summary: '',
                                detail: sprintf(
                                    __(msg, 'gepafin'),
                                    forms.length - 2
                                ),
                                closable: false
                            }
                        ]);
                    }
                }
            }
        }
    }, [flowStructure.initialForm, flowStructure.finalForm, flowStructure.chosenField]);

    const { initialForm = 0, finalForm = 0, flowData = [], chosenField = '' } = flowStructure;
    const initialFormData = head(forms.filter(o => o.id === initialForm));
    const finalFormData = head(forms.filter(o => o.id === finalForm));
    const levelForms = forms.filter(o => o.id !== initialForm && o.id !== finalForm);

    return (
        <div className="appPage">
            <div className="appPage__pageHeader">
                <h1>{__('Gestisci flusso dei form', 'gepafin')}</h1>
                <p>
                    {__('Scegli un form iniziale e li form finale e aggiungi i form intermedi per questo bando', 'gepafin')}
                </p>
            </div>

            <div className="appPage__spacer"></div>

            <div className="appPageSection">
                <div className="appForm__cols">
                    <div className="appForm__field">
                        <label htmlFor="initialForm">{__('Scegli form iniziale', 'gepafin')}</label>
                        <Dropdown
                            id="initialForm"
                            disabled={'PUBLISH' === bandoStatus}
                            value={initialForm}
                            onChange={(e) => updateInitialForm(e.value)}
                            optionDisabled={(opt) => finalForm === opt.value || isEmpty(opt.value)}
                            options={formOptions}
                            optionLabel="label"
                            optionValue="value"
                            placeholder={__('Scegli il form', 'gepafin')}/>
                    </div>

                    {forms.length > 2 && initialForm && chosenMainFieldOptions
                        ? <div className="appForm__field">
                            <label htmlFor="chosenMainField">{__('Scegli il campo principale', 'gepafin')}</label>
                            <Dropdown
                                id="chosenMainField"
                                disabled={'PUBLISH' === bandoStatus}
                                value={chosenField}
                                onChange={(e) => updateChosenField(e.value)}
                                optionDisabled={(opt) => isEmpty(opt.value)}
                                options={chosenMainFieldOptions}
                                optionLabel="label"
                                optionValue="value"
                                placeholder={__('Scegli il campo', 'gepafin')}/>
                        </div> : null}

                    {(forms.length > 2 && chosenField && isFlowAllowed) || (forms.length === 2 && isFlowAllowed)
                        ? <div className="appForm__field">
                            <label htmlFor="finalForm">{__('Scegli form finale', 'gepafin')}</label>
                            <Dropdown
                                id="finalForm"
                                disabled={'PUBLISH' === bandoStatus}
                                value={finalForm}
                                onChange={(e) => updateFinalForm(e.value)}
                                optionDisabled={(opt) => initialForm === opt.value || isEmpty(opt.value)}
                                options={formOptions}
                                optionLabel="label"
                                optionValue="value"
                                placeholder={__('Scegli il form', 'gepafin')}/>
                        </div> : null}
                </div>
            </div>

            <div className="appPageSection">
                <div className="appPageSection__actions">
                    <Button
                        onClick={goBack}
                        outlined
                        label={__('Indietro', 'gepafin')} icon="pi pi-arrow-left" iconPos="left"/>
                    <Button
                        onClick={doSave}
                        disabled={shoudDisableSaving()}
                        label={__('Salva', 'gepafin')} icon="pi pi-save" iconPos="right"/>
                </div>
            </div>

            <div className="appPageSection">
                <Messages ref={flowMsgs}/>

                {forms.length >= 2 && initialForm && finalForm && isFlowAllowed
                    ? <div className="flowContainer" ref={itemContainerRef}>
                        <div className="flowContainerInner">
                            <div className="flowContainer__level initialLevel">
                                <div className="flowContainer__flowItem initialForm"
                                     ref={(el) => initialForm ? setItemRef(initialForm, el) : null}>
                                    <div className="flowContainer__flowItemInner">
                                        <label htmlFor="chosenMainField">
                                            {initialFormData.label}
                                        </label>
                                    </div>
                                </div>
                            </div>

                            {levelForms.length && initialForm && finalForm
                                ? <div className="flowContainer__level intermediateLevel">
                                    {levelForms.map((o, i) => <div key={o.id}
                                                                   ref={(el) => setItemRef(o.id, el)}
                                                                   className="flowContainer__flowItem levelForms">
                                        <div className="flowContainer__flowItemInner">
                                            <label htmlFor="chosenMainField">{o.label}</label>
                                            <div className="flowContainer__flowItemContent">
                                                {mainFieldSuboptions && !isEmpty(mainFieldSuboptions)
                                                    ? 'PUBLISH' !== bandoStatus
                                                        ? <Dropdown
                                                            id="initialForm"
                                                            value={pathOr('', ['chosenValue'], head(flowData.filter(f => f.formId === parseInt(o.id))))}
                                                            onChange={(e) => updateItermediateForm(e.value, o.id)}
                                                            options={mainFieldSuboptions}
                                                            optionDisabled={disabledOptionForIntermediateForm}
                                                            optionLabel="label"
                                                            optionValue="name"
                                                            placeholder={__('Scegli il valore', 'gepafin')}/>
                                                        :
                                                        <label>{displayChosenOptionValue(o.id)}</label>
                                                    : null}
                                            </div>
                                        </div>
                                    </div>)}
                                    {levelForms.length > 1
                                        ? <>
                                            <div className="flowContainer__levelMaskEnd"></div>
                                            <div className="flowContainer__levelMaskStart"></div>
                                        </> : null}
                                </div> : null}

                            {forms.length >= 2 && initialForm && finalForm
                                ? <div className="flowContainer__level finalLevel">
                                    <div className="flowContainer__flowItem finalForm"
                                         ref={(el) => finalForm ? setItemRef(finalForm, el) : null}>
                                        <div className="flowContainer__flowItemInner">
                                            <label htmlFor="chosenMainField">
                                                {finalFormData.label}
                                            </label>
                                        </div>
                                    </div>
                                </div>
                                : null}
                        </div>
                    </div> : null}
            </div>

            <div className="appPage__spacer"></div>

            <Toast ref={toast}/>
            <div className="appPageSection">
                <div className="appPageSection__actions">
                    <Button
                        onClick={goBack}
                        outlined
                        label={__('Indietro', 'gepafin')} icon="pi pi-arrow-left" iconPos="left"/>
                    <Button
                        onClick={doSave}
                        disabled={shoudDisableSaving()}
                        label={__('Salva', 'gepafin')} icon="pi pi-save" iconPos="right"/>
                </div>
                <div className="appPageSection__actions">
                    <ConfirmPopup/>
                    <Button
                        onClick={confirmDelete}
                        disabled={'PUBLISH' === bandoStatus}
                        severity="warning"
                        label={__('Reset', 'gepafin')} icon="pi pi-refresh" iconPos="right"/>
                </div>
            </div>
        </div>
    )

}

export default BandoFlowEdit;