import clsx from 'clsx';
import {
	getColorClassName,
	getColorObjectByAttributeValues,
	RichText,
	store as blockEditorStore,
	useBlockProps,
} from '@wordpress/block-editor';
import { select } from '@wordpress/data';
import { SOLID_COLOR_CLASS } from './shared';
import migrateTextAlignAttributeToBlockSupport from '../utils/migrate-text-align';

const blockAttributes = {
	value: {
		type: 'string',
		source: 'html',
		selector: 'blockquote',
		multiline: 'p',
	},
	citation: {
		type: 'string',
		source: 'html',
		selector: 'cite',
		default: '',
	},
	mainColor: {
		type: 'string',
	},
	customMainColor: {
		type: 'string',
	},
	textColor: {
		type: 'string',
	},
	customTextColor: {
		type: 'string',
	},
};

function parseBorderColor( styleString ) {
	if ( ! styleString ) {
		return;
	}
	const matches = styleString.match( /border-color:([^;]+)[;]?/ );
	if ( matches && matches[ 1 ] ) {
		return matches[ 1 ];
	}
}

function multilineToInline( value ) {
	value = value || `<p></p>`;
	const padded = `</p>${ value }<p>`;
	const values = padded.split( `</p><p>` );

	values.shift();
	values.pop();

	return values.join( '<br>' );
}

const v6 = {
	attributes: {
		value: {
			type: 'rich-text',
			source: 'rich-text',
			selector: 'p',
			role: 'content',
		},
		citation: {
			type: 'rich-text',
			source: 'rich-text',
			selector: 'cite',
			role: 'content',
		},
		textAlign: {
			type: 'string',
		},
	},
	supports: {
		anchor: true,
		align: [ 'left', 'right', 'wide', 'full' ],
		background: {
			backgroundImage: true,
			backgroundSize: true,
			__experimentalDefaultControls: {
				backgroundImage: true,
			},
		},
		color: {
			gradients: true,
			background: true,
			link: true,
			__experimentalDefaultControls: {
				background: true,
				text: true,
			},
		},
		dimensions: {
			minHeight: true,
		},
		spacing: {
			margin: true,
			padding: true,
		},
		typography: {
			fontSize: true,
			lineHeight: true,
			__experimentalFontFamily: true,
			__experimentalFontWeight: true,
			__experimentalFontStyle: true,
			__experimentalTextTransform: true,
			__experimentalTextDecoration: true,
			__experimentalLetterSpacing: true,
			__experimentalDefaultControls: {
				fontSize: true,
			},
		},
		__experimentalBorder: {
			color: true,
			radius: true,
			style: true,
			width: true,
			__experimentalDefaultControls: {
				color: true,
				radius: true,
				style: true,
				width: true,
			},
		},
		__experimentalStyle: {
			typography: {
				fontSize: '1.5em',
				lineHeight: '1.6',
			},
		},
		interactivity: {
			clientNavigation: true,
		},
	},
	save( { attributes } ) {
		const { textAlign, citation, value } = attributes;
		const shouldShowCitation = ! RichText.isEmpty( citation );

		return (
			<figure
				{ ...useBlockProps.save( {
					className: clsx( {
						[ `has-text-align-${ textAlign }` ]: textAlign,
					} ),
				} ) }
			>
				<blockquote>
					<RichText.Content tagName="p" value={ value } />
					{ shouldShowCitation && (
						<RichText.Content tagName="cite" value={ citation } />
					) }
				</blockquote>
			</figure>
		);
	},
	isEligible( attributes ) {
		return (
			!! attributes.textAlign ||
			!! attributes.className?.match(
				/\bhas-text-align-(left|center|right)\b/
			)
		);
	},
	migrate: migrateTextAlignAttributeToBlockSupport,
};

