import React, { useCallback, useEffect, useState } from 'react';
import 'formiojs/dist/formio.full.min.css';
import 'react-tiny-fab/dist/styles.css';
import '../../theme/customScrollbar.scss';
import { FormBuilder } from 'react-formio';
import { GenericModal } from '../common/GenericModal';
import ReactJson from 'react-json-view';
import styled from 'styled-components';
import { saveAs } from 'file-saver';
import { Button, Form } from 'react-bootstrap';
import { closeSpinner, showFlashMessage, showSpinner } from '../../actions/common';
import { useDispatch } from 'react-redux';
import { FileUpload } from '../common/FileUpload';
import {
    generateUniqueID,
    deleteForm,
    deployForm,
    fetchFormDetails,
    FormProps,
    getAllForms,
    saveForm,
    searchForms,
} from '../../services/FormService';
import { FormModal } from '../common/FormModal';
import Sidebar from '../common/Sidebar';
import GSButton from '../common/Button';
import { COLORS } from '../../theme';
import { Action, Fab } from 'react-tiny-fab';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faDownload, faEye, faFile, faPlus, faUpload } from '@fortawesome/free-solid-svg-icons';
import buttonEditDisplay from 'formiojs/components/button/editForm/Button.edit.display';
import { ButtonAction, RenderedForm, SingleFormProps } from './FormTypes';
import { CustomFormattedMessage } from '../common/CustomFormattedMessage';

export interface Props {
    setOpenModal?: boolean;
    setDeploy?: boolean;
    formSchema?: unknown;
    inputValue?: string;
}

interface formDetails {
    id: number;
    name: string;
    version: number;
    deploymentName: string;
}

const getUniqueID = async (): Promise<number> => {
    const { data, success } = await generateUniqueID();
    if (success && data) {
        return data.id;
    }
    return Math.random();
};

