import React, { useCallback, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import DOMPurify from 'dompurify';
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 CheckboxUncontrolled from '../../atoms/checkbox-uncontrolled/CheckboxUncontrolled';
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 FormLabel from '../../atoms/form-label/FormLabel';
import Alert, { alertTypes } from '../../atoms/alert/Alert';
import { addAlert, ALERT_LEAD_ADD_SUCCESS } from '../../tools/alerts';
import { createPostPayload, getDefaultSelectedOption, sanitizePhoneData } from '../../tools/components/leadForm';
import { createNamespace } from '../../tools/namingTools';
import { generateUniqueID, getCookieValue } from '../../tools/helpers';
import { useContainerWidth } from '../../tools/widths/useContainerWidth';
import { bMedium } from '../../constants/breakpoints';
import { leadsPropTypes } from '../../constants/prop-types/application/leads';
import { TYPE_TEXTAREA, TYPE_SELECT, TYPE_STYLED_SELECT, TYPE_CHECKBOX, NAME_COUNTRY, NAME_STATE, NAME_PROVINCE } from '../../constants/components/lead-form';
import withListener from '../withListener';
import './styles/LeadForm.scss';

const n = createNamespace('Leadform');

const LeadForm = ({
    data: {
        form: {
            actionEndpoint,
            productUrl,
            otherData,
            userAndCompanyInfo,
            schema: {
                firstName,
                lastName,
                email,
                phone,
                phoneExtension,
                companyName,
                companySize,
                street1,
                street2,
                city,
                state,
                province,
                zip,
                country,
                notes,
                custom
            }
        },
        i18n: i18nLeads
    }
}) => {
    const [currentCountry, setCurrentCountry] = useState(getDefaultSelectedOption(country));
    const [currentState, setCurrentState] = useState(getDefaultSelectedOption(state));
    const [currentProvince, setCurrentProvince] = useState(getDefaultSelectedOption(province));

    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(getDefaultSelectedOption(country));
        setCurrentState(getDefaultSelectedOption(state));
        setCurrentProvince(getDefaultSelectedOption(province));
        setSession(Date.now());
    };

    const getCountryPhoneCode = useMemo(
        () => (
            (currentCountry && currentCountry.phoneCode)
                ? `+${currentCountry.phoneCode}`
                : null),
        [currentCountry]
    );

    const onSubmit = formData => {
        const payload = createPostPayload(otherData, formData, custom);
        payload.customer.contact.phone = sanitizePhoneData(formData, getCountryPhoneCode);
        const payloadString = JSON.stringify(payload);

        const XHR = new XMLHttpRequest();
        const success = [200, 201, 202, 204];

        XHR.addEventListener('load', () => {
            if (success.includes(XHR.status)) {
                resetForm();
                addAlert(ALERT_LEAD_ADD_SUCCESS);
                window.location.assign(productUrl);
            } else {
                console.error(`Leads API error: ${XHR?.responseText} ${actionEndpoint}`);
                setErrorStatus(true);
            }
        });

        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 i18nLeadsWithDefaults = { ...LeadForm.defaultProps.data.i18n, ...i18nLeads };

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

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

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

            // TODO add custom message for maxLength error
            if (item.editorAttrs && item.editorAttrs.maxlength) {
                inputValidationRules.maxLength = {
                    value: item.editorAttrs.maxlength,
                    message: i18nLeadsWithDefaults.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: i18nLeadsWithDefaults.defaultError
                        };
                    }
                    if (validator === 'arrayRequired') {
                        inputValidationRules.required = {
                            value: item.isRequired,
                            message: i18nLeadsWithDefaults.defaultError
                        };
                    }
                });
            }

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

    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 addonLabel = '';
        let defaultValue = '';

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

        switch (name) {
            case NAME_COUNTRY: {
                defaultValue = currentCountry;
                inputUpdateFunc = setCurrentCountry;
                break;
            }
            case NAME_STATE: {
                defaultValue = currentState;
                inputUpdateFunc = setCurrentState;
                break;
            }
            case NAME_PROVINCE: {
                defaultValue = currentProvince;
                inputUpdateFunc = setCurrentProvince;
                break;
            }
            default: {
                defaultValue = userAndCompanyInfo[name];
            }
        }

        switch (type) {
            case TYPE_CHECKBOX: {
                return (
                    <div key={inputKey}>
                        <FormLabel
                            labelText={title}
                            required={isRequired}
                            labelProps={n('label').props}
                            textProps={n('text').props}
                        >
                            {item.options.map(option => (
                                <CheckboxUncontrolled
                                    key={option.value}
                                    name={name}
                                    value={option.value}
                                    label={option.label}
                                    required={isRequired}
                                    inputRef={register(validationRules)}
                                />
                            ))}
                        </FormLabel>
                        {errorMessage && (
                            <span {...n('error').props} aria-live="assertive">
                                {errorMessage}
                            </span>
                        )}
                    </div>
                );
            }
            case TYPE_TEXTAREA:
                return (
                    <Textarea
                        key={inputKey}
                        name={name}
                        label={title}
                        required={isRequired}
                        placeholder={placeholder}
                        inputRef={register(validationRules)}
                        error={errorMessage}
                    />
                );
            case TYPE_SELECT:
            case TYPE_STYLED_SELECT:
                return (
                    <Select
                        key={inputKey}
                        name={name}
                        label={title}
                        defaultValue={defaultValue}
                        required={isRequired}
                        placeholder={placeholder || i18nLeadsWithDefaults.select}
                        onChange={inputUpdateFunc}
                        options={optionList}
                        i18n={i18nLeadsWithDefaults}
                        hookFormControl={control}
                        validationRules={validationRules}
                        error={errorMessage}
                        isSearchable
                    />
                );
            default:
                return (
                    <Input
                        key={inputKey}
                        name={name}
                        label={title}
                        defaultValue={defaultValue}
                        type={type && type.toLowerCase()}
                        hint={hint}
                        i18n={i18nLeadsWithDefaults}
                        addon={addon}
                        addonLabel={addonLabel}
                        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">
                    {i18nLeadsWithDefaults.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}>
                    {i18nLeadsWithDefaults.title && (
                        <div {...n('header').props}>
                            <h2 {...n('title').props}>{i18nLeadsWithDefaults.title}</h2>
                        </div>
                    )}
                    {i18nLeadsWithDefaults.instructions && (
                        <div
                            {...n('instructions').props}
                            dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(i18nLeadsWithDefaults.instructions) }}
                        />
                    )}
                    <form {...n('form').props} onSubmit={handleSubmit(onSubmit)} noValidate>
                        {errorStatus && (
                            <Alert
                                alertMessage={i18nLeadsWithDefaults.errorMessage}
                                type={alertTypes.ERROR_ALERT}
                                onClick={() => setErrorStatus(false)}
                                scrollIntoView
                                closeAlert={i18nLeadsWithDefaults.closeAlert}
                            />
                        )}
                        <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}>{i18nLeadsWithDefaults.closeLeadForm}</span>
                            <span {...n('closeButtonIcon').props}>
                                <Icon type="close" width="24px" />
                            </span>
                        </Button>
                    </div>
                </div>
            </GridContainer>
        </section>
    );
};

LeadForm.propTypes = {
    data: PropTypes.shape(leadsPropTypes)
};

LeadForm.defaultProps = {
    data: {
        form: {
            actionEndpoint: '',
            schema: {}
        },
        i18n: {
            submit: 'Send',
            cancel: 'Close',
            defaultError: 'Please fill out the required fields.',
            select: 'Select',
            closeAlert: 'Close alert',
            closeLeadForm: 'Close lead form',
            instructions: '',
            fieldRequired: 'Required',
            phoneExtension: 'Phone Extension',
            countryCode: 'Country Code'
        }
    }
};

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

export default withListener(LeadForm);
