/**
 * External dependencies.
 */
import { RichText, useBlockProps } from '@wordpress/block-editor';
import { memo, useCallback, useMemo, useState } from '@wordpress/element';
import { select } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies.
 */
import { spectraClassNames } from '@spectra-helpers';
import { useSpectraStyles } from '@spectra-hooks';
import RenderSVG from '@spectra-helpers/render-svg';

const Render = ( props ) => {
	const { setAttributes, attributes, context } = props;

	const {
		text,
		showText,
		icon,
		iconPosition,
		flipForRTL,
		rotation,
		iconColor,
		iconColorHover,
		textColorHover,
		size,
		shadowHover,
		borderHover,
		showIconOnHover,
		hoverIcon,
		hoverIconPosition,
		hoverIconRotation,
		hoverIconFlipForRTL,
		hoverIconAriaLabel,
		width,
		buttonAlignment,
	} = attributes;

	// Get context values from parent login form
	const {
		'spectra-pro/form/ctaIcon': contextCtaIcon,
		'spectra-pro/form/ctaIconPosition': contextCtaIconPosition,
		'spectra-pro/form/ctaIconSpace': contextCtaIconSpace,
		'spectra-pro/form/submitButtonText': contextSubmitButtonText,
		'spectra-pro/form/overallAlignment': overallAlignment,
	} = context || {};

	// Use context values as fallbacks
	const finalIcon = icon || contextCtaIcon;
	const finalIconPosition = iconPosition || contextCtaIconPosition || 'after';
	const finalText = contextSubmitButtonText || text;
	// Use button's own alignment, fallback to parent's overallAlignment, then default to left
	const finalAlignment = buttonAlignment || overallAlignment || 'left';

	/**
	 * Dynamically retrieve all registered RichText format types from the
	 * WordPress rich-text store and exclude the 'core/link' format.
	 *
	 * This ensures that all other formatting options (like bold, italic, etc.)
	 * remain available in the RichText toolbar, while preventing users from
	 * inserting or editing links.
	 */
	const allowedFormats = useMemo( () => {
		const allFormats = select( 'core/rich-text' ).getFormatTypes() || [];
	
		return allFormats
			.map( ( format ) => format.name )
			.filter( ( name ) => name !== 'core/link' && name !== 'core/footnote' );
	}, [] );

	// Convert shadow hover object to CSS string.
	const getShadowHoverValue = () => {
		if ( !shadowHover ) return undefined;
		const { x = 0, y = 4, blur = 8, spread = 0, color = '' } = shadowHover;
		
		// Only return shadow if color is actually set.
		if ( !color || color.trim() === '' ) {
			return undefined;
		}
		
		return `${x}px ${y}px ${blur}px ${spread}px ${color}`;
	};

	// Convert border hover object to CSS strings.
	const getBorderHoverValues = () => {
		if ( !borderHover?.color ) return {};
		
		// Get normal border values from block supports.
		const normalBorder = attributes.style?.border || {};
		
		// Handle flat border (same border for all sides).
		if ( normalBorder.width || normalBorder.style ) {
			const borderWidth = normalBorder.width || '0px';
			const borderStyle = normalBorder.style || 'solid';
			const borderColor = borderHover.color || 'transparent';
			
			return {
				borderHoverWidth: borderWidth,
				borderHoverStyle: borderStyle,
				borderHoverColor: borderColor,
				borderHover: `${borderWidth} ${borderStyle} ${borderColor}`,
			};
		}
		
		// Handle split borders (different border for each side).
		if ( normalBorder.top || normalBorder.right || normalBorder.bottom || normalBorder.left ) {
			const getValue = ( normalSide ) => {
				if ( !normalSide ) return { width: '0px', style: 'solid', color: borderHover.color || 'transparent' };
				return {
					width: normalSide.width || '0px',
					style: normalSide.style || 'solid',
					color: borderHover.color || 'transparent',
				};
			};
			
			const top    = getValue( normalBorder.top );
			const right  = getValue( normalBorder.right );
			const bottom = getValue( normalBorder.bottom );
			const left   = getValue( normalBorder.left );
			
			return {
				borderHoverTopWidth: top.width,
				borderHoverTopStyle: top.style,
				borderHoverTopColor: top.color,
				borderHoverTop: `${top.width} ${top.style} ${top.color}`,
				borderHoverRightWidth: right.width,
				borderHoverRightStyle: right.style,
				borderHoverRightColor: right.color,
				borderHoverRight: `${right.width} ${right.style} ${right.color}`,
				borderHoverBottomWidth: bottom.width,
				borderHoverBottomStyle: bottom.style,
				borderHoverBottomColor: bottom.color,
				borderHoverBottom: `${bottom.width} ${bottom.style} ${bottom.color}`,
				borderHoverLeftWidth: left.width,
				borderHoverLeftStyle: left.style,
				borderHoverLeftColor: left.color,
				borderHoverLeft: `${left.width} ${left.style} ${left.color}`,
			};
		}
		
		return {};
	};

	const borderHoverValues = getBorderHoverValues();

	// Configuration for the useSpectraStyles hook.
	const config = [
		{ key: 'textColor' },
		{ key: 'textColorHover' },
		{ key: 'iconColor' },
		{ key: 'iconColorHover' },
		{ key: 'backgroundColor' },
		{ key: 'backgroundColorHover' },
		{ key: 'backgroundGradient' },
		{ key: 'backgroundGradientHover' },
		{ 
			key: 'shadowHover', 
			cssVar: '--spectra-shadow-hover', 
			className: 'spectra-shadow-hover',
			value: getShadowHoverValue(),
		},
		// Border hover configurations.
		...( borderHoverValues.borderHover ? [
			{ 
				key: 'borderHover', 
				cssVar: '--spectra-border-hover', 
				className: 'spectra-border-hover',
				value: borderHoverValues.borderHover,
			}
		] : [] ),
		...( borderHoverValues.borderHoverTop ? [
			{ 
				key: 'borderHoverTop', 
				cssVar: '--spectra-border-hover-top', 
				className: 'spectra-border-hover-top',
				value: borderHoverValues.borderHoverTop,
			},
			{ 
				key: 'borderHoverRight', 
				cssVar: '--spectra-border-hover-right', 
				className: 'spectra-border-hover-right',
				value: borderHoverValues.borderHoverRight,
			},
			{ 
				key: 'borderHoverBottom', 
				cssVar: '--spectra-border-hover-bottom', 
				className: 'spectra-border-hover-bottom',
				value: borderHoverValues.borderHoverBottom,
			},
			{ 
				key: 'borderHoverLeft', 
				cssVar: '--spectra-border-hover-left', 
				className: 'spectra-border-hover-left',
				value: borderHoverValues.borderHoverLeft,
			}
		] : [] ),
		{ key: 'gap', cssVar: '--spectra-icon-gap', className: null },
	];

	const customClassNames = [
		'spectra-button',
		'wp-block-button',
		'wp-block-button__link',
		showIconOnHover && hoverIcon && 'has-hover-icon',
		borderHover && 'has-border-hover',
		borderHover && 'spectra-border-hover-override',
		shadowHover && 'spectra-shadow-hover-override',
		`spectra-button--align-${finalAlignment}`,
	].filter( Boolean );

	// Generate styles and class names.
	const { style, classNames } = useSpectraStyles( attributes, config, customClassNames );

	// Apply button-specific styles from context
	// Width is handled by responsive controls system which generates CSS for different breakpoints
	// and the width attribute stores the current device's value
	const buttonStyles = {
		...style,
		'--spectra-icon-spacing': contextCtaIconSpace || '5px',
		...( width && { width } ),
	};

	// Default aria-label for accessibility (normal state).
	const getDefaultAriaLabel = useCallback( () => {
		// Use the text content (strip HTML tags for better accessibility).
		return text ? text.replace( /<[^>]*>/g, '' ) : undefined;
	}, [ text ] );

	// Store current aria-label state.
	const [ currentAriaLabel, setCurrentAriaLabel ] = useState( getDefaultAriaLabel() );

	// Hover handlers for dynamic aria-label.
	const handleMouseEnter = useCallback( () => {
		if ( showIconOnHover && hoverIconAriaLabel ) {
			setCurrentAriaLabel( hoverIconAriaLabel );
		}
	}, [ showIconOnHover, hoverIconAriaLabel ] );

	const handleMouseLeave = useCallback( () => {
		setCurrentAriaLabel( getDefaultAriaLabel() );
	}, [ getDefaultAriaLabel ] );

	// Use the block props, with the added CSS variables and their related classes.
	const blockProps = useBlockProps( {
		'className': classNames,
		'style': buttonStyles,
		'aria-label': currentAriaLabel,
		'onMouseEnter': handleMouseEnter,
		'onMouseLeave': handleMouseLeave,
		'onFocus': handleMouseEnter,
		'onBlur': handleMouseLeave,
	} );

	const iconHtml = useCallback(
		( position ) => {
			// If there's no icon, or if the position given does not match the required one, abandon ship.
			if ( ! finalIcon || position !== finalIconPosition ) {
				return null;
			}
			// Render the icon.
			return (
				<RenderSVG
					className={ spectraClassNames( [
						'spectra-button__icon',
						`spectra-button__icon-position-${ finalIconPosition }`,
						iconColor && 'spectra-icon-color',
						( iconColorHover || textColorHover ) && 'spectra-icon-color-hover',
					] ) }
					svg={ finalIcon }
					needsRTL={ flipForRTL }
					extraProps={ {
						width: size ? size : '16px',
						height: size ? size : '16px',
						style: {
							width: size || '16px',
							height: size || '16px',
							transform: rotation ? `rotate(${ rotation }deg)` : '',
						},
					} }
				/>
			);
		},
		[ finalIcon, finalIconPosition, size, iconColor, iconColorHover, textColorHover, rotation, flipForRTL ]
	);

	// Hover icon rendering function.
	const hoverIconHtml = useCallback(
		( position ) => {
			const finalHoverIconPosition = hoverIconPosition || 'right'; // Ensure a default value.
			// Only render if showIconOnHover is enabled, hoverIcon exists, and position matches.
			if ( ! showIconOnHover || ! hoverIcon || position !== finalHoverIconPosition ) {
				return null;
			}

			return (
				<RenderSVG
					className={ spectraClassNames( [
						'spectra-button__hover-icon',
						`spectra-button__hover-icon-position-${ finalHoverIconPosition }`,
						iconColor && 'spectra-icon-color',
						( iconColorHover || textColorHover ) && 'spectra-icon-color-hover',
					] ) }
					svg={ hoverIcon }
					needsRTL={ hoverIconFlipForRTL }
					extraProps={ {
						fill: 'currentColor',
						width: size ? size : '16px',
						height: size ? size : '16px',
						style: {
							width: size || '16px',
							height: size || '16px',
							transform: hoverIconRotation ? `rotate(${ hoverIconRotation }deg)` : '',
						},
					} }
				/>
			);
		},
		[ showIconOnHover, hoverIcon, hoverIconPosition, hoverIconRotation, size, iconColor, iconColorHover, textColorHover, hoverIconFlipForRTL ]
	);

	const btnText = useCallback( () => {
		// If we don't have to show text, abandon ship.
		if ( ! showText ) {
			return '';
		}
		return (
			<RichText
				placeholder={ __( 'Add text…', 'spectra-pro' ) }
				value={ finalText }
				tagName="div"
				onChange={ ( value ) => setAttributes( { text: value } ) }
				className="spectra-button__link"
				rel= 'follow noopener'
				keepPlaceholderOnFocus
				allowedFormats={ allowedFormats }
			/>
		);
	}, [ showText, finalText, setAttributes ] );

	return (
		<button { ...blockProps } type='submit'>
			{ iconHtml( 'before' ) }
			{ hoverIconHtml( 'left' ) }
			{ btnText() }
			{ hoverIconHtml( 'right' ) }
			{ iconHtml( 'after' ) }
		</button>
	);
};

export default memo( Render );
