/**
 * Color / gradient fill picker.
 * When enableGradient is on: Solid | Gradient tabs sit above Theme | Default.
 *
 * Remembers the last solid + gradient (including stop positions) while switching
 * tabs so control points are not wiped; stop colors remain editable.
 */
import { GradientPicker, TabPanel } from '@wordpress/components';
import { useEffect, useLayoutEffect, useRef, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { isAbsentColor } from '../cssVars';
import ThemeColorPalette from './ThemeColorPalette';

/**
 * Paint each stop handle with its color. WP's GradientPicker exposes the color
 * in aria-label but does not set a background, so handles look empty once CSS
 * resets strip the default ring.
 *
 * @param {HTMLElement | null} root
 */
function syncGradientStopHandleColors( root ) {
	if ( ! root ) {
		return;
	}
	root
		.querySelectorAll(
			'.components-custom-gradient-picker__control-point-button'
		)
		.forEach( ( btn ) => {
			const label = String( btn.getAttribute( 'aria-label' ) || '' );
			const match = label.match( /with color code\s+(.+?)\.?\s*$/i );
			if ( match?.[ 1 ] ) {
				btn.style.backgroundColor = match[ 1 ].trim();
			}
		} );
}

export const DEFAULT_FILL_SOLID = '#3525cd';
export const DEFAULT_FILL_GRADIENT =
	'linear-gradient(135deg, #3525cd 0%, #131b2e 100%)';

export const DEFAULT_OVERLAY_SOLID = 'rgba(19, 27, 46, 0.45)';
export const DEFAULT_OVERLAY_GRADIENT =
	'linear-gradient(180deg, rgba(19,27,46,0.55) 0%, rgba(53,37,205,0.65) 100%)';

export const FILL_GRADIENT_PRESETS = [
	{
		name: __( 'Midnight fade', 'joc-blockora-morph' ),
		slug: 'midnight-fade',
		gradient:
			'linear-gradient(180deg, rgba(19,27,46,0.15) 0%, rgba(19,27,46,0.75) 100%)',
	},
	{
		name: __( 'Brand wash', 'joc-blockora-morph' ),
		slug: 'brand-wash',
		gradient: DEFAULT_OVERLAY_GRADIENT,
	},
	{
		name: __( 'Soft vignette', 'joc-blockora-morph' ),
		slug: 'soft-vignette',
		gradient:
			'radial-gradient(circle at center, rgba(19,27,46,0.1) 0%, rgba(19,27,46,0.7) 100%)',
	},
	{
		name: __( 'Left shade', 'joc-blockora-morph' ),
		slug: 'left-shade',
		gradient:
			'linear-gradient(90deg, rgba(19,27,46,0.8) 0%, rgba(19,27,46,0.15) 100%)',
	},
];

/**
 * @param {string} value
 * @return {boolean}
 */
export function isGradientValue( value ) {
	return String( value || '' )
		.toLowerCase()
		.includes( 'gradient' );
}

/**
 * @param {object} field
 * @param {string} [resolvedType]
 * @return {boolean}
 */
export function styleFieldAllowsGradient( field, resolvedType = '' ) {
	if ( field?.enableGradient === false ) {
		return false;
	}
	if ( field?.enableGradient === true ) {
		return true;
	}
	const type = resolvedType || field?.type || '';
	const key = String( field?.key || '' );
	return (
		type === 'overlay' ||
		type === 'fill' ||
		key === '--bm-bg' ||
		key.includes( 'overlay' )
	);
}

/**
 * @param {object}   props
 * @param {string}   props.gradientValue
 * @param {Array}    props.gradients
 * @param {boolean}  props.clearable
 * @param {Function} props.onCommit
 * @param {object}   props.lastGradientRef
 */
function GradientTabPanel( {
	gradientValue,
	gradients,
	clearable,
	onCommit,
	lastGradientRef,
} ) {
	const rootRef = useRef( null );

	useLayoutEffect( () => {
		syncGradientStopHandleColors( rootRef.current );
	}, [ gradientValue ] );

	useEffect( () => {
		const root = rootRef.current;
		if ( ! root || typeof MutationObserver === 'undefined' ) {
			return undefined;
		}
		const observer = new MutationObserver( () => {
			syncGradientStopHandleColors( root );
		} );
		observer.observe( root, {
			subtree: true,
			childList: true,
			attributes: true,
			attributeFilter: [ 'aria-label', 'style', 'class' ],
		} );
		syncGradientStopHandleColors( root );
		return () => observer.disconnect();
	}, [] );

	return (
		<div
			ref={ rootRef }
			className="bm-fill-color-palette__gradient"
		>
			<GradientPicker
				value={ gradientValue }
				onChange={ ( next ) => {
					if ( next == null || next === '' ) {
						if ( clearable ) {
							// Clear keeps the last stops in draft; switch
							// display back to the remembered gradient.
							onCommit( lastGradientRef.current );
						}
						return;
					}
					onCommit( next );
				} }
				gradients={ gradients }
				clearable={ clearable }
				__experimentalIsRenderedInSidebar
			/>
		</div>
	);
}

/**
 * @param {object}   props
 * @param {string}   [props.value]
 * @param {Function} props.onChange
 * @param {boolean}  [props.enableGradient]
 * @param {string}   [props.solidDefault]
 * @param {string}   [props.gradientDefault]
 * @param {Array}    [props.gradients]
 * @param {Array}    [props.palettes]
 * @param {boolean}  [props.clearable]
 * @param {boolean}  [props.enableAlpha]
 * @param {string}   [props.className]
 */
export default function FillColorPalette( {
	value = '',
	onChange,
	enableGradient = false,
	solidDefault = DEFAULT_FILL_SOLID,
	gradientDefault = DEFAULT_FILL_GRADIENT,
	gradients = FILL_GRADIENT_PRESETS,
	palettes = null,
	clearable = true,
	enableAlpha = true,
	className = '',
} ) {
	const initialGradient = isGradientValue( value )
		? String( value )
		: String( gradientDefault || DEFAULT_FILL_GRADIENT );
	const initialSolid = isGradientValue( value )
		? String( solidDefault || DEFAULT_FILL_SOLID )
		: String( value || solidDefault || DEFAULT_FILL_SOLID );

	const [ lastSolid, setLastSolid ] = useState( initialSolid );
	const [ lastGradient, setLastGradient ] = useState( initialGradient );
	const lastSolidRef = useRef( lastSolid );
	const lastGradientRef = useRef( lastGradient );
	lastSolidRef.current = lastSolid;
	lastGradientRef.current = lastGradient;

	// Keep drafts in sync when the stored value changes from outside.
	useEffect( () => {
		if ( isGradientValue( value ) ) {
			const next = String( value );
			setLastGradient( next );
			lastGradientRef.current = next;
			return;
		}
		if ( value != null && String( value ).trim() !== '' ) {
			const next = String( value );
			setLastSolid( next );
			lastSolidRef.current = next;
		}
	}, [ value ] );

	// Prefer a concrete remembered gradient when the parent only supplies a default.
	useEffect( () => {
		if ( ! isGradientValue( gradientDefault ) ) {
			return;
		}
		if ( ! isGradientValue( lastGradientRef.current ) ) {
			const next = String( gradientDefault );
			setLastGradient( next );
			lastGradientRef.current = next;
		}
	}, [ gradientDefault ] );

	if ( ! enableGradient ) {
		return (
			<ThemeColorPalette
				className={ className }
				palettes={ palettes }
				value={ isGradientValue( value ) ? '' : value || '' }
				onChange={ ( next ) =>
					onChange?.( isAbsentColor( next ) ? '' : next )
				}
				clearable={ clearable }
				enableAlpha={ enableAlpha }
			/>
		);
	}

	const mode = isGradientValue( value ) ? 'gradient' : 'solid';
	const solidValue = isGradientValue( value ) ? lastSolid : value || lastSolid;
	const gradientValue = isGradientValue( value )
		? String( value )
		: lastGradient;

	const commitSolid = ( next ) => {
		if ( isAbsentColor( next ) ) {
			onChange?.( '' );
			return;
		}
		const solid = String( next );
		setLastSolid( solid );
		lastSolidRef.current = solid;
		onChange?.( solid );
	};

	const commitGradient = ( next ) => {
		// Preserve stops: empty/undefined from clear → fall back to last draft,
		// never invent a brand-new default mid-edit unless we have nothing saved.
		const fallback =
			lastGradientRef.current || gradientDefault || DEFAULT_FILL_GRADIENT;
		const gradient = isGradientValue( next ) ? String( next ) : fallback;
		setLastGradient( gradient );
		lastGradientRef.current = gradient;
		onChange?.( gradient );
	};

	return (
		<TabPanel
			key={ mode }
			className={ `bm-fill-color-palette ${ className }`.trim() }
			activeClass="is-active"
			initialTabName={ mode }
			onSelect={ ( tabName ) => {
				if ( tabName === mode ) {
					return;
				}
				if ( tabName === 'gradient' ) {
					commitGradient( lastGradientRef.current );
					return;
				}
				commitSolid( lastSolidRef.current );
			} }
			tabs={ [
				{
					name: 'solid',
					title: __( 'Solid', 'joc-blockora-morph' ),
				},
				{
					name: 'gradient',
					title: __( 'Gradient', 'joc-blockora-morph' ),
				},
			] }
		>
			{ ( tab ) =>
				tab.name === 'gradient' ? (
					<GradientTabPanel
						gradientValue={ gradientValue }
						gradients={ gradients }
						clearable={ clearable }
						onCommit={ commitGradient }
						lastGradientRef={ lastGradientRef }
					/>
				) : (
					<ThemeColorPalette
						palettes={ palettes }
						value={ solidValue }
						onChange={ commitSolid }
						clearable={ clearable }
						enableAlpha={ enableAlpha }
					/>
				)
			}
		</TabPanel>
	);
}
