import {
	consoleInfo,
	getNode,
	objectToAttributes,
	spacerClasses,
	trigger,
	uniqueId,
} from '@gravityforms/utils';

/**
 * @function helpTextTemplate
 * @description The markup for form field helper text.
 *
 * @since 1.1.18
 *
 * @param {string} id       Optional id. Auto generated if not passed.
 * @param {string} helpText Optional helper text.
 *
 * @return {string}
 */
export const helpTextTemplate = (
	helpText = '',
	id = '',
) => {
	const componentProps = objectToAttributes( {
		class: [
			'gform-input-help-text',
		],
		id: `${ id }-help-text`,
	} );

	return `
		<span ${ componentProps }>
			${ helpText }
		</span>
	`;
};

/**
 * @function labelTemplate
 * @description The markup for form field label.
 *
 * @since 1.1.18
 *
 * @param {string}  id                    Optional id. Auto generated if not passed.
 * @param {string}  label                 Label for the textarea.
 * @param {object}  labelCustomAttributes Any custom attributes for the label.
 * @param {Array}   labelCustomClasses    Any custom classes for the label.
 * @param {boolean} labelVisible          If the label should be visible. Defaults to true.
 *
 * @return {string}
 */
export const labelTemplate = (
	id = '',
	label = '',
	labelCustomAttributes = {},
	labelCustomClasses = [],
	labelVisible = true,
) => {
	const componentProps = objectToAttributes( {
		class: [
			'gform-label',
			'gform-input__label',
			'gform-input__label--textarea',
			! labelVisible ? 'gform-visually-hidden' : '',
			...labelCustomClasses,
		],
		for: id,
		...labelCustomAttributes,
	} );

	return `
		<label ${ componentProps }>
			${ label }
		</label>
	`;
};

/**
 * @function textareaTemplate
 * @description The markup for a textarea.
 *
 * @since 1.1.18
 *
 * @requires labelTemplate as labelTemplate
 * @requires helpTextTemplate as helpTextTemplate
 *
 * @param {object}                     options                         The options for the component template.
 * @param {object}                     options.customAttributes        Any custom attributes for the textarea.
 * @param {Array}                      options.customClasses           Any custom classes for the textarea.
 * @param {string}                     options.helpText                Optional helper text.
 * @param {string}                     options.id                      Optional id. Auto generated if not passed.
 * @param {string}                     options.label                   Label for the textarea.
 * @param {object}                     options.labelCustomAttributes   Any custom attributes for the label.
 * @param {Array}                      options.labelCustomClasses      Any custom classes for the label.
 * @param {boolean}                    options.labelVisible            If the label should be visible. Defaults to true.
 * @param {boolean}                    options.disabled                If the textarea should be disabled.  Defaults to false.
 * @param {string}                     options.placeholder             Optional placeholder text.
 * @param {string|number|Array|object} options.spacing                 The spacing for the component, string, number, object or array.
 * @param {string}                     options.theme                   Theme for the toggle, primary or cosmos.
 * @param {string}                     options.value                   Optional initial value for the textarea.
 * @param {object}                     options.wrapperCustomAttributes Any custom attributes for the textarea wrapper.
 * @param {Array}                      options.wrapperCustomClasses    Any custom classes for the textarea wrapper.
 * @param {string}                     options.wrapperTagName          Tag to use for the textarea wrapper. Defaults to 'div',
 *
 * @return {string}
 * @example
 * import { textareaTemplate } from '@gravityforms/components/html/admin/elements/Textarea';
 *
 * function Example() {
 *      const textareaHTML = textareaTemplate( options );
 *      document.body.insertAdjacentHTML( 'beforeend', textareaHTML );
 * }
 *
 */
export const textareaTemplate = ( {
	customAttributes = {},
	customClasses = [],
	helpText = '',
	id = '',
	label = '',
	labelCustomAttributes = {},
	labelCustomClasses = [],
	labelVisible = true,
	disabled = false,
	placeholder = '',
	spacing = '',
	theme = 'cosmos',
	value = '',
	wrapperCustomAttributes = {},
	wrapperCustomClasses = [],
	wrapperTagName = 'div',
} ) => {
	const wrapperProps = objectToAttributes( {
		class: [
			'gform-input-wrapper',
			`gform-input-wrapper--theme-${ theme }`,
			...Object.keys( spacerClasses( spacing ) ),
			...wrapperCustomClasses,
		],
		...wrapperCustomAttributes,
	} );
	const componentProps = {
		class: [
			'gform-input',
			'gform-input--textarea',
			...customClasses,
		],
		id,
		name: id,
		placeholder,
		...customAttributes,
	};
	if ( helpText ) {
		componentProps[ 'aria-describedby' ] = `${ id }-help-text`;
	}
	if ( disabled ) {
		componentProps.disabled = 'disabled';
	}
	const labelHtml = labelTemplate(
		id,
		label,
		labelCustomAttributes,
		labelCustomClasses,
		labelVisible,
	);
	const helpTextHtml = helpTextTemplate(
		helpText,
		id,
	);

	return `
		<${ wrapperTagName } ${ wrapperProps }>
			${ label && labelHtml }
			<textarea ${ objectToAttributes( componentProps ) }>${ value }</textarea>
			${ helpText && helpTextHtml }
		</${ wrapperTagName }>
	`;
};

