import * as React from 'react';
import { ValidationRule, ValidationState } from '../../models/Validation';
import { TooltipProps } from '../tooltip/Tooltip';
import { Colors } from '../../models/colors';
import { Query } from '../../models/query';

export interface AutoCompleteProps {
	label?: string;
	type?: string;
	name?: string;
	automationName?: string;
	placeholder?: string;
	validationRules?: Array<ValidationRule>;
	options?: Array<{ label: string; id: string }>;
	value?: string;
	query?: Query;
	valid?: ValidationState;
	required?: boolean;
	errorMessage?: string;
	palette?: Colors;
	tooltip?: React.ReactElement<TooltipProps>;
	noResults?: string;
	notifyValidity?: (name: string, state: ValidationState, errorMessage?: string) => void;
	onChange?: (name: string, value: string) => void;
	onOptionClick?: (option: any) => void;
	translator?: Function;
}

type AutocompleteContainer = AutoCompleteProps & {
	render: (
		onChange: (name: string, value: string) => void,
		onOptionClick: (option: { label: string; id: string }) => void,
		onFocus: (name: string, value: string, e?: React.FocusEvent<HTMLInputElement>) => void,
		onclick: (e: React.MouseEvent<HTMLInputElement>) => void,
		onKeyDown: (
			value: string,
			e: React.KeyboardEvent,
			option?: { label: string; id: string }
		) => void,
		value: string,
		opened: boolean,
		ref: React.RefObject<HTMLUListElement>,
		query: Query,
		close: () => void
	) => JSX.Element;
};

interface AutoCompleteState {
	value: string;
	opened: boolean;
	focusedLi: number;
}
class Autocomplete extends React.PureComponent<AutocompleteContainer, AutoCompleteState> {
	private ref: React.RefObject<HTMLUListElement>;

	constructor(props: AutocompleteContainer) {
		super(props);
		this.state = {
			value: this.props.value,
			opened: false,
			focusedLi: -1
		};

		this.ref = React.createRef();
	}

	componentDidUpdate(prevProps: AutoCompleteProps) {
		if (prevProps.options !== this.props.options) {
			this.open(false);
		}
	}

	onChange = (name: string, value: string) => {
		document.activeElement.id = this.props.name;
		this.props.onChange(name, value);
		this.setState({ value, focusedLi: -1 });
	};

	open = (forceOpen: boolean) => {
		if (this.state.opened) return;
		if (
			(this.props.options &&
				this.props.options.length > 0 &&
				document.activeElement.id === this.props.name) ||
			forceOpen
		) {
			this.setState({
				opened: true
			});
			if (this.props.query !== Query.small) {
				document.addEventListener('click', this.close);
			}
		}
	};

	close = () => {
		this.setState({
			opened: false,
			focusedLi: -1
		});
		if (this.props.query !== Query.small) {
			document.removeEventListener('click', this.close);
		}
	};

	onOptionClick = (option: { label: string; id: string }) => {
		if (this.props.onOptionClick) {
			this.props.onOptionClick(option);
		}

		this.setState({
			value: option.label
		});
		this.close();
	};

	onFocus = (name: string, value: string, e: React.FocusEvent<HTMLInputElement>) => {
		e.stopPropagation();
		if (this.props.options || this.props.query === Query.small) {
			this.open(true);
		}
	};

	onClick = (e: React.MouseEvent<HTMLInputElement>) => {
		e.preventDefault();
		e.stopPropagation();
		e.nativeEvent.stopImmediatePropagation();
	};

	onKeyDown = (value: string, e: React.KeyboardEvent, option?: { label: string; id: string }) => {
		if (e.keyCode === 40) {
			//arrow down
			e.preventDefault();
			let focusedIndex =
				this.state.focusedLi === this.props.options.length - 1 ? 0 : this.state.focusedLi + 1;
			this.ref.current.querySelectorAll('li')[focusedIndex].focus();
			this.setState({
				focusedLi: focusedIndex,
				value: this.props.options[focusedIndex].label
			});
		}
		if (e.keyCode === 38) {
			//arrow up
			e.preventDefault();
			let focusedIndex =
				this.state.focusedLi === 0 ? this.props.options.length - 1 : this.state.focusedLi - 1;
			this.ref.current.querySelectorAll('li')[focusedIndex].focus();
			this.setState({
				focusedLi: focusedIndex,
				value: this.props.options[focusedIndex].label
			});
		}

		if (e.keyCode === 13) {
			//enter
			this.onOptionClick(option);
		}
		if (e.keyCode === 9) {
			//tab
			this.close();
		}
	};

	render() {
		return this.props.render(
			this.onChange,
			this.onOptionClick,
			this.onFocus,
			this.onClick,
			this.onKeyDown,
			this.state.value,
			this.state.opened,
			this.ref,
			this.props.query,
			this.close
		);
	}
}

export default Autocomplete;
