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

/**
 * @function headingTemplate
 * @description Generates the markup for a Heading Element in the admin.
 *
 * @since 2.2.0
 *
 * @param {object} options               The options for the heading component.
 * @param {string} options.content       Content for the heading.
 * @param {Array}  options.customClasses An array of additional classes for the heading.
 * @param {string} options.id            Id for the heading, auto generated if not passed.
 * @param {string} options.size          Size string, check storybook for available ones.
 * @param {string} options.spacing       Spacing for the component.
 * @param {string} options.tagName       Tag for the heading, h1 if not passed.
 * @param {string} options.weight        The font weight for the heading, regular, semibold or bold.
 *
 * @return {string}
 * @example
 * import { headingTemplate } from '@gravityforms/components/html/admin/elements/Heading';
 *
 * function Example() {
 *      const headingTemplateHTML = headingTemplateTemplate( options );
 *      document.body.insertAdjacentHTML( 'beforeend', headingTemplateHTML );
 * }
 *
 */
export const headingTemplate = ( {
	content = '',
	customClasses = [],
	id = '',
	size = 'display-3xl',
	spacing = '',
	tagName = 'h2',
	weight = 'semibold',
} ) => {
	const componentProps = {
		class: [
			'gform-heading',
			'gform-text',
			`gform-typography--size-${ size }`,
			`gform-typography--weight-${ weight }`,
			...customClasses,
			...Object.keys( spacerClasses( spacing ) ),
		],
		id,
	};

	return `<${ tagName } ${ objectToAttributes( componentProps ) }>${ content }</${ tagName }>`;
};

/**
 * @class Heading
 * @description A heading component.
 *
 * @since 2.2.0
 *
 * @borrows headingTemplate as headingTemplate
 *
 * @param {object}  options                The options for the heading component.
 * @param {string}  options.content        Content for the heading.
 * @param {Array}   options.customClasses  An array of additional classes for the heading.
 * @param {string}  options.id             Id for the heading, auto generated if not passed.
 * @param {boolean} options.rendered       Is this heading already rendered in the dom, eg by php?
 * @param {boolean} options.renderOnInit   Render the heading on init of the class?
 * @param {string}  options.size           Size string, check storybook for available ones.
 * @param {string}  options.spacing        The spacing classes to apply to the component.
 * @param {string}  options.tagName        Tag for the heading, h1 if not passed.
 * @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 text.
 *
 * @return {Class} The Heading instance.
 * @example
 * import Heading from '@gravityforms/components/html/admin/elements/Heading';
 *
 * function Example() {
 *      const headingInstance = new Heading( {
 *          content: 'This is a heading',
 *          rendered: false,
 *          renderOnInit: true,
 *          tagName: 'h2',
 *          target: '#example-target',
 *      } );
 * }
 *
 */
export default class Heading {
	/**
	 * @description The class constructor.
	 *
	 * @param {object} options The options object. Check defaults for descriptions.
	 */
	constructor( options = {} ) {
		this.options = {};
		Object.assign(
			this.options,
			{
				content: '',
				customClasses: [],
				id: uniqueId( 'heading' ),
				rendered: false,
				renderOnInit: true,
				size: 'display-3xl',
				spacing: '',
				tagName: 'h1',
				target: '',
				targetPosition: 'afterbegin',
				weight: 'semibold',
			},
			options
		);

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

		this.elements = {};

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

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

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

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

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

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

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