/**
 * @class Textarea
 * @description A textarea component to use wherever textarea form fields are needed.
 *
 * @since 1.1.18
 *
 * @borrows textareaTemplate as textareaTemplate
 *
 * @param {object}  options                         The options for the component.
 * @param {object}  options.customAttributes        Any custom attributes for the textarea.
 * @param {Array}   options.customClasses           Any custom classes for the textarea.
 * @param {string}  options.helpText                Optional helper text.
 * @param {string}  options.id                      Optional id. Auto generated if not passed.
 * @param {string}  options.label                   Label for the textarea.
 * @param {object}  options.labelCustomAttributes   Any custom attributes for the label.
 * @param {Array}   options.labelCustomClasses      Any custom classes for the label.
 * @param {boolean} options.labelVisible            If the label should be visible. Defaults to true.
 * @param {string}  options.placeholder             Optional placeholder text.
 * @param {string}  options.rendered                Is the component already rendered in the dom, eg by php?
 * @param {string}  options.renderOnInit            Render the component on init of the class?
 * @param {string}  options.spacing                 Spacing for the component.
 * @param {string}  options.target                  The target to render to. Any valid css selector string.
 * @param {string}  options.targetPosition          The insert position for the component relative to the target.
 * @param {string}  options.theme                   Theme for the component, primary or cosmos.
 * @param {string}  options.value                   Optional initial value for the textarea.
 * @param {object}  options.wrapperCustomAttributes Any custom attributes for the textarea wrapper.
 * @param {Array}   options.wrapperCustomClasses    Any custom classes for the textarea wrapper.
 * @param {string}  options.wrapperTagName          Tag to use for the textarea wrapper. Defaults to 'div',
 *
 * @return {Class} The class instance.
 * @example
 * import Textarea from '@gravityforms/components/html/admin/elements/Textarea';
 *
 * function Example() {
 *      const textareaInstance = new Textarea( {
 *          id: 'example-textarea',
 *          renderOnInit: false,
 *          target: '#example-target',
 *          targetPosition: 'beforeend',
 *          theme: 'cosmos',
 *      } );
 *
 *      // Some time later we can render it. This is only done if we set renderOnInit to false.
 *      // If true it will render on initialization.
 *      textareaInstance.init();
 * }
 *
 */
export default class Textarea {
	constructor( options = {} ) {
		this.options = {};
		Object.assign(
			this.options,
			{
				customAttributes: {},
				customClasses: [],
				helpText: '',
				id: '',
				label: '',
				labelCustomAttributes: {},
				labelCustomClasses: [],
				labelVisible: true,
				placeholder: '',
				rendered: false,
				renderOnInit: true,
				spacing: '',
				target: '',
				targetPosition: 'afterbegin',
				theme: 'cosmos',
				value: '',
				wrapperCustomAttributes: {},
				wrapperCustomClasses: [],
				wrapperTagName: 'div',
			},
			options
		);

		/**
		 * @event gform/textarea/pre_init
		 * @type {object}
		 * @description Fired before the component has started any internal init functions. A great chance to augment the options.
		 *
		 * @since 1.1.16
		 *
		 * @property {object} instance The Component class instance.
		 */
		trigger( { event: 'gform/textarea/pre_init', native: false, data: { instance: this } } );

		this.options.id = this.options.id || uniqueId( 'textarea' );

		this.elements = {};

		if ( this.options.renderOnInit ) {
			this.init();
		}
	}

	/**
	 * @memberof Textarea
	 * @description Renders the component into the dom.
	 *
	 * @since 1.1.16
	 *
	 * @return {void}
	 */
	render() {
		const { rendered, target, targetPosition } = this.options;

		if ( ! rendered ) {
			const renderTarget = getNode( target, document, true );

			renderTarget.insertAdjacentHTML(
				targetPosition,
				textareaTemplate( this.options )
			);
		}

		this.elements.textarea = getNode( `#${ this.options.id }`, document, true );
		this.elements.wrapper = this.elements.textarea.parentNode;
	}

	/**
	 * @memberof Textarea
	 * @description Initialize the component.
	 *
	 * @fires gform/textarea/post_render
	 *
	 * @since 1.1.16
	 *
	 * @return {void}
	 */
	init() {
		this.render();

		/**
		 * @event gform/textarea/post_render
		 * @type {object}
		 * @description Fired when the component has completed rendering and all class init functions have completed.
		 *
		 * @since 1.1.16
		 *
		 * @property {object} instance The Component class instance.
		 */
		trigger( { event: 'gform/textarea/post_render', native: false, data: { instance: this } } );

		consoleInfo( `Gravity Forms Admin: Initialized textarea component.` );
	}
}
