import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Moment from 'moment';
import {
	Dropdown as SemanticDropdown,
	Icon as SemanticIcon,
	Input as SemanticInput,
	Label as SemanticLabel,
	Popup as SemanticPopup
} from 'semantic-ui-react';
import {
	DateInput,
	DatesRangeInput
} from 'semantic-ui-calendar-react';

import * as styles from './Style.less';

export default class Filter extends Component {
	static propTypes = {
		maxDate: PropTypes.number,
		properties: PropTypes.array.isRequired,
		options: PropTypes.object,
		property: PropTypes.string,
		value: PropTypes.string,
		operand: PropTypes.string,
		onChange: PropTypes.func.isRequired,
		onRemove: PropTypes.func.isRequired,
		index: PropTypes.number.isRequired,
		valid: PropTypes.bool
	};

	static defaultProps = {
		maxDate: null,
		options: null,
		property: null,
		value: null,
		operand: null,
		valid: false
	};

	static types = {
		id: ['eq', 'in'],
		string: ['con', 'eq', 'neq', 'in', 'nin'],
		enum: ['eq', 'neq', 'in', 'nin'],
		enumSingle: ['eq'],
		number: ['gt', 'gte', 'lt', 'lte', 'rng', 'nrng'],
		percent: ['gt', 'gte', 'lt', 'lte', 'rng', 'nrng'],
		date: ['gt', 'gte', 'lt', 'lte', 'rng', 'nrng'],
		path: ['cona']
	};

	static operands = [{
		value: 'con',
		symbol: '~=',
		text: 'contains'
	}, {
		value: 'eq',
		symbol: '=',
		text: 'is equal to'
	}, {
		value: 'neq',
		symbol: '!=',
		text: 'is not equal to'
	}, {
		value: 'gt',
		symbol: '>',
		text: 'is greater than'
	}, {
		value: 'gte',
		symbol: '>=',
		text: 'is greater than or equal to'
	}, {
		value: 'lt',
		symbol: '<',
		text: 'is less than'
	}, {
		value: 'lte',
		symbol: '<=',
		text: 'is less than or equal to'
	}, {
		value: 'in',
		symbol: '=',
		text: 'is one of'
	}, {
		value: 'nin',
		symbol: '!=',
		text: 'is not one of'
	}, {
		value: 'rng',
		symbol: '<>',
		text: 'is between'
	}, {
		value: 'nrng',
		symbol: '><',
		text: 'is not between'
	}, {
		value: 'cona',
		symbol: '%',
		text: 'contains'
	}];

	static getOperand(operand) {
		return Filter.operands.find(o => o.value === operand);
	}

	static rangeValue(operand) {
		return operand === 'rng' || operand === 'nrng';
	}

	static formatDate(value) {
		if (value.indexOf('/') === -1) {
			if (value.indexOf(',') > -1) {
				const dateStrings = value.split(',');

				return `${Moment.utc(parseInt(dateStrings[0], 10)).format('L')} - ${Moment.utc(parseInt(dateStrings[1], 10)).format('L')}`;
			}

			return Moment.utc(parseInt(value, 10)).format('L');
		}

		return value;
	}

	constructor(props) {
		super(props);

		const { type } = this.props.properties.find(prop => prop.value === this.props.property);
		const rangeValue = Filter.rangeValue(this.props.operand);

		let { value } = this.props;

		if (!value || value === '') {
			value = this.defaultValue(type, this.props.property, this.props.operand, rangeValue);
		} else if (type === 'date') {
			value = Filter.formatDate(value);
		}

		this.state = {
			operand: this.props.operand || Filter.types[type][0],
			property: this.props.property,
			rangeValue,
			type,
			value
		};
	}

	componentWillReceiveProps(nextProps) {
		const { type } = nextProps.properties.find(prop => prop.value === nextProps.property);
		const rangeValue = Filter.rangeValue(nextProps.operand);

		let { value } = nextProps;

		if (!value || value === '') {
			value = this.defaultValue(type, nextProps.property, nextProps.operand, rangeValue);
		} else if (type === 'date') {
			value = Filter.formatDate(value);
		}

		this.setState({
			property: nextProps.property,
			operand: nextProps.operand || Filter.types[type][0],
			value,
			type,
			rangeValue
		});
	}

	onRemove() {
		this.props.onRemove(this.props.index);
	}

	onSelectProperty(property) {
		const { type } = this.props.properties.find(prop => prop.value === property);
		const operand = Filter.types[type][0];
		const rangeValue = Filter.rangeValue(operand);
		const value = this.defaultValue(type, property, operand, rangeValue);

		this.setState({
			property,
			operand,
			value,
			type,
			rangeValue
		}, this.broadcastChange);
	}

	onSelectOperand(operand) {
		const rangeValue = Filter.rangeValue(operand);
		const value = this.defaultValue(this.state.type, this.state.property, this.state.operand, rangeValue);

		this.setState(() => ({
			operand,
			value,
			rangeValue
		}), this.broadcastChange);
	}

	onValueChange(value) {
		if (this.state.type === 'enum' && (this.state.operand === 'in' || this.state.operand === 'nin')) {
			value = value.filter(v => v.length).map(v => `"${v}"`).join(',');
		} else if (this.state.type === 'path' && this.state.operand === 'cona') {
			value = value.filter(v => v.length).join(',');
		}
		this.setState({	value }, this.broadcastChange);
	}

