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

/**
 * @function labelTemplate
 * @description Generates the markup for a Label Element in the admin.
 *
 * @since 2.2.0
 *
 * @param {object}              options                  The options for the label component.
 * @param {object}              options.customAttributes Any custom attributes.
 * @param {Array}               options.customClasses    Any custom classes.
 * @param {string}              options.htmlFor          The for attribute for the label.
 * @param {string}              options.label            Label text.
 * @param {boolean}             options.isVisible        If label is visible (true) or hidden (false).
 * @param {string}              options.size             The font size for the label.
 * @param {string|number|Array} options.spacing          The spacing for the component, string or array.
 * @param {string}              options.weight           The font weight for the label.
 *
 * @return {string} The html for the label component.
 * @example
 * import { labelTemplate } from '@gravityforms/components/html/admin/elements/Label';
 *
 * function Example() {
 *      const labelTemplateHTML = labelTemplate( options );
 *      document.body.insertAdjacentHTML( 'beforeend', labelTemplateHTML );
 * }
 *
 */
export const labelTemplate = ( {
	customAttributes = {},
	customClasses = [],
	htmlFor = '',
	label = '',
	isVisible = true,
	size = 'text-sm',
	spacing = '',
	weight = 'medium',
} ) => {
	if ( ! label ) {
		return '';
	}

	const labelAttrs = objectToAttributes( {
		...customAttributes,
		class: [
			'gform-label',
			`gform-typography--size-${ size }`,
			`gform-typography--weight-${ weight }`,
			! isVisible && 'gform-visually-hidden',
			...Object.keys( spacerClasses( spacing ) ),
			...customClasses,
		],
		for: htmlFor,
	} );

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

/**
 * @class Label
 * @description A label component with optional header area to contain a group of html elements.
 *
 * @since 2.2.0
 *
 * @param {object}                     options                  Component options.
 * @param {object}                     options.customAttributes Custom attributes for the component.
 * @param {string|Array|object}        options.customClasses    Custom classes for the component.
 * @param {string}                     options.htmlFor          The for attribute for the label.
 * @param {boolean}                    options.isVisible        If label is visible (true) or hidden (false).
 * @param {boolean}                    options.rendered         Is this label already rendered in the dom, eg by php?
 * @param {boolean}                    options.renderOnInit     Render the label on init of the class?
 * @param {string}                     options.label            Label text.
 * @param {string}                     options.size             The font size for the label.
 * @param {string|number|Array|object} options.spacing          The spacing for the component, as a string, number, array, or object.
 * @param {string}                     options.target           The target to render to. Any valid css selector string.
 * @param {string}                     options.targetPosition   The insert position for the target.
 * @param {string}                     options.weight           The font weight for the label.
 *
 * @return {Class} The Label instance.
 * @example
 * import Label from '@gravityforms/components/html/admin/elements/Label';
 *
 * function Example() {
 *      const labelInstance = new Label( {
 *          label: 'Label text',
 *          htmlFor: 'input-id',
 *          target: '#example-target',
 *      } );
 * }
 *
 */
export default class Label {
	/**
	 * @description The class constructor.
	 *
	 * @param {object} options The options object. Check defaults for descriptions.
	 */
	constructor( options = {} ) {
		this.options = {};
		Object.assign(
			this.options,
			{
				customAttributes: {},
				customClasses: [],
				htmlFor: '',
				label: '',
				isVisible: true,
				rendered: false,
				renderOnInit: true,
				size: 'text-sm',
				spacing: '',
				target: '',
				targetPosition: 'afterbegin',
				weight: 'medium',
			},
			options
		);

		trigger( { event: 'gform/label/pre_init', native: false, data: { instance: this } } );

		this.elements = {};

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

	/**
	 * @function render
	 * @description Renders the label into the dom.
	 *
	 * @since 2.2.0
	 *
	 * @return {void}
	 */
	render() {
		const { rendered, target, targetPosition } = this.options;

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

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

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

	/**
	 * @function init
	 * @description Initialize the label.
	 *
	 * @since 2.2.0
	 *
	 * @return {void}
	 */
	init() {
		this.render();

		trigger( { event: 'gform/label/post_render', native: false, data: { instance: this } } );

		consoleInfo( `Gravity Forms Admin: Initialized label component on ${ this.options.target }.` );
	}
}