// Version 5 created in #43210 / c4b2ca7f3f. Supports match block.json at the time.
const v5 = {
	attributes: {
		value: {
			type: 'string',
			source: 'html',
			selector: 'blockquote',
			multiline: 'p',
			role: 'content',
		},
		citation: {
			type: 'string',
			source: 'html',
			selector: 'cite',
			default: '',
			role: 'content',
		},
		textAlign: {
			type: 'string',
		},
	},
	supports: {
		anchor: true,
		align: [ 'left', 'right', 'wide', 'full' ],
		color: {
			gradients: true,
			background: true,
			link: true,
			__experimentalDefaultControls: {
				background: true,
				text: true,
			},
		},
		typography: {
			fontSize: true,
			lineHeight: true,
			__experimentalFontFamily: true,
			__experimentalFontWeight: true,
			__experimentalFontStyle: true,
			__experimentalTextTransform: true,
			__experimentalTextDecoration: true,
			__experimentalLetterSpacing: true,
			__experimentalDefaultControls: {
				fontSize: true,
				fontAppearance: true,
			},
		},
		__experimentalBorder: {
			color: true,
			radius: true,
			style: true,
			width: true,
			__experimentalDefaultControls: {
				color: true,
				radius: true,
				style: true,
				width: true,
			},
		},
		__experimentalStyle: {
			typography: {
				fontSize: '1.5em',
				lineHeight: '1.6',
			},
		},
	},
	save( { attributes } ) {
		const { textAlign, citation, value } = attributes;
		const shouldShowCitation = ! RichText.isEmpty( citation );

		return (
			<figure
				{ ...useBlockProps.save( {
					className: clsx( {
						[ `has-text-align-${ textAlign }` ]: textAlign,
					} ),
				} ) }
			>
				<blockquote>
					<RichText.Content value={ value } multiline />
					{ shouldShowCitation && (
						<RichText.Content tagName="cite" value={ citation } />
					) }
				</blockquote>
			</figure>
		);
	},
	migrate( { value, ...attributes } ) {
		return migrateTextAlignAttributeToBlockSupport( {
			value: multilineToInline( value ),
			...attributes,
		} );
	},
};

// TODO: this is ripe for a bit of a clean up according to the example in https://developer.wordpress.org/block-editor/reference-guides/block-api/block-deprecation/#example

// Version 4 created in #30951 / 92d36a4ea1. Supports match block.json at the time.
const v4 = {
	attributes: {
		...blockAttributes,
	},
	supports: {
		anchor: true,
		align: [ 'left', 'right', 'wide', 'full' ],
		color: {
			gradients: true,
			background: true,
			link: true,
		},
		__experimentalBorder: {
			color: true,
			radius: true,
			style: true,
			width: true,
		},
	},
	save( { attributes } ) {
		const {
			mainColor,
			customMainColor,
			customTextColor,
			textColor,
			value,
			citation,
			className,
		} = attributes;

		const isSolidColorStyle = className?.includes( SOLID_COLOR_CLASS );

		let figureClasses, figureStyles;

		// Is solid color style
		if ( isSolidColorStyle ) {
			const backgroundClass = getColorClassName(
				'background-color',
				mainColor
			);

			figureClasses = clsx( {
				'has-background': backgroundClass || customMainColor,
				[ backgroundClass ]: backgroundClass,
			} );

			figureStyles = {
				backgroundColor: backgroundClass ? undefined : customMainColor,
			};
			// Is normal style and a custom color is being used ( we can set a style directly with its value)
		} else if ( customMainColor ) {
			figureStyles = {
				borderColor: customMainColor,
			};
		}

		const blockquoteTextColorClass = getColorClassName(
			'color',
			textColor
		);
		const blockquoteClasses = clsx( {
			'has-text-color': textColor || customTextColor,
			[ blockquoteTextColorClass ]: blockquoteTextColorClass,
		} );

		const blockquoteStyles = blockquoteTextColorClass
			? undefined
			: { color: customTextColor };

		return (
			<figure
				{ ...useBlockProps.save( {
					className: figureClasses,
					style: figureStyles,
				} ) }
			>
				<blockquote
					className={ blockquoteClasses }
					style={ blockquoteStyles }
				>
					<RichText.Content value={ value } multiline />
					{ ! RichText.isEmpty( citation ) && (
						<RichText.Content tagName="cite" value={ citation } />
					) }
				</blockquote>
			</figure>
		);
	},
	migrate( {
		value,
		className,
		mainColor,
		customMainColor,
		customTextColor,
		...attributes
	} ) {
		const isSolidColorStyle = className?.includes( SOLID_COLOR_CLASS );
		let style;

		if ( customMainColor ) {
			if ( ! isSolidColorStyle ) {
				// Block supports: Set style.border.color if a deprecated block has a default style and a `customMainColor` attribute.
				style = {
					border: {
						color: customMainColor,
					},
				};
			} else {
				// Block supports: Set style.color.background if a deprecated block has a solid style and a `customMainColor` attribute.
				style = {
					color: {
						background: customMainColor,
					},
				};
			}
		}

		// Block supports: Set style.color.text if a deprecated block has a `customTextColor` attribute.
		if ( customTextColor && style ) {
			style.color = {
				...style.color,
				text: customTextColor,
			};
		}

		return migrateTextAlignAttributeToBlockSupport( {
			value: multilineToInline( value ),
			className,
			backgroundColor: isSolidColorStyle ? mainColor : undefined,
			borderColor: isSolidColorStyle ? undefined : mainColor,
			textAlign: isSolidColorStyle ? 'left' : undefined,
			...attributes,
			style,
		} );
	},
};

