import React, { useEffect, useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { useForm } from 'react-hook-form';
import { createNamespace } from '../../tools/namingTools';
import { CTA_DOMAIN_ADD_TO_CART, CTA_DOMAIN_BUY_NOW } from '../../constants/actions';
import { useResponsive } from '../../tools/widths/useResponsive';
import { domainAppDataStore } from '../../store/domainApp';

import Preloader from '../../atoms/preloader/Preloader';
import ButtonIcon from '../../atoms/button-icon/ButtonIcon';
import Input from '../../atoms/input/Input';
import Icon from '../../atoms/icon/Icon';

import './styles/DomainApp.scss';

import withListener from '../withListener';

const n = createNamespace('DomainApp');

const ADD_ACTION = 'ADD';
const REMOVE_ACTION = 'REMOVE';
const SELECTED_ACTION = 'SELECTED';

const ALL_COLUMNS_SPAN = 3;
const SINGLE_COLUMN_SPAN = 1;

const renderTableName = name => (
    <span {...n('name').props}>{name}</span>
);

const renderTablePrice = (price, frequency) => {
    if (!price) {
        return '';
    }

    return (
        <span {...n('price').props}>
            <strong>{price}</strong>
            {` / ${frequency}`}
        </span>
    );
};

const renderTableAction = (label, icon, theme, isDisabled, clickCallback = () => {}, name) => {
    const buttonProps = n('actionButton', icon).withTestId(name).props;
    return (
        <span {...n('action').props}>
            <ButtonIcon
                {...buttonProps}
                icon={icon}
                iconClass={n('actionIcon', icon).props.className}
                theme={theme}
                extraClass={buttonProps.className}
                onClick={clickCallback}
                disabled={isDisabled}
            >
                {label}
            </ButtonIcon>
        </span>
    );
};

export const DomainAppComponent = ({
    data: {
        application,
        config
    },
    fetchSuggestions
}) => {
    const appId = application.config && application.config.id;
    const canRouteToV2 = config && config.canRouteToV2;
    const { i18n } = application.domains;
    const { smallDown, largeDown } = useResponsive();

    const [searchValue, setSearchValue] = useState(null);
    const [suggestions, setSuggestions] = useState([]);
    const [isLoading, setIsLoading] = useState(false);
    const { handleSubmit, register } = useForm();

    const onSubmit = formData => {
        setSearchValue(formData.domain);
    };

    const {
        selectedDomains,
        addDomain,
        removeDomain,
        initializeDomains,
        setCallToActions,
        handleBuyNowDomains,
        handleAddToCartDomains,
        callToActions
    } = domainAppDataStore();

    useEffect(() => {
        initializeDomains(appId);
    }, [appId, initializeDomains]);

    const getCheckoutUrl = useCallback(() => {
        const isLoggedIn = config.user && config.user.isLoggedIn;
        const { checkoutUrl, loginUrl } = application.domains;
        return isLoggedIn ? checkoutUrl : loginUrl + checkoutUrl;
    }, [config.user, application.domains]);

    const hasDomains = selectedDomains && selectedDomains.length > 0;
    useEffect(() => {
        if (canRouteToV2) {
            setCallToActions([
                {
                    label: i18n.addToCart,
                    disabled: !hasDomains,
                    actionType: CTA_DOMAIN_ADD_TO_CART,
                    theme: 'emphasisAlt',
                    icon: 'cart',
                    name: 'addToCartButton'
                },
                {
                    label: i18n.buyNow,
                    disabled: !hasDomains,
                    actionType: CTA_DOMAIN_BUY_NOW,
                    theme: 'emphasis',
                    name: 'buyNowButton'
                }
            ]);
        } else {
            setCallToActions([
                {
                    label: i18n.checkout,
                    disabled: !hasDomains,
                    theme: 'primary',
                    icon: 'cart',
                    url: getCheckoutUrl(),
                    name: 'checkoutButton'
                }
            ]);
        }
    }, [canRouteToV2, hasDomains, setCallToActions, i18n, getCheckoutUrl]);

    const finalFetch = useCallback(params => {
        if (fetchSuggestions) {
            return fetchSuggestions(params);
        }
        const { endpoint, options } = application.domains;
        const parsedOptions = Object.keys(options).reduce((builtParams, paramName) => {
            const paramValue = params[paramName] || options[paramName];
            return [
                ...builtParams,
                `${paramName}=${paramValue}`
            ];
        }, []).join('&');
        const finalEndpoint = `${endpoint}?${parsedOptions}`;
        return fetch(finalEndpoint).then(res => res.json());
    }, [application.domains, fetchSuggestions]);

    // perform ajax fetch whenever we click on search and thus change de search value.
    useEffect(
        () => {
            if (!searchValue) {
                setSuggestions([]);
                return undefined;
            }

            // keep a closure var to invalidate all effects of this call when a subsequent
            // call is made, or component unmounts
            let isCancelled = false;
            setIsLoading(true);
            const onResolved = finalFetch({ q: searchValue });
            onResolved.then(result => {
                // this effect has been invalidated, do nothing
                if (isCancelled) {
                    return;
                }
                setIsLoading(false);
                setSuggestions(result);
            });
            return () => {
                isCancelled = true;
            };
        },
        [searchValue, finalFetch]
    );

    const defaultSearchValue = '';

    const hasDomainSuggestions = Boolean(suggestions && suggestions.length);
    const hasSelectedNames = Boolean(selectedDomains && selectedDomains.length);
    // on mobile devices, the call to actions are in the tabs menu only.
    const hasCallToActions = !largeDown;

    const renderTableRow = (item, action) => {
        const { name, formattedPricePerYear } = item;
        const isAddState = action === ADD_ACTION;
        const isRemoveState = action === REMOVE_ACTION;
        const isSelectedState = action === SELECTED_ACTION;

        // Set Button label based on the row action
        const defaultButtonLabel = isRemoveState ? i18n.removeDomain : i18n.select;
        const buttonLabel = isSelectedState ? i18n.selected : defaultButtonLabel;

        // Set Button icon based on the row action
        const buttonIcon = isSelectedState ? 'check' : null;
        let buttonStyle;
        switch (action) {
            case REMOVE_ACTION:
                buttonStyle = 'secondary';
                break;
            case SELECTED_ACTION:
                buttonStyle = 'emphasis-outline';
                break;
            case ADD_ACTION:
            default:
                buttonStyle = 'primary';
        }

        const renderedTableName = renderTableName(name);
        const renderedPricing = renderTablePrice(formattedPricePerYear, i18n.frequency);
        const renderedAction = renderTableAction(
            buttonLabel,
            buttonIcon,
            buttonStyle,
            isSelectedState,
            isAddState ? () => addDomain(item) : () => removeDomain(item),
            `${action.toLowerCase()}-${name}`
        );

        return (
            <tr {...n('tableRow').props} key={name}>
                {smallDown ? (
                    <td {...n('tableCell').props}>
                        <span {...n('tableContent').props}>
                            {renderedTableName}
                            {renderedPricing}
                            {renderedAction}
                        </span>
                    </td>
                ) : (
                    <React.Fragment>
                        <td {...n('tableCell', 'name').props}>
                            {renderedTableName}
                        </td>
                        <td {...n('tableCell', 'price').props}>
                            {renderedPricing}
                        </td>
                        <td {...n('tableCell', 'action').props}>
                            {renderedAction}
                        </td>
                    </React.Fragment>
                )}
            </tr>
        );
    };

    const renderRecommendDomains = hasDomainSuggestions ? suggestions.map(suggestion => {
        const isDomainSelected = (
            selectedDomains && (selectedDomains.findIndex(item => item.name === suggestion.name) !== -1)
        );
        const action = isDomainSelected ? SELECTED_ACTION : ADD_ACTION;

        return renderTableRow(suggestion, action);
    }) : (
        <tr {...n('tableRow').props}>
            <td {...n('tableCell', 'placeholder').props}>{i18n.recommendedPlaceholder}</td>
        </tr>
    );

    const renderAddedDomains = hasSelectedNames ? selectedDomains.map(
        selectedDomain => renderTableRow(selectedDomain, REMOVE_ACTION)
    ) : (
        <tr {...n('tableRow').props}>
            <td {...n('tableCell', 'placeholder').props}>{i18n.addedPlaceholder}</td>
        </tr>
    );

    const renderCta = item => {
        const { actionType, url, name, label } = item;
        let action;
        switch (actionType) {
            case CTA_DOMAIN_BUY_NOW:
                action = handleBuyNowDomains;
                break;
            case CTA_DOMAIN_ADD_TO_CART:
                action = handleAddToCartDomains;
                break;
            default:
        }

        const buttonProps = {
            ...item,
            onClick: action,
            url: !action ? url : null,
            extraClass: n('footerButton').props.className,
            ...n(name).withTestId().props
        };

        return (
            <div {...n('footerItem').props} key={name}>
                <ButtonIcon {...buttonProps}>
                    {label}
                </ButtonIcon>
            </div>
        );
    };

    return (
        <section {...n('wrapper').props}>
            <h1>{i18n.sectionTitle}</h1>
            <form {...n('form').props} onSubmit={handleSubmit(onSubmit)} noValidate>
                <div {...n('search').props}>
                    <Input
                        name="domain"
                        label={i18n.search}
                        placeholder={i18n.search}
                        defaultValue={defaultSearchValue}
                        hiddenLabel
                        type="search"
                        inputRef={register({})}
                        {...n('searchInput').withTestId().props}
                    />
                </div>
                <div {...n('submit').props}>
                    <ButtonIcon size="input" theme="primary" action="submit" {...n('searchButton').withTestId().props}>
                        {smallDown ? (
                            <React.Fragment>
                                <Icon {...n('submitIcon').props} type="search" />
                                <span {...n('submitText').props}>{i18n.search}</span>
                            </React.Fragment>
                        ) : i18n.search}
                    </ButtonIcon>
                </div>
            </form>
            <div {...n('result').props}>
                <div {...n('resultItem').props}>
                    <table {...n('table').props}>
                        <thead>
                            <tr>
                                <th {...n('tableCell', 'heading').props} colSpan={hasDomainSuggestions ? ALL_COLUMNS_SPAN : SINGLE_COLUMN_SPAN}>
                                    <h2 {...n('tableTitle').props}>{i18n.recommendedTitle}</h2>
                                </th>
                            </tr>
                        </thead>
                        <tbody {...n('tableBody', 'suggestions').withTestId('suggestions').props}>{renderRecommendDomains}</tbody>
                    </table>
                </div>
                <div {...n('resultItem').props}>
                    <table {...n('table').props}>
                        <thead>
                            <tr>
                                <th {...n('tableCell', 'heading').props} colSpan={hasSelectedNames ? ALL_COLUMNS_SPAN : SINGLE_COLUMN_SPAN}>
                                    <h2 {...n('tableTitle').props}>{i18n.addedTitle}</h2>
                                </th>
                            </tr>
                        </thead>
                        <tbody {...n('tableBody', 'selected').withTestId('selected').props}>{renderAddedDomains}</tbody>
                    </table>
                </div>
            </div>
            {hasCallToActions ? (
                <div {...n('footer').props}>
                    {callToActions && callToActions.map(renderCta)}
                </div>
            ) : null}
            <Preloader isLoading={isLoading} />
        </section>
    );
};

DomainAppComponent.schema = () => ({
    name: 'Domain App',
    title: 'uieditor.sfbComponent.domainApp.title',
    iconName: '',
    componentTitleKey: 'domainAppTitle',
    form: {
    },
    marketplaceData: {
        application: 'application',
        config: 'config'
    }
});

DomainAppComponent.propTypes = {
    data: PropTypes.shape({
        config: PropTypes.shape({
            user: PropTypes.shape({
                isLoggedIn: PropTypes.bool
            }),
            canRouteToV2: PropTypes.bool
        }),
        application: PropTypes.shape({
            config: PropTypes.shape({
                id: PropTypes.number
            }),
            domains: PropTypes.shape({
                endpoint: PropTypes.string,
                options: PropTypes.shape({
                    q: PropTypes.string,
                    max: PropTypes.number,
                    currency: PropTypes.string,
                    domainAvailable: PropTypes.bool
                }),
                loginUrl: PropTypes.string,
                checkoutUrl: PropTypes.string,
                i18n: PropTypes.object
            })
        })
    }),
    fetchSuggestions: PropTypes.func
};

DomainAppComponent.defaultProps = {
    data: {
        config: {
            canRouteToV2: true
        },
        application: {
            config: {
                id: null
            },
            domains: {
                i18n: {
                    sectionTitle: 'Search for your domain',
                    search: 'Search Domains',
                    recommendedTitle: 'Recommended Domains',
                    recommendedPlaceholder: 'Domains you search for will appear here',
                    addedTitle: 'Added Domains',
                    addedPlaceholder: 'Selected domains will appear here',
                    checkout: 'Continue Checkout',
                    frequency: 'Year',
                    removeDomain: 'Remove',
                    select: 'Select',
                    selected: 'Selected',
                    addToCart: 'Add to Cart',
                    buyNow: 'Buy Now'
                }
            }
        }
    },
    fetchSuggestions: null
};

export default withListener(DomainAppComponent);