	broadcastChange() {
		const { type } = this.props.properties.find(prop => prop.value === this.state.property);
		let { value } = this.state;

		if (type === 'date') {
			if (this.state.rangeValue) {
				const dateStrings = value.split(' - ');
				const dateStart = Moment.utc(dateStrings[0], 'L').startOf('day');
				const dateEnd = Moment.utc(dateStrings[1], 'L').endOf('day');

				let dateStartString = 'Start Date';
				let dateEndString = 'End Date';

				if (dateStart.isValid()) {
					dateStartString = dateStart.format('x');

					if (dateEnd.isValid()) {
						dateEndString = dateEnd.format('x');
					} else {
						dateEndString = Moment.utc(dateStrings[0], 'L').endOf('day').format('x');
					}
				}
				value = `${dateStartString},${dateEndString}`;
			} else {
				value = Moment.utc(value, 'L').format('x');
			}
		}

		this.props.onChange({
			property: this.state.property,
			operand: this.state.operand,
			value,
			type,
			index: this.props.index
		});
	}

	defaultValue(type, property, operand, rangeValue) {
		const value = '';

		if (type === 'date') {
			const dateStart = Moment.utc(this.state.maxDate).subtract(30, 'days').startOf('day').format('L');

			if (rangeValue) {
				const dateEnd = Moment.utc(this.state.maxDate).format('L');
				return `${dateStart} - ${dateEnd}`;
			}

			return dateStart;
		}

		return value;
	}

	render() {
		const {
			maxDate,
			options,
			properties,
			valid
		} = this.props;

		const {
			operand,
			property,
			rangeValue,
			type,
			value
		} = this.state;

		let enumType = false;
		let enumOptions;
		let dropdownValue = value;

		if (type === 'enum' || type === 'enumSingle' || type === 'path') {
			enumType = true;
			enumOptions = options[property];

			if (operand === 'in' || operand === 'nin') {
				dropdownValue = value.split('",').map(v => v.replace(/^"|"$/g, ''));
			} else if (operand === 'cona') {
				dropdownValue = value.split(',');
			}
		}

		let dateValue = value;
		let dateInputValue = value;
		let dateSelected = value;

		if (type === 'date' && rangeValue) {
			const dateStrings = value.split(' - ');
			const dateStart = Moment.utc(dateStrings[0], 'L');
			const dateEnd = Moment.utc(dateStrings[1], 'L');
			let dateStartString = 'Start date';
			let dateEndString = 'End date';
			if (dateStart.isValid()) {
				dateStartString = dateStart.format('L');

				if (dateEnd.isValid()) {
					dateEndString = dateEnd.format('L');
				}
			}

			dateValue = `${dateStartString} - ${dateEndString}`;

			if (dateStartString === dateEndString) {
				dateEndString = '';
			}
			dateInputValue = `${dateStartString} - ${dateEndString}`;

			dateSelected = dateStrings[1]; // eslint-disable-line prefer-destructuring
		}

		if (!dateSelected || dateSelected.length === 0 || dateSelected.indexOf('/') === -1) {
			dateSelected = Moment.utc(maxDate).format('L');
		}

		return (
			<SemanticLabel className={valid ? '' : 'invalid'}>
				<SemanticDropdown
					selection
					selectOnBlur={false}
					selectOnNavigation={false}
					options={properties.map(o => ({ value: o.value, text: o.text, active: o.value === property }))}
					placeholder="Field"
					value={property}
					onChange={(e, { value: val }) => this.onSelectProperty(val)}
				/>
				<SemanticDropdown
					selection
					selectOnBlur={false}
					selectOnNavigation={false}
					options={Filter.types[type].map(o => Filter.getOperand(o)).map(o_ => ({ ...o_, active: o_.value === operand }))}
					placeholder="Operator"
					value={operand}
					onChange={(e, { value: val }) => this.onSelectOperand(val)}
				/>
				{
					enumType && (
						<SemanticDropdown
							selection
							selectOnBlur={false}
							selectOnNavigation={false}
							multiple={operand === 'in' || operand === 'nin' || operand === 'cona'}
							search
							options={enumOptions.map(o => ({ ...o, active: o.value === value }))}
							placeholder="Value"
							value={dropdownValue}
							onChange={(e, { value: val }) => this.onValueChange(val)}
						/>
					)
				}
				{
					type === 'date' && (
						<SemanticPopup
							className={styles.Popup}
							on="click"
							position="bottom center"
							basic
							trigger={(
								<div className="dateLabel">
									<SemanticLabel>{dateValue}</SemanticLabel>
									<SemanticIcon name="caret down" className="openDateInput" />
								</div>
							)}
							content={
								rangeValue ? (
									<DatesRangeInput
										inline
										name="date"
										dateFormat="L"
										maxDate={Moment.utc(maxDate)}
										placeholder="Date"
										value={dateInputValue}
										initialDate={dateSelected}
										onChange={(e, { value: val }) => this.onValueChange(val)}
									/>
								) : (
									<DateInput
										inline
										name="date"
										dateFormat="L"
										maxDate={Moment.utc(maxDate)}
										placeholder="Date"
										value={value}
										initialDate={dateSelected}
										onChange={(e, { value: val }) => this.onValueChange(val)}
									/>
								)
							}
						/>
					)
				}
				{
					!enumType && type !== 'date' && (
						<SemanticInput
							placeholder="Value"
							value={value}
							onChange={(e, { value: val }) => this.onValueChange(val)}
						/>
					)
				}
				<SemanticIcon
					name="times"
					onClick={() => this.onRemove()}
				/>
			</SemanticLabel>
		);
	}
}