const v3 = {
	attributes: {
		...blockAttributes,
		// figureStyle is an attribute that never existed.
		// We are using it as a way to access the styles previously applied to the figure.
		figureStyle: {
			source: 'attribute',
			selector: 'figure',
			attribute: 'style',
		},
	},
	save( { attributes } ) {
		const {
			mainColor,
			customMainColor,
			textColor,
			customTextColor,
			value,
			citation,
			className,
			figureStyle,
		} = attributes;

		const isSolidColorStyle = className?.includes( SOLID_COLOR_CLASS );

		let figureClasses, figureStyles;

		// Is solid color style
		if ( isSolidColorStyle ) {
			const backgroundClass = getColorClassName(
				'background-color',
				mainColor
			);

			figureClasses = clsx( {
				'has-background': backgroundClass || customMainColor,
				[ backgroundClass ]: backgroundClass,
			} );

			figureStyles = {
				backgroundColor: backgroundClass ? undefined : customMainColor,
			};
			// Is normal style and a custom color is being used ( we can set a style directly with its value)
		} else if ( customMainColor ) {
			figureStyles = {
				borderColor: customMainColor,
			};
			// If normal style and a named color are being used, we need to retrieve the color value to set the style,
			// as there is no expectation that themes create classes that set border colors.
		} else if ( mainColor ) {
			// Previously here we queried the color settings to know the color value
			// of a named color. This made the save function impure and the block was refactored,
			// because meanwhile a change in the editor made it impossible to query color settings in the save function.
			// Here instead of querying the color settings to know the color value, we retrieve the value
			// directly from the style previously serialized.
			const borderColor = parseBorderColor( figureStyle );
			figureStyles = {
				borderColor,
			};
		}

		const blockquoteTextColorClass = getColorClassName(
			'color',
			textColor
		);
		const blockquoteClasses =
			( textColor || customTextColor ) &&
			clsx( 'has-text-color', {
				[ blockquoteTextColorClass ]: blockquoteTextColorClass,
			} );

		const blockquoteStyles = blockquoteTextColorClass
			? undefined
			: { color: customTextColor };

		return (
			<figure className={ figureClasses } style={ figureStyles }>
				<blockquote
					className={ blockquoteClasses }
					style={ blockquoteStyles }
				>
					<RichText.Content value={ value } multiline />
					{ ! RichText.isEmpty( citation ) && (
						<RichText.Content tagName="cite" value={ citation } />
					) }
				</blockquote>
			</figure>
		);
	},
	migrate( {
		value,
		className,
		figureStyle,
		mainColor,
		customMainColor,
		customTextColor,
		...attributes
	} ) {
		const isSolidColorStyle = className?.includes( SOLID_COLOR_CLASS );
		let style;

		if ( customMainColor ) {
			if ( ! isSolidColorStyle ) {
				// Block supports: Set style.border.color if a deprecated block has a default style and a `customMainColor` attribute.
				style = {
					border: {
						color: customMainColor,
					},
				};
			} else {
				// Block supports: Set style.color.background if a deprecated block has a solid style and a `customMainColor` attribute.
				style = {
					color: {
						background: customMainColor,
					},
				};
			}
		}

		// Block supports: Set style.color.text if a deprecated block has a `customTextColor` attribute.
		if ( customTextColor && style ) {
			style.color = {
				...style.color,
				text: customTextColor,
			};
		}
		// If is the default style, and a main color is set,
		// migrate the main color value into a custom border color.
		// The custom border color value is retrieved by parsing the figure styles.
		if ( ! isSolidColorStyle && mainColor && figureStyle ) {
			const borderColor = parseBorderColor( figureStyle );
			if ( borderColor ) {
				return {
					value: multilineToInline( value ),
					...attributes,
					className,
					// Block supports: Set style.border.color if a deprecated block has `mainColor`, inline border CSS and is not a solid color style.
					style: {
						border: {
							color: borderColor,
						},
					},
				};
			}
		}
		return migrateTextAlignAttributeToBlockSupport( {
			value: multilineToInline( value ),
			className,
			backgroundColor: isSolidColorStyle ? mainColor : undefined,
			borderColor: isSolidColorStyle ? undefined : mainColor,
			textAlign: isSolidColorStyle ? 'left' : undefined,
			...attributes,
			style,
		} );
	},
};

