import React, { useCallback, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import { useForm } from 'react-hook-form';
import Grid from '../../atoms/grid/Grid';
import GridContainer from '../../atoms/grid-container/GridContainer';
import Input from '../../atoms/input/Input';
import Checkbox from '../../atoms/checkbox/Checkbox';
import Textarea from '../../atoms/textarea/Textarea';
import Select from '../../atoms/select/Select';
import Button from '../../atoms/button/Button';
import Icon from '../../atoms/icon/Icon';
import { addAlert, ALERT_LEAD_ADD_SUCCESS } from '../../tools/alerts';
import { createNamespace } from '../../tools/namingTools';
import { generateUniqueID, getCookieValue } from '../../tools/helpers';
import { useContainerWidth } from '../../tools/widths/useContainerWidth';
import { bMedium } from '../../constants/breakpoints';
import { SMALL } from '../../constants/sizes';
import './styles/LeadForm.scss';
import withListener from '../withListener';
import Alert, { alertTypes } from '../../atoms/alert/Alert';

const n = createNamespace('Leadform');

const TYPE_PHONE = 'Tel';
const TYPE_EMAIL = 'Email';
const TYPE_TEXT = 'Text';
const TYPE_TEXTAREA = 'TextArea';
const TYPE_SELECT = 'Select';
const TYPE_STYLED_SELECT = 'StyledSelect';
const TYPE_CHECKBOX = 'Checkboxes';
const NAME_COUNTRY = 'country';

const getDefaultCountry = country => {
    if (!country || !country.options || !country.options.length) {
        return '';
    }

    return country.options.find(item => item.isSelected);
};

const parseCustomAttr = (attrs = {}) => {
    const customAttributes = {};
    Object.keys(attrs).forEach(key => {
        customAttributes[key] = [attrs[key]];
    });
    return customAttributes;
};

const createPostPayload = (otherData, formData) => {
    const {
        companyName,
        companySize,
        street1,
        street2,
        city,
        state,
        province,
        zip,
        country,
        firstName,
        lastName,
        email,
        phone,
        phoneExtension,
        notes,
        comment,
        ...customAttributes
    } = formData;
    const customAttrs = parseCustomAttr(customAttributes);
    const payload = {
        ...otherData,
        customer: {
            company: {
                name: companyName,
                size: companySize && companySize.value,
                street1,
                street2,
                city,
                state: state && state.value,
                province: province && province.value,
                zip,
                country: country && country.value
            },
            contact: {
                firstName,
                lastName,
                email,
                phone,
                phoneExtension: phoneExtension || ''
            }
        },
        customAttrs,
        notes,
        comment
    };
    return payload;
};

const LeadForm = ({
    data: {
        form: {
            actionEndpoint,
            productUrl,
            otherData,
            userInfo,
            schema: {
                firstName,
                lastName,
                email,
                phone,
                phoneExtension,
                companyName,
                companySize,
                street1,
                street2,
                city,
                state,
                province,
                zip,
                country,
                notes,
                custom
            }
        },
        i18n
    }
}) => {
    const [currentCountry, setCurrentCountry] = useState(getDefaultCountry(country));
    const [errorStatus, setErrorStatus] = useState(false);
    const [containerRef, containerWidth] = useContainerWidth();
    const isMobile = containerWidth && containerWidth < bMedium;
    const columns = isMobile ? 1 : 2;
    const { handleSubmit, register, errors, control } = useForm();

    // since form inputs are uncontrolled, we don't have access to empty their values, so in order
    // to empty them, we just recreate them with a different key when we want to empty them
    // we cannot use html form reset, because some inputs (Selects, Checkboxes) need to be controlled in order
    // to be displayed correctly, as they are not native inputs
    const [currentSession, setSession] = useState(Date.now());
    const resetForm = () => {
        setCurrentCountry(getDefaultCountry(country));
        setSession(Date.now());
    };

    const onSubmit = formData => {
        const payload = createPostPayload(otherData, formData);
        const payloadString = JSON.stringify(payload);

        const XHR = new XMLHttpRequest();

        XHR.addEventListener('load', () => {
            resetForm();
            addAlert(ALERT_LEAD_ADD_SUCCESS);
            window.location.assign(productUrl);
        });

        XHR.addEventListener('error', () => {
            setErrorStatus(true);
        });

        XHR.open('POST', actionEndpoint);
        XHR.setRequestHeader('Content-Type', 'application/json');
        XHR.setRequestHeader('X-XSRF-TOKEN', getCookieValue('XSRF-TOKEN'));
        XHR.send(payloadString);
    };

    const closeForm = () => {
        window.location.assign(productUrl);
    };

    const getInputValidationRules = useCallback(
        item => {
            const inputValidationRules = {};

            if (item.isRequired) {
                inputValidationRules.required = {
                    value: item.isRequired,
                    message: i18n.defaultError
                };
            }

            // TODO add custom message for maxLength error
            if (item.editorAttrs && item.editorAttrs.maxlength) {
                inputValidationRules.maxLength = {
                    value: item.editorAttrs.maxlength,
                    message: i18n.defaultError
                };
            }

            // TODO add custom message for email error
            if (item.validators) {
                item.validators.forEach(validator => {
                    if (validator === 'email') {
                        inputValidationRules.pattern = {
                            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
                            message: i18n.defaultError
                        };
                    }
                    if (validator === 'arrayRequired') {
                        inputValidationRules.required = {
                            value: item.isRequired,
                            message: i18n.defaultError
                        };
                    }
                });
            }

            return inputValidationRules;
        },
        [i18n.defaultError]
    );

    const getCountryPhoneCode = useMemo(() => {
        if (!currentCountry || !currentCountry.phoneCode) {
            return null;
        }
        return `+${currentCountry.phoneCode}`;
    }, [currentCountry]);

    const renderInput = item => {
        if (!item) {
            return null;
        }

        const { name, title, hint, isRequired, type } = item;
        const placeholder = item.editorAttrs && item.editorAttrs.placeholder;
        const errorMessage = errors && errors[name] ? errors[name].message : null;
        const validationRules = getInputValidationRules(item);
        const optionList = item.options && [...item.options];
        const inputKey = generateUniqueID([name, currentSession]);

        let inputUpdateFunc = null;
        let addon = '';
        let defaultValue = '';

        if (item.countryCodeValidationField) {
            addon = getCountryPhoneCode;
        }

        if (name === NAME_COUNTRY) {
            defaultValue = currentCountry;
            inputUpdateFunc = setCurrentCountry;
        }

        switch (type) {
            case TYPE_CHECKBOX: {
                return (
                    <div key={inputKey}>
                        {item.options.map(option => {
                            const optionErrorMessage = errors
                            && errors[option.value] ? errors[option.value].message : null;
                            return (
                                <Checkbox
                                    key={option.value}
                                    title={title}
                                    name={option.value}
                                    hint={hint}
                                    label={option.label}
                                    error={optionErrorMessage}
                                    checkboxSize={SMALL.value}
                                    inputRef={register(validationRules)}
                                />
                            );
                        })}
                    </div>
                );
            }
            case TYPE_TEXTAREA:
                return (
                    <Textarea
                        key={inputKey}
                        name={name}
                        label={title}
                        placeholder={placeholder}
                        inputRef={register(validationRules)}
                        error={errorMessage}
                    />
                );
            case TYPE_SELECT:
            case TYPE_STYLED_SELECT:
                return (
                    <Select
                        key={inputKey}
                        name={name}
                        label={title}
                        defaultValue={defaultValue}
                        placeholder={placeholder || i18n.select}
                        onChange={inputUpdateFunc}
                        options={optionList}
                        hookFormControl={control}
                        validationRules={validationRules}
                        error={errorMessage}
                        isSearchable
                    />
                );
            default:
                if (userInfo[name]) {
                    defaultValue = userInfo[name];
                }

                return (
                    <Input
                        key={inputKey}
                        name={name}
                        label={title}
                        defaultValue={defaultValue}
                        type={type && type.toLowerCase()}
                        hint={hint}
                        addon={addon}
                        required={isRequired}
                        error={errorMessage}
                        inputRef={register(validationRules)}
                    />
                );
        }
    };

    const renderCustomFields = () => {
        if (!custom || !custom.length) {
            return null;
        }

        return custom.map(item => (
            <div {...n('formGroup').props} key={item.name}>
                {renderInput(item)}
            </div>
        ));
    };

    const renderButtons = (
        <div {...n('buttons').props}>
            <div {...n('submit').props}>
                <Button type="primary" action="submit">
                    {i18n.submit}
                </Button>
            </div>
        </div>
    );

    const renderStateProvinceBlock = () => {
        if (currentCountry && province && currentCountry.value_iso3 === province.visibleOn) {
            return renderInput(province);
        }

        if (currentCountry && state && currentCountry.value_iso3 === state.visibleOn) {
            return renderInput(state);
        }

        return null;
    };

    const renderPhoneBlock = () => (
        <div {...n('row').props}>
            {phone && <div {...n('col', isMobile ? '8' : '9').props}>{renderInput(phone)}</div>}
            {phoneExtension && <div {...n('col', isMobile ? '4' : '3').props}>{renderInput(phoneExtension)}</div>}
        </div>
    );

    return (
        <section {...n('container').props} ref={containerRef}>
            <GridContainer {...n('container').props}>
                <div {...n('content').props}>
                    <div {...n('header').props}>
                        <h1 {...n('title').props}>{i18n.title}</h1>
                    </div>
                    <form {...n('form').props} onSubmit={handleSubmit(onSubmit)} noValidate>
                        {errorStatus && (
                            <Alert
                                alertMessage={i18n.errorMessage}
                                type={alertTypes.ERROR_ALERT}
                                onClick={() => setErrorStatus(false)}
                                scrollIntoView
                            />
                        )}
                        <Grid maxCol={columns}>
                            {firstName && renderInput(firstName)}
                            {lastName && renderInput(lastName)}
                            {email && renderInput(email)}
                            {(phone || phoneExtension) && renderPhoneBlock()}
                            {companyName && renderInput(companyName)}
                            {companySize && <Grid maxCol={columns}>{renderInput(companySize)}</Grid>}
                            {street1 && renderInput(street1)}
                            {street2 && renderInput(street2)}
                            {city && renderInput(city)}
                            <Grid maxCol={columns}>
                                {renderStateProvinceBlock()}
                                {zip && renderInput(zip)}
                            </Grid>
                            {country && renderInput(country)}
                        </Grid>
                        {notes && <div {...n('formGroup').props}>{notes && renderInput(notes)}</div>}
                        {renderCustomFields()}
                        {renderButtons}
                    </form>
                    <div {...n('closeButton').props}>
                        <Button buttonType="icon" onClick={closeForm}>
                            <span {...n('closeButtonText').props}>Close</span>
                            <span {...n('closeButtonIcon').props}>
                                <Icon type="close" width="24px" />
                            </span>
                        </Button>
                    </div>
                </div>
            </GridContainer>
        </section>
    );
};

const defaultInputShape = {
    title: PropTypes.string,
    name: PropTypes.string,
    type: PropTypes.oneOf([
        TYPE_TEXT,
        TYPE_EMAIL,
        TYPE_PHONE,
        TYPE_TEXTAREA,
        TYPE_SELECT,
        TYPE_STYLED_SELECT,
        TYPE_CHECKBOX
    ])
};

const provinceShape = PropTypes.shape({
    ...defaultInputShape,

    isRequired: PropTypes.bool,
    inline: PropTypes.bool,
    includeEmptyOption: PropTypes.bool,
    emptyOptionLabel: PropTypes.string,
    toggledBy: PropTypes.string,
    visibleOn: PropTypes.string,
    options: PropTypes.arrayOf(
        PropTypes.shape({
            label: PropTypes.string,
            value: PropTypes.string
        })
    ),
    validators: PropTypes.arrayOf(
        PropTypes.oneOfType([
            PropTypes.shape({
                type: PropTypes.string,
                conditionalField: PropTypes.string,
                requiredWhen: PropTypes.string
            }),
            PropTypes.string
        ])
    )
});

const leadFormProps = PropTypes.shape({
    actionEndpoint: PropTypes.string,
    productUrl: PropTypes.string,
    otherData: PropTypes.shape({
        profileProduct: PropTypes.shape({
            id: PropTypes.string,
            name: PropTypes.string
        }),
        partner: PropTypes.string,
        leadType: PropTypes.string
    }),
    userInfo: PropTypes.shape({
        firstName: PropTypes.string,
        lastName: PropTypes.string,
        email: PropTypes.string,
        companyName: PropTypes.string
    }),
    schema: PropTypes.shape({
        firstName: PropTypes.shape({
            ...defaultInputShape,
            isRequired: PropTypes.bool,
            validators: PropTypes.arrayOf(PropTypes.string),
            editorAttrs: PropTypes.shape({
                maxlength: PropTypes.number
            })
        }),
        lastName: PropTypes.shape({
            ...defaultInputShape,
            isRequired: PropTypes.bool,
            validators: PropTypes.arrayOf(PropTypes.string),
            editorAttrs: PropTypes.shape({
                maxlength: PropTypes.number
            })
        }),
        email: PropTypes.shape({
            ...defaultInputShape,
            isRequired: PropTypes.bool,
            validators: PropTypes.arrayOf(PropTypes.string),
            editorAttrs: PropTypes.shape({
                maxlength: PropTypes.number
            })
        }),
        phone: PropTypes.shape({
            ...defaultInputShape,
            isRequired: PropTypes.bool,
            validators: PropTypes.arrayOf(PropTypes.string),
            countryCodeValidationField: PropTypes.string
        }),
        phoneExtension: PropTypes.shape({
            ...defaultInputShape
        }),
        companyName: PropTypes.shape({
            ...defaultInputShape,
            isRequired: PropTypes.bool,
            validators: PropTypes.arrayOf(PropTypes.string),
            editorAttrs: PropTypes.shape({
                maxlength: PropTypes.number
            }),
            inline: PropTypes.bool
        }),
        companySize: PropTypes.shape({
            ...defaultInputShape,
            isRequired: PropTypes.bool,
            validators: PropTypes.arrayOf(PropTypes.string),
            includeEmptyOptions: PropTypes.bool,
            inline: PropTypes.bool,
            options: PropTypes.arrayOf(
                PropTypes.shape({
                    label: PropTypes.string,
                    value: PropTypes.string
                })
            )
        }),
        street1: PropTypes.shape(
            Object.assign(defaultInputShape, {
                isRequired: PropTypes.bool,
                validators: PropTypes.arrayOf(PropTypes.string),
                editorAttrs: PropTypes.shape({
                    maxlength: PropTypes.number
                }),
                inline: PropTypes.bool
            })
        ),
        street2: PropTypes.shape({
            ...defaultInputShape,
            editorAttrs: PropTypes.shape({
                maxlength: PropTypes.number
            }),
            inline: PropTypes.bool
        }),
        city: PropTypes.shape({
            ...defaultInputShape,
            isRequired: PropTypes.bool,
            validators: PropTypes.arrayOf(PropTypes.string),
            editorAttrs: PropTypes.shape({
                maxlength: PropTypes.number
            }),
            inline: PropTypes.bool
        }),
        state: provinceShape,
        province: provinceShape,
        zip: PropTypes.shape({
            ...defaultInputShape,
            isRequired: PropTypes.bool,
            validators: PropTypes.arrayOf(PropTypes.string),
            inline: PropTypes.bool
        }),
        country: PropTypes.shape({
            ...defaultInputShape,
            includeEmptyOption: PropTypes.bool,
            isRequired: PropTypes.bool,
            iso3: PropTypes.bool,
            validators: PropTypes.arrayOf(PropTypes.string),
            options: PropTypes.arrayOf(
                PropTypes.shape({
                    isSelected: PropTypes.bool,
                    label: PropTypes.string,
                    phoneCode: PropTypes.string,
                    value: PropTypes.string,
                    value_iso3: PropTypes.string
                })
            )
        }),
        notes: PropTypes.shape({
            ...defaultInputShape,
            editorAttrs: PropTypes.shape({
                maxlength: PropTypes.number,
                placeholder: PropTypes.string
            })
        }),
        custom: PropTypes.arrayOf(
            PropTypes.shape({
                ...defaultInputShape,
                hint: PropTypes.string,
                isRequired: PropTypes.bool,
                options: PropTypes.arrayOf(
                    PropTypes.shape({
                        label: PropTypes.string,
                        value: PropTypes.string
                    })
                ),
                validators: PropTypes.arrayOf(PropTypes.string)
            })
        )
    })
});

LeadForm.propTypes = {
    data: PropTypes.shape({
        form: leadFormProps,
        i18n: PropTypes.shape({
            title: PropTypes.string,
            cancel: PropTypes.string,
            submit: PropTypes.string,
            defaultError: PropTypes.string,
            select: PropTypes.string,
            errorMessage: PropTypes.string,
            sucessMessage: PropTypes.string
        })
    })
};

LeadForm.defaultProps = {
    data: {
        form: {
            actionEndpoint: '',
            productUrl: '',
            otherData: {
                profileProduct: {
                    id: '',
                    name: ''
                },
                partner: '',
                leadType: ''
            },
            userInfo: {
                firstName: '',
                lastName: '',
                email: '',
                companyName: ''
            },
            schema: {
                firstName: {
                    name: 'firstName',
                    title: 'First Name',
                    type: 'Text',
                    isRequired: true,
                    editorAttrs: {
                        maxlength: 50
                    },
                    validators: ['trimmedRequired']
                },
                lastName: {
                    name: 'lastName',
                    title: 'Last Name',
                    type: 'Text',
                    isRequired: true,
                    editorAttrs: {
                        maxlength: 50
                    },
                    validators: ['trimmedRequired']
                },
                email: {
                    name: 'email',
                    title: 'Email',
                    type: 'Text',
                    isRequired: true,
                    editorAttrs: {
                        maxlength: 50
                    },
                    validators: ['trimmedRequired', 'email']
                },
                phone: {
                    name: 'phone',
                    title: 'Phone',
                    type: 'Text',
                    isRequired: true,
                    countryCodeValidationField: 'country',
                    validators: ['required']
                },
                phoneExtension: {
                    name: 'phoneExtension',
                    type: 'Text',
                    title: 'Extension'
                },
                companyName: {
                    name: 'companyName',
                    title: 'Company Name',
                    validators: ['trimmedRequired'],
                    isRequired: true,
                    inline: true,
                    editorAttrs: {
                        maxlength: 50
                    }
                },
                companySize: {
                    name: 'companySize',
                    title: 'Company Size',
                    type: 'StyledSelect',
                    isRequired: true,
                    inline: true,
                    includeEmptyOption: true,
                    options: [
                        {
                            value: 'RANGE_1_9',
                            label: '1 – 9'
                        }
                    ],
                    validators: ['required']
                },
                street1: {
                    name: 'street1',
                    title: 'Street',
                    type: 'Text',
                    isRequired: true,
                    inline: true,
                    editorAttrs: {
                        maxlength: 50
                    },
                    validators: ['required']
                },
                street2: {
                    name: 'street2',
                    title: 'Suite',
                    type: 'Text',
                    inline: true,
                    editorAttrs: {
                        maxlength: 50
                    }
                },
                city: {
                    name: 'city',
                    title: 'City',
                    type: 'Text',
                    inline: true,
                    isRequired: true,
                    editorAttrs: {
                        maxlength: 50
                    },
                    validators: ['required']
                },
                state: {
                    name: 'state',
                    title: 'State',
                    type: 'Select',
                    inline: true,
                    includeEmptyOption: true,
                    emptyOptionLabel: 'Select',
                    toggledBy: 'country',
                    visibleOn: 'USA',
                    isRequired: true,
                    options: [
                        {
                            label: 'California',
                            value: 'CA'
                        }
                    ],
                    validators: [
                        {
                            type: 'conditionallyRequired',
                            conditionalField: 'country',
                            requiredWhen: 'USA'
                        }
                    ]
                },
                province: {
                    name: 'province',
                    title: 'Province',
                    type: 'Select',
                    inline: true,
                    includeEmptyOption: true,
                    emptyOptionLabel: 'Select',
                    isRequired: true,
                    toggledBy: 'country',
                    visibleOn: 'CAN',
                    options: [
                        {
                            value: 'ON',
                            label: 'Ontario'
                        }
                    ],
                    validators: [
                        {
                            type: 'conditionallyRequired',
                            conditionalField: 'country',
                            requiredWhen: 'CAN'
                        }
                    ]
                },
                zip: {
                    name: 'zip',
                    title: 'Zip Code',
                    type: 'Text',
                    inline: true,
                    isRequired: true,
                    editorAttrs: {
                        maxlength: 50
                    },
                    validators: ['required']
                },
                country: {
                    name: 'country',
                    title: 'Country',
                    type: 'Select',
                    options: [
                        {
                            isSelected: true,
                            label: 'United States',
                            phoneCode: '1',
                            value: 'US',
                            value_iso3: 'USA'
                        }
                    ],
                    validators: ['required'],
                    includeEmptyOption: true,
                    isRequired: true,
                    iso3: true
                },
                notes: {
                    name: 'notes',
                    title: 'Notes',
                    type: 'TextArea',
                    editorAttrs: {
                        maxlength: 400,
                        placeholder: 'Please specify any details'
                    }
                },
                custom: []
            }
        },
        i18n: {
            submit: 'Send',
            cancel: 'Close',
            defaultError: 'Please fill out the required fields.',
            select: 'Select'
        }
    }
};

LeadForm.schema = () => ({
    name: 'ProductLeadForm',
    title: 'uieditor.sfbComponent.leadForm.title',
    iconName: '',
    form: {},
    marketplaceData: {
        form: 'application.leads.form',
        i18n: 'application.leads.i18n'
    }
});

export default withListener(LeadForm);
