import * as React from 'react';
import { Validation, ValidationState } from '../../models/Validation';
import { getConfigByNames } from '../../utils/helper';
import { SecondaryButtonProps } from '../secondary-button/SecondaryButton';
import { SubmitButtonProps } from '../submit-button/SubmitButton';
import { ConfirmButtonProps } from '../confirm-button/ConfirmButton';
import { Application } from '../../models/application';
import { ServerErrors } from '../../models/serverErrors';
import { Colors } from '../../models/colors';
import SubmitButtonOne from '../../components/submit-buttons/submit-button-one/SubmitButtonOne';
import { Query } from '../../models/query';

export interface TemplateProps {
	name: string;
	moreButtons?: Array<React.ReactElement<SecondaryButtonProps>>;
	submitButton?: React.ReactElement<SubmitButtonProps | ConfirmButtonProps>;
	translator?: Function;
	checkIfTranslateExist?: Function;
}

export type TemplateContainer = TemplateProps & {
	query?: Query;
	onSubmit?: any;
	application?: Application;
	submitButton?: React.ReactElement<SubmitButtonProps | ConfirmButtonProps>;
	moreButtons?: Array<React.ReactElement<SecondaryButtonProps>>;
	palette?: Colors;
	onChange?: (name: string, value: any) => void;
	serverErrors?: ServerErrors;
	config?: { [key: string]: any };
	render?: (
		props: {
			elements: JSX.Element[];
			palette: Colors;
			onSubmit: any;
			submitButton: JSX.Element;
		}
	) => JSX.Element;
};

export interface TemplateState {
	validations: { [name: string]: Validation | any };
}

class Template extends React.PureComponent<TemplateContainer, TemplateState> {
	constructor(props: TemplateContainer) {
		super(props);

		this.state = {
			validations: {}
		};

		document.addEventListener(
			'invalid',
			(() => {
				return (e: any) => {
					e.preventDefault();
					const firstInvalid = document.querySelector('input:invalid') as HTMLInputElement;
					window.scroll({
						top:
							firstInvalid.getBoundingClientRect().top -
							document.body.getBoundingClientRect().top -
							300,
						behavior: 'smooth'
					});
					setTimeout(() => firstInvalid.focus(), 400);
				};
			})(),
			true
		);
	}

	onSubmit = (props: any) => {
		if (this.checkValidity()) {
			if (props.preventDefault) props.preventDefault();
			this.props.onSubmit(props);
		}
		return false;
	};

	checkValidity = () => {
		let valid = true;
		let validations = Object.assign({}, this.state.validations);
		for (let key in this.state.validations) {
			if (validations[key].state) {
				if (
					validations.hasOwnProperty(key) &&
					validations[key].state === ValidationState.Required
				) {
					validations[key] = Object.assign({}, validations[key], {
						state: ValidationState.Invalid
					});
					valid = false;
				}
				if (validations.hasOwnProperty(key) && validations[key].state === ValidationState.Invalid) {
					valid = false;
				}
			} else {
				let innerObj: { [key: string]: Validation } = {};
				for (let innerKey in this.state.validations[key]) {
					if (
						validations[key].hasOwnProperty(innerKey) &&
						validations[key][innerKey].state === ValidationState.Required
					) {
						innerObj[innerKey] = Object.assign(
							innerObj[innerKey] || {},
							validations[key][innerKey],
							{
								state: ValidationState.Invalid
							}
						);
						valid = false;
					}
					if (
						validations[key].hasOwnProperty(innerKey) &&
						validations[key][innerKey].state === ValidationState.Invalid
					) {
						valid = false;
					}
					validations[key] = Object.assign({}, validations[key], innerObj);
				}
			}
		}
		this.setState(prevState => {
			return { validations: Object.assign({}, prevState.validations, validations) };
		});
		return valid;
	};

	notifyValidity = (name: string, state: ValidationState, errorMessage: string) => {
		this.setState(prevState => {
			if (name.lastIndexOf('.') > 0) {
				let prefix = name.substring(0, name.lastIndexOf('.'));
				let suffix = name.substring(name.lastIndexOf('.') + 1);
				let validationObject = Object.assign({}, prevState.validations[prefix], {
					[suffix]: { state, errorMessage }
				});
				let validations = Object.assign({}, prevState.validations, {
					[prefix]: validationObject
				});
				return { validations };
			}
			let validations = Object.assign({}, prevState.validations, {
				[name]: { state, errorMessage }
			});
			return { validations };
		});
	};

	getConfigByNames = (name: string | string[]) => {
		return getConfigByNames(name, this.props.config);
	};

	render() {
		const { children, palette } = this.props;

		const childrenWithProps = React.Children.map(children, (child: any) => {
			let props = {
				notifyValidity: this.notifyValidity,
				getConfig: this.getConfigByNames,
				validationState: this.state.validations[child.props.name],
				validations: this.state.validations,
				palette,
				onChange: this.props.onChange,
				value: this.props.application[child.props.name],
				application: this.props.application,
				serverErrors: (this.props.serverErrors || {})[child.props.name],
				templateName: this.props.name,
				checkIfTranslateExist: this.props.checkIfTranslateExist,
				translator: this.props.translator,
				query: this.props.query
			};

			return React.cloneElement(child, props);
		});

		const submitButton = React.cloneElement(this.props.submitButton || <SubmitButtonOne />, {
			onSubmit: this.onSubmit,
			palette,
			checkIfTranslateExist: this.props.checkIfTranslateExist,
			translator: this.props.translator
		});
		return this.props.render({
			elements: childrenWithProps,
			palette: this.props.palette,
			onSubmit: this.onSubmit,
			submitButton
		});
	}
}

export default Template;