const v2 = {
	attributes: blockAttributes,
	save( { attributes } ) {
		const {
			mainColor,
			customMainColor,
			textColor,
			customTextColor,
			value,
			citation,
			className,
		} = attributes;
		const isSolidColorStyle = className?.includes( SOLID_COLOR_CLASS );

		let figureClass, figureStyles;
		// Is solid color style
		if ( isSolidColorStyle ) {
			figureClass = getColorClassName( 'background-color', mainColor );
			if ( ! figureClass ) {
				figureStyles = {
					backgroundColor: customMainColor,
				};
			}
			// Is normal style and a custom color is being used ( we can set a style directly with its value)
		} else if ( customMainColor ) {
			figureStyles = {
				borderColor: customMainColor,
			};
			// Is normal style and a named color is being used, we need to retrieve the color value to set the style,
			// as there is no expectation that themes create classes that set border colors.
		} else if ( mainColor ) {
			const colors =
				select( blockEditorStore ).getSettings().colors ?? [];
			const colorObject = getColorObjectByAttributeValues(
				colors,
				mainColor
			);
			figureStyles = {
				borderColor: colorObject.color,
			};
		}

		const blockquoteTextColorClass = getColorClassName(
			'color',
			textColor
		);
		const blockquoteClasses =
			textColor || customTextColor
				? clsx( 'has-text-color', {
						[ blockquoteTextColorClass ]: blockquoteTextColorClass,
				  } )
				: undefined;
		const blockquoteStyle = blockquoteTextColorClass
			? undefined
			: { color: customTextColor };
		return (
			<figure className={ figureClass } style={ figureStyles }>
				<blockquote
					className={ blockquoteClasses }
					style={ blockquoteStyle }
				>
					<RichText.Content value={ value } multiline />
					{ ! RichText.isEmpty( citation ) && (
						<RichText.Content tagName="cite" value={ citation } />
					) }
				</blockquote>
			</figure>
		);
	},
	migrate( {
		value,
		className,
		mainColor,
		customMainColor,
		customTextColor,
		...attributes
	} ) {
		const isSolidColorStyle = className?.includes( SOLID_COLOR_CLASS );
		let style = {};

		if ( customMainColor ) {
			if ( ! isSolidColorStyle ) {
				// Block supports: Set style.border.color if a deprecated block has a default style and a `customMainColor` attribute.
				style = {
					border: {
						color: customMainColor,
					},
				};
			} else {
				// Block supports: Set style.color.background if a deprecated block has a solid style and a `customMainColor` attribute.
				style = {
					color: {
						background: customMainColor,
					},
				};
			}
		}

		// Block supports: Set style.color.text if a deprecated block has a `customTextColor` attribute.
		if ( customTextColor && style ) {
			style.color = {
				...style.color,
				text: customTextColor,
			};
		}

		return migrateTextAlignAttributeToBlockSupport( {
			value: multilineToInline( value ),
			className,
			backgroundColor: isSolidColorStyle ? mainColor : undefined,
			borderColor: isSolidColorStyle ? undefined : mainColor,
			textAlign: isSolidColorStyle ? 'left' : undefined,
			...attributes,
			style,
		} );
	},
};

const v1 = {
	attributes: {
		...blockAttributes,
	},
	save( { attributes } ) {
		const { value, citation } = attributes;
		return (
			<blockquote>
				<RichText.Content value={ value } multiline />
				{ ! RichText.isEmpty( citation ) && (
					<RichText.Content tagName="cite" value={ citation } />
				) }
			</blockquote>
		);
	},
	migrate( { value, ...attributes } ) {
		return {
			value: multilineToInline( value ),
			...attributes,
		};
	},
};

const v0 = {
	attributes: {
		...blockAttributes,
		citation: {
			type: 'string',
			source: 'html',
			selector: 'footer',
		},
		align: {
			type: 'string',
			default: 'none',
		},
	},

	save( { attributes } ) {
		const { value, citation, align } = attributes;

		return (
			<blockquote className={ `align${ align }` }>
				<RichText.Content value={ value } multiline />
				{ ! RichText.isEmpty( citation ) && (
					<RichText.Content tagName="footer" value={ citation } />
				) }
			</blockquote>
		);
	},
	migrate( { value, ...attributes } ) {
		return {
			value: multilineToInline( value ),
			...attributes,
		};
	},
};

/**
 * New deprecations need to be placed first
 * for them to have higher priority.
 *
 * Old deprecations may need to be updated as well.
 *
 * See block-deprecation.md
 */
export default [ v6, v5, v4, v3, v2, v1, v0 ];