const FormModeler: React.FC = () => {
    const [isNewForm, setIsNewForm] = useState<boolean>(false);
    const [uploadedForm, setUploadedForm] = useState<SingleFormProps | null>(null);
    const [formSchema, setFormSchema] = useState({});
    const [formDetails, setFormDetails] = useState<formDetails>({
        id: Math.random(),
        name: '',
        version: 0,
        deploymentName: '',
    });
    const [openModal, setOpenModal] = useState<boolean>(false);
    const [isViewForm, setIsViewForm] = useState<boolean>(false);
    const [deploy, setDeploy] = useState<boolean>(false);
    const [allForms, setAllForms] = useState<FormProps[]>([]);
    const [formData, setFormData] = useState<SingleFormProps>({});
    const [activeForm, setActiveForm] = useState<number | null>();
    const dispatch = useDispatch();

    const fetchAllForms = useCallback(async (): Promise<void> => {
        dispatch(showSpinner());
        const { success = false, data }: { success: boolean; data?: FormProps[] } = await getAllForms();
        dispatch(closeSpinner());
        if (success && data) {
            setAllForms(data);
        }
    }, [dispatch]);

    useEffect(() => {
        const setGeneratedId = async (): Promise<void> => {
            setFormDetails({ name: '', version: 0, deploymentName: '', id: await getUniqueID() });
        };
        setGeneratedId();
        fetchAllForms().then(() => console.log('fetched forms'));
    }, [fetchAllForms]);

    useEffect(() => {
        const triggerProcessArr = buttonEditDisplay[3].data.values.filter((item: ButtonAction) => {
            return item.value === 'triggerProcess';
        });
        const nextArr = buttonEditDisplay[3].data.values.filter((item: ButtonAction) => {
            return item.value === 'next';
        });
        const saveArr = buttonEditDisplay[3].data.values.filter((item: ButtonAction) => {
            return item.value === 'save';
        });
        const callbackArr = buttonEditDisplay[3].data.values.filter((item: ButtonAction) => {
            return item.value === 'callback';
        });

        if (triggerProcessArr.length === 0) {
            buttonEditDisplay[3].data.values = [
                ...buttonEditDisplay[3].data.values,
                {
                    label: 'Trigger Process',
                    value: 'triggerProcess',
                },
            ];
        }
        if (nextArr.length === 0) {
            buttonEditDisplay[3].data.values = [
                ...buttonEditDisplay[3].data.values,
                {
                    label: 'Next',
                    value: 'next',
                },
            ];
        }
        if (saveArr.length === 0) {
            buttonEditDisplay[3].data.values = [
                ...buttonEditDisplay[3].data.values,
                {
                    label: 'Save',
                    value: 'save',
                },
            ];
        }
        if (callbackArr.length === 0) {
            buttonEditDisplay[3].data.values = [
                ...buttonEditDisplay[3].data.values,
                {
                    label: 'Callback',
                    value: 'callback',
                },
            ];
        }
    }, []);

    const searchForm = async (searchTerm: string): Promise<void> => {
        const { data, success } = await searchForms(searchTerm.toLowerCase());
        if (success && data) {
            setAllForms(data);
        }
    };

    const onSaveChanges = async (): Promise<void> => {
        const { id, name, version } = formDetails;
        const formParams = {
            id: id,
            name: name,
            version: version,
            components: formSchema,
        };
        dispatch(showSpinner());
        const { success = false, message = '' } = await saveForm(formParams);
        if (success) {
            // setIsNewForm(false);
            setFormData(formSchema);
            setOpenModal(false);
            await fetchAllForms();
            setActiveForm(id);
            dispatch(showFlashMessage({ successMessage: message }));
        }
        dispatch(closeSpinner());
        if (!success) {
            setOpenModal(false);
            dispatch(
                showFlashMessage({
                    errorMessage: 'We are facing an internal issue, Please try again later.',
                }),
            );
        }
    };

    const deleteSelectedForm = async (): Promise<void> => {
        const { id } = formDetails;
        dispatch(showSpinner());
        const { success = false } = await deleteForm(id);
        dispatch(closeSpinner());
        if (success) {
            newForm();
            await fetchAllForms();
            dispatch(showFlashMessage({ successMessage: 'Form deleted successfully' }));
        }
        if (!success) {
            dispatch(
                showFlashMessage({
                    errorMessage: 'We are facing an internal issue, Please try again later.',
                }),
            );
        }
    };

    const onDeployChanges = async (): Promise<void> => {
        dispatch(showSpinner());
        const { success = false, message = '' } = await deployForm({ ...formDetails, components: formSchema });
        if (success) {
            setOpenModal(false);
            dispatch(showFlashMessage({ successMessage: message }));
        }
        dispatch(closeSpinner());
        if (!success) {
            setOpenModal(false);
            dispatch(
                showFlashMessage({
                    errorMessage: 'Unable to deploy form',
                }),
            );
        }
    };

    const handleSubmit = (event: React.FormEvent<HTMLElement>): void => {
        event.preventDefault();
        deploy ? onDeployChanges() : onSaveChanges();
    };

    const onChangeFunc = (statename: string, event: React.BaseSyntheticEvent): void => {
        const { value } = event.target;
        setFormDetails((prevState) => ({
            ...prevState,
            [statename]: value,
        }));
    };

    const renderFormDetails = ({
        fieldLabel,
        stateName,
        inputType = 'text',
        controlId,
        disabled = false,
    }: RenderedForm): React.ReactElement => {
        return (
            <Form.Group controlId={controlId}>
                <Form.Label>{<CustomFormattedMessage id={fieldLabel} />}</Form.Label>
                <Form.Control
                    disabled={disabled}
                    required
                    type={inputType}
                    value={formDetails[stateName]}
                    onChange={(evt: React.BaseSyntheticEvent): void => onChangeFunc(stateName, evt)}
                />
            </Form.Group>
        );
    };

    const renderModalView = (): React.ReactElement => {
        return (
            <FormModal
                title={<CustomFormattedMessage id="form" />}
                show={openModal}
                onCancel={(): void => setOpenModal(false)}
                onSave={(): Promise<void> => (deploy ? onDeployChanges() : onSaveChanges())}
                centered
                hideFooter>
                <Form onSubmit={handleSubmit}>
                    {!deploy &&
                        renderFormDetails({
                            fieldLabel: 'Id',
                            stateName: 'id',
                            controlId: 'formControlsText',
                            disabled: true,
                        })}
                    {!deploy &&
                        renderFormDetails({
                            fieldLabel: 'name',
                            stateName: 'name',
                            controlId: 'formControlsText',
                        })}
                    {!deploy &&
                        renderFormDetails({
                            fieldLabel: 'version',
                            stateName: 'version',
                            controlId: 'formControlsText',
                            inputType: 'number',
                        })}
                    {deploy &&
                        renderFormDetails({
                            fieldLabel: 'deploymentName',
                            stateName: 'deploymentName',
                            controlId: 'formControlsText',
                        })}
                    <div style={{ marginTop: '40px' }}>
                        <Button data-testid="submitModalButton" type="submit" variant="success">
                            <CustomFormattedMessage id="submit" />
                        </Button>
                        <Button
                            style={{ marginLeft: '15px', alignSelf: 'center' }}
                            onClick={(): void => setOpenModal(false)}>
                            <CustomFormattedMessage id="cancel" />
                        </Button>
                    </div>
                </Form>
            </FormModal>
        );
    };

    const renderFormButtons = (): React.ReactElement => {
        return (
            <ButtonsWrapper>
                <Button
                    data-testid="saveButton"
                    style={{
                        margin: '5px',
                    }}
                    variant="success"
                    onClick={(): void => {
                        setOpenModal(true);
                        setDeploy(false);
                    }}>
                    <CustomFormattedMessage id="save" />
                </Button>
                {activeForm && (
                    <Button
                        data-testid="deploy"
                        style={{
                            margin: '5px',
                        }}
                        variant="success"
                        onClick={(): void => {
                            setOpenModal(true);
                            setDeploy(true);
                        }}>
                        <CustomFormattedMessage id="deploy" />
                    </Button>
                )}
                {activeForm && (
                    <Button
                        data-testid="delete"
                        style={{
                            margin: '5px',
                        }}
                        variant="danger"
                        onClick={deleteSelectedForm}>
                        <CustomFormattedMessage id="delete" />
                    </Button>
                )}
            </ButtonsWrapper>
        );
    };

    const getFormDetails = async ({ id }: { id?: number }): Promise<void> => {
        if (id) {
            dispatch(showSpinner());
            const { success, data } = await fetchFormDetails(id);
            dispatch(closeSpinner());
            if (success && data) {
                setActiveForm(id);
                setFormData(data?.components);
                setFormDetails((prevState) => ({
                    ...prevState,
                    id: data.id,
                    name: data.name,
                    version: data.version,
                }));
            } else {
                dispatch(
                    showFlashMessage({
                        errorMessage: 'Unable to fetch form details',
                    }),
                );
            }
        }
    };

    const downloadForm = (): void => {
        const fileToSave = new Blob([JSON.stringify(formSchema, null, 2)]);
        saveAs(fileToSave, 'form.json');
    };

    const newForm = async (): Promise<void> => {
        setIsNewForm(true);
        setUploadedForm(null);
        setFormData({});
        setActiveForm(null);
        setFormDetails({
            id: await getUniqueID(),
            name: '',
            version: 0,
            deploymentName: '',
        });
    };

    const onReaderLoad = async (event) => {
        setIsNewForm(false);
        setUploadedForm(JSON.parse(event.target.result));
        setFormData({});
        setActiveForm(null);
        setFormDetails({
            id: await getUniqueID(),
            name: '',
            version: 0,
            deploymentName: '',
        });
    };

    const openForm = (file) => {
        const reader = new FileReader();
        reader.onload = onReaderLoad;
        reader.readAsText(file);
    };

    const getForm = (): SingleFormProps => {
        if (isNewForm) {
            return {
                display: 'form',
                components: [],
            };
        } else if (uploadedForm) {
            return uploadedForm;
        } else if (formData && Object.keys(formData).length) {
            return formData;
        } else {
            return {
                display: 'form',
            };
        }
    };

    const onFormSchemaChange = (schema) => {
        setIsNewForm(false);
        setFormSchema(schema);
    };

    const rendernames = (): React.ReactElement[] | null => {
        if (allForms && allForms.length) {
            return allForms.map((form, key) => {
                return (
                    <FormButtoms key={key}>
                        <GSButton
                            data-testid="testName"
                            style={{ width: '90%', borderRadius: '0px' }}
                            activeButton={activeForm === form.id}
                            onClick={(): Promise<void> => getFormDetails(form)}>
                            {form.name}
                        </GSButton>
                    </FormButtoms>
                );
            });
        }
        return null;
    };

    const renderFab = (): React.ReactElement => {
        return (
            <FabWrapper>
                <Fab
                    style={{ bottom: 0 }}
                    mainButtonStyles={{
                        outline: 'none',
                        backgroundColor: COLORS.ACTIVE_BUTTON,
                        width: 35,
                        height: 35,
                        zIndex: 1,
                    }}
                    event={'hover'}
                    icon={<FontAwesomeIcon icon={faPlus} style={{ height: 10, width: 10 }} />}
                    alwaysShowTitle={false}>
                    <Action
                        data-testid="createNewForm"
                        text="New Form"
                        onClick={newForm}
                        style={{
                            outline: 'none',
                            backgroundColor: COLORS.ACTIVE_BUTTON,
                            width: 30,
                            height: 30,
                        }}>
                        <FontAwesomeIcon icon={faFile} style={{ height: 10, width: 10 }} />
                    </Action>

                    <Action
                        data-testid="viewForm"
                        text="View Form"
                        onClick={(): void => setIsViewForm(true)}
                        style={{
                            outline: 'none',
                            backgroundColor: COLORS.ACTIVE_BUTTON,
                            width: 30,
                            height: 30,
                        }}>
                        <FontAwesomeIcon icon={faEye} style={{ height: 10, width: 10 }} />
                    </Action>
                    <Action
                        text="Import Form"
                        style={{
                            outline: 'none',
                            backgroundColor: COLORS.ACTIVE_BUTTON,
                            width: 30,
                            height: 30,
                        }}>
                        <FileUpload extensions={['json']} onChange={openForm} onError={alert}>
                            <div
                                style={{
                                    height: '4em',
                                    width: '4em',
                                    display: 'flex',
                                    alignItems: 'center',
                                    justifyContent: 'center',
                                }}>
                                <FontAwesomeIcon icon={faUpload} style={{ height: 10, width: 10 }} />
                            </div>
                        </FileUpload>
                    </Action>
                    <Action
                        data-testid="downloadForm"
                        onClick={downloadForm}
                        text="Export Form"
                        style={{
                            outline: 'none',
                            backgroundColor: COLORS.ACTIVE_BUTTON,
                            width: 30,
                            height: 30,
                        }}>
                        <FontAwesomeIcon icon={faDownload} style={{ height: 10, width: 10 }} />
                    </Action>
                </Fab>
            </FabWrapper>
        );
    };

    return (
        <>
            <div
                style={{
                    width: '100%',
                    flexDirection: 'row',
                    display: 'flex',
                    overflowY: 'auto',
                }}>
                <Sidebar inputValue={formDetails.id} onChange={searchForm}>
                    <div className="custom-scrollbar">{rendernames()}</div>
                </Sidebar>

                {renderFab()}
                <div style={{ width: '100%' }}>
                    <FormBuilderWrapper>
                        <FormBuilder form={getForm()} onChange={(schema) => onFormSchemaChange(schema)} />
                    </FormBuilderWrapper>
                    {renderFormButtons()}
                </div>
            </div>
            {renderModalView()}
            <GenericModal show={isViewForm} cancel={(): void => setIsViewForm(false)}>
                <div style={{ paddingBottom: '1em', overflow: 'auto' }}>
                    {formSchema && <ReactJson name={false} src={formSchema} />}
                </div>
            </GenericModal>
        </>
    );
};

export default FormModeler;

const FormBuilderWrapper = styled.div`
    overflow-x: hidden;
    padding: 20px 20px 5px 20px;
    overflow-y: auto;
    height: calc(100vh - 140px);
`;

const ButtonsWrapper = styled.div`
    position: fixed;
    bottom: 10px;
    margin-left: 15px;
`;

const FormButtoms = styled.div`
    flex-direction: row;
    margin-bottom: 10px;
    display: flex;
    justify-content: center;
`;

const FabWrapper = styled.div`
    .rtf {
        margin: 0 0 -42px -45px;
        z-index: 1;
    }

    .rtf--ab__c {
        position: relative !important;
        padding: 0 !important;
        margin: -12px -36px !important;
    }
`;
