import React from 'react';
import castArray from 'lodash/castArray';
import { Search } from '@wix/wix-ui-icons-common';
import Input from '../Input';
import InputWithOptions from '../InputWithOptions';
import { google2address, includes, trySetStreetNumberIfNotReceived, } from './google2address';
import { classes } from './GoogleAddressInput.st.css.js';
import { IconThemeContext } from '../WixDesignSystemIconThemeProvider/IconThemeContext';
export const GoogleAddressInputHandler = {
    geocode: 'geocode',
    places: 'places',
};
/**
 * Address input box (using Google Maps)
 */
class GoogleAddressInput extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            suggestions: [],
            value: props.value || '',
        };
        this.autoCompleteRequestId = 0;
        this.geocodeRequestId = 0;
        this.client = new props.Client();
        this.onChange = this.onChange.bind(this);
        this.onBlur = this.onBlur.bind(this);
        this.onFocus = this.onFocus.bind(this);
        this.onSet = this.onSet.bind(this);
        this.onManuallyInput = this.onManuallyInput.bind(this);
    }
    UNSAFE_componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.props.value) {
            this._getSuggestions(nextProps.value)
                .then(suggestions => {
                this.setState({ suggestions });
            })
                .catch(() => {
                // Nothing really to do...
                this.setState({ suggestions: [] });
            });
        }
    }
    render() {
        const { suggestions, value } = this.state;
        const { magnifyingGlass } = this.props;
        const options = [
            ...suggestions.map((suggestion, index) => {
                const { place_id, description } = suggestion;
                return { id: place_id || index, value: description };
            }),
            ...(this.props.footer
                ? [
                    {
                        id: suggestions.length,
                        value: this.props.footer,
                        ...this.props.footerOptions,
                    },
                ]
                : []),
        ];
        return (React.createElement(IconThemeContext.Consumer, null, ({ icons = {} }) => {
            const SearchIcon = icons.GoogleAddressInput?.Search || Search;
            const suffix = magnifyingGlass ? (React.createElement(Input.IconAffix, null,
                React.createElement(SearchIcon, { "data-hook": "search-icon" }))) : undefined;
            return (React.createElement("div", null,
                React.createElement(InputWithOptions, { ref: autocomplete => {
                        this.autocomplete = autocomplete;
                    }, ...this.props, onInput: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onSelect: option => this.onSet(option.value), onManuallyInput: this.onManuallyInput, value: value, options: options, fixedFooter: suggestions.length && this.props.poweredByGoogle
                        ? GoogleAddressInput.getGoogleFooter()
                        : null, suffix: suffix, selectedHighlight: false, menuArrow: false })));
        }));
    }
    focus() {
        this.autocomplete.focus();
    }
    select() {
        this.autocomplete.select();
    }
    onChange(e) {
        const value = e.target.value;
        this.props.onChange && this.props.onChange(e);
        this.props.onSet && this.props.onSet(null);
        if (typeof this.props.value !== 'undefined') {
            // Controlled mode
            return;
        }
        this._getSuggestions(value)
            .then(suggestions => {
            this.setState({ suggestions });
        })
            .catch(() => {
            // Nothing really to do...
            this.setState({ suggestions: [] });
        });
    }
    onBlur() {
        this.props.onBlur && this.props.onBlur();
        if (this.props.clearSuggestionsOnBlur) {
            this.timer = setTimeout(() => {
                this.setState({ suggestions: [] });
            }, 250);
        }
    }
    onFocus() {
        this.props.onFocus && this.props.onFocus();
    }
    onSet(value) {
        const { countryCode, handler } = this.props;
        const suggestion = this.state.suggestions.find(s => s.description === value);
        this.setState({ suggestions: [], value: this.props.value || value });
        const requestId = ++this.geocodeRequestId;
        let handlerCall;
        if (handler === GoogleAddressInputHandler.places &&
            suggestion &&
            suggestion.place_id) {
            const request = {
                request: {
                    placeId: suggestion.place_id,
                },
            };
            if (this.props.placeDetailsFields) {
                request.request.fields = this.props.placeDetailsFields;
            }
            handlerCall = this.client.placeDetails(request);
        }
        else {
            handlerCall = this.client.geocode({
                request: {
                    region: countryCode,
                    [suggestion ? 'placeId' : 'address']: suggestion
                        ? suggestion.place_id
                        : value,
                },
            });
        }
        handlerCall
            .then(results => {
            results = castArray(results).filter(Boolean);
            if (requestId !== this.geocodeRequestId) {
                return;
            }
            if (results.length === 0) {
                console.error(`[GoogleAddressInput] handler (${handler}) returned no results on`, value);
                this.props.onSet && this.props.onSet(null);
                // This shouldn't happen since we're running geocode on exactly the same
                // value returned by suggestions list
                return;
            }
            const firstResult = trySetStreetNumberIfNotReceived(results[0], this.state.value);
            const result = {
                originValue: value,
                googleResult: firstResult,
                address: google2address(firstResult),
            };
            this.props.onSet && this.props.onSet(result);
        })
            .catch(e => {
            console.error(`[GoogleAddressInput] handler (${handler}) failed on`, value, e.message);
            this.props.onSet && this.props.onSet(null);
        });
    }
    onManuallyInput(inputValue) {
        const { value, fallbackToManual, onSet } = this.props;
        if (fallbackToManual) {
            this._getSuggestions(inputValue, typeof value !== 'undefined').then(suggestions => {
                if (suggestions.length === 0) {
                    // No suggestion to the text entered
                    if (inputValue) {
                        this.onSet(inputValue);
                    }
                    else {
                        onSet && onSet(null);
                    }
                }
            });
        }
    }
    componentWillUnmount() {
        if (this.timer) {
            clearTimeout(this.timer);
        }
    }
    _getSuggestions(value, skipSetState) {
        const { valuePrefix = '', countryCode, types, filterTypes } = this.props;
        const requestId = ++this.autoCompleteRequestId;
        return new Promise(resolve => {
            if (skipSetState) {
                // Controlled mode
                resolve();
                return;
            }
            this.setState({ value }, () => resolve());
        })
            .then(() => {
            if (value === '') {
                return Promise.resolve([]);
            }
            const request = {
                types,
                componentRestrictions: { country: countryCode },
                input: valuePrefix + value,
            };
            return this.client.autocomplete({ request });
        })
            .then(results => {
            if (results.length === 0) {
                return Promise.resolve([]);
            }
            if (requestId !== this.autoCompleteRequestId) {
                return Promise.resolve([]);
            }
            if (filterTypes) {
                results = results.filter(result => includes(result.types, filterTypes));
            }
            return Promise.resolve(results);
        });
    }
}
GoogleAddressInput.getGoogleFooter = () => (React.createElement("div", { className: classes.googleFooter, "data-hook": "google-footer" }));
GoogleAddressInput.displayName = 'GoogleAddressInput';
GoogleAddressInput.defaultProps = {
    magnifyingGlass: true,
    autoSelect: true,
    footerOptions: {},
    clearSuggestionsOnBlur: true,
    fallbackToManual: false,
    poweredByGoogle: false,
    handler: GoogleAddressInputHandler.geocode,
};
export default GoogleAddressInput;
//# sourceMappingURL=GoogleAddressInput.js.map