<?php
/**
 * Block CSS generator entry point.
 *
 * Loaded via require_once inside each block's render.php.
 * PHP's require_once ensures this file executes only once per request
 * even when multiple block instances appear on the same page.
 *
 * Provides:
 *   axcelersblocks_sanitize_css_value( $value )
 *   axcelersblocks_get_responsive_devices()
 *   axcelersblocks_generate_block_styles( $unique_class, $devices, $attributes, $context )
 *   axcelersblocks_generate_block_styles_hover( $unique_class, $devices, $attributes )
 *   axcelersblocks_collect_block_styles( $unique_class, $block, $styles_css )
 *   axcelersblocks_get_allowed_svg_html()
 *
 * @package AxcelersBlocks
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Load the CSS generator class (class_exists guard is inside that file).
require_once __DIR__ . '/css/class-block-css.php';

// ─── Public functions ─────────────────────────────────────────────────────────

if ( ! function_exists( 'axcelersblocks_get_responsive_devices' ) ) {
	/**
	 * Build the device-to-media-query map used by every block's CSS generator.
	 * Reads breakpoints from the AxcelersBlocks settings page.
	 *
	 * @return array{ mobile: array{media:string}, tablet: array{media:string}, desktop: array{media:string} }
	 */
	function axcelersblocks_get_responsive_devices(): array {
		$settings    = get_option( 'axcelersblocks_responsive_settings' );
		$settings    = is_array( $settings ) ? $settings : [];
		$tablet_min  = (int) ( $settings['devices']['mobile']['width'] ?? 768 ) + 1;
		$desktop_min = (int) ( $settings['devices']['tablet']['width'] ?? 1024 ) + 1;
		return [
			'mobile'  => [ 'media' => '' ],
			'tablet'  => [ 'media' => "@media (min-width: {$tablet_min}px)" ],
			'desktop' => [ 'media' => "@media (min-width: {$desktop_min}px)" ],
		];
	}
}

if ( ! function_exists( 'axcelersblocks_sanitize_css_value' ) ) {
	/**
	 * Sanitize a single CSS property value.
	 * Strips tags, removes brace characters, trims whitespace.
	 *
	 * @param mixed $value Raw attribute value.
	 * @return string Safe CSS value or empty string.
	 */
	function axcelersblocks_sanitize_css_value( $value ): string {
		if ( is_array( $value ) ) { return ''; }
		if ( is_string( $value ) ) { $value = trim( $value ); }
		if ( $value === '' || $value === null ) { return ''; }
		$value = wp_strip_all_tags( (string) $value );
		$value = str_ireplace( [ '</style', '<style' ], '', $value );
		$value = str_replace( [ '{', '}' ], '', $value );
		return $value;
	}
}

if ( ! function_exists( 'axcelersblocks_generate_block_styles' ) ) {
	/**
	 * Generate all inline CSS rules for a block instance.
	 *
	 * @param string $unique_class Block-scoped unique class.
	 * @param array  $devices      Device map (mobile/tablet/desktop with media queries).
	 * @param array  $attributes   Full block attributes.
	 * @param array  $context      Optional: { has_icon?: bool, is_linked?: bool }
	 *                             When has_icon is false, icon/SVG CSS is omitted entirely.
	 *                             When is_linked is false, link/link-hover CSS is omitted.
	 * @return string[] CSS rule strings (with media queries where needed).
	 */
	function axcelersblocks_generate_block_styles(
		string $unique_class,
		array  $devices,
		array  $attributes,
		array  $context = []
	): array {
		return AxcelersBlocksCss::generate( $unique_class, $devices, $attributes, $context );
	}
}

if ( ! function_exists( 'axcelersblocks_generate_block_styles_state' ) ) {
	/**
	 * Generate CSS rules for a block interactive state (:hover / :focus / :active).
	 *
	 * Reads attributes whose keys end in $attr_suffix, strips the suffix, and passes
	 * them to AxcelersBlocksCss with the matching selector_suffix.
	 *
	 * @param string $unique_class    Block-scoped unique class.
	 * @param array  $devices         Device map from axcelersblocks_get_responsive_devices().
	 * @param array  $attributes      Full block attributes (including state variants).
	 * @param string $attr_suffix     Attribute key suffix, e.g. '__hover', '__focus', '__active'.
	 * @param string $selector_suffix CSS pseudo-class, e.g. ':hover', ':focus', ':active'.
	 * @param array  $context         Optional: { has_icon?: bool, is_linked?: bool } passed to the
	 *                                generator. When has_icon is false, icon/SVG CSS is omitted and
	 *                                iconSvg is not carried over.
	 * @return string[] CSS rule strings.
	 */
	function axcelersblocks_generate_block_styles_state( string $unique_class, array $devices, array $attributes, string $attr_suffix, string $selector_suffix, array $context = [] ): array {
		$len   = strlen( $attr_suffix );
		$state = [];
		foreach ( $attributes as $key => $value ) {
			if ( substr( $key, -$len ) === $attr_suffix ) {
				$state[ substr( $key, 0, -$len ) ] = $value;
			}
		}
		if ( empty( $state ) ) {
			return [];
		}
		// Carry over iconSvg so has_icon is correctly set, ensuring icon state selectors are
		// emitted — unless the caller explicitly disabled icon CSS via has_icon => false.
		$icon_disabled = array_key_exists( 'has_icon', $context ) && false === $context['has_icon'];
		if ( ! $icon_disabled && ! isset( $state['iconSvg'] ) && ! empty( $attributes['iconSvg'] ) ) {
			$state['iconSvg'] = $attributes['iconSvg'];
		}
		$context['selector_suffix'] = $selector_suffix;
		return AxcelersBlocksCss::generate( $unique_class, $devices, $state, $context );
	}
}

if ( ! function_exists( 'axcelersblocks_generate_block_styles_hover' ) ) {
	/** Generate CSS rules for a block's :hover state (from __hover attributes). */
	function axcelersblocks_generate_block_styles_hover( string $unique_class, array $devices, array $attributes, array $context = [] ): array {
		return axcelersblocks_generate_block_styles_state( $unique_class, $devices, $attributes, '__hover', ':hover', $context );
	}
}

if ( ! function_exists( 'axcelersblocks_generate_block_styles_focus' ) ) {
	/** Generate CSS rules for a block's :focus state (from __focus attributes). */
	function axcelersblocks_generate_block_styles_focus( string $unique_class, array $devices, array $attributes, array $context = [] ): array {
		return axcelersblocks_generate_block_styles_state( $unique_class, $devices, $attributes, '__focus', ':focus', $context );
	}
}

if ( ! function_exists( 'axcelersblocks_generate_block_styles_active' ) ) {
	/** Generate CSS rules for a block's :active state (from __active attributes). */
	function axcelersblocks_generate_block_styles_active( string $unique_class, array $devices, array $attributes, array $context = [] ): array {
		return axcelersblocks_generate_block_styles_state( $unique_class, $devices, $attributes, '__active', ':active', $context );
	}
}

if ( ! function_exists( 'axcelersblocks_collect_block_styles' ) ) {
	/**
	 * Add generated CSS rules to the global per-block collection.
	 * The collected styles are flushed to the page by the plugin's output hook.
	 *
	 * @param string   $unique_class Block-scoped unique class.
	 * @param WP_Block $block        The current WP_Block instance.
	 * @param string[] $styles_css   CSS rule strings from axcelersblocks_generate_block_styles().
	 */
	function axcelersblocks_collect_block_styles( string $unique_class, $block, array $styles_css ): void {
		if ( empty( $unique_class ) || empty( $styles_css ) ) {
			return;
		}
		global $axcelersblocks_collected_styles;
		if ( ! isset( $axcelersblocks_collected_styles ) ) {
			$axcelersblocks_collected_styles = [];
		}
		$block_name = isset( $block->name )
			? str_replace( '/', '-', $block->name ) . '-block'
			: 'unknown-block';
		if ( ! isset( $axcelersblocks_collected_styles[ $block_name ] ) ) {
			$axcelersblocks_collected_styles[ $block_name ] = [];
		}
		foreach ( $styles_css as $rule ) {
			$axcelersblocks_collected_styles[ $block_name ][] = $rule;
		}
	}
}

if ( ! function_exists( 'axcelersblocks_get_allowed_svg_html' ) ) {
	/**
	 * Return the wp_kses allowed-HTML map for post content extended with SVG tags.
	 * Used by render.php files that output inline SVG via wp_kses().
	 *
	 * @return array<string, array<string, bool>> Allowed HTML map.
	 */
	function axcelersblocks_get_allowed_svg_html(): array {
		$allowed                  = wp_kses_allowed_html( 'post' );
		$common_svg_attrs         = [
			'fill'          => true, 'stroke' => true, 'stroke-width' => true,
			'stroke-linecap'=> true, 'stroke-linejoin' => true,
			'fill-rule'     => true, 'clip-rule' => true,
			'transform'     => true, 'class'    => true,
		];
		$allowed['svg']      = array_merge( $common_svg_attrs, [
			'xmlns' => true, 'width' => true, 'height' => true,
			'viewBox' => true, 'viewbox' => true,
			'style' => true, 'aria-hidden' => true,
			'role' => true, 'focusable' => true, 'preserveAspectRatio' => true,
		] );
		$allowed['path']     = array_merge( $common_svg_attrs, [ 'd' => true ] );
		$allowed['g']        = $common_svg_attrs;
		$allowed['defs']     = [];
		$allowed['title']    = [];
		$allowed['use']      = [
			'href' => true, 'xlink:href' => true,
			'x' => true, 'y' => true, 'width' => true, 'height' => true,
			'class' => true, 'fill' => true,
		];
		$allowed['rect']     = array_merge( $common_svg_attrs, [
			'x' => true, 'y' => true, 'width' => true, 'height' => true, 'rx' => true, 'ry' => true,
		] );
		$allowed['circle']   = array_merge( $common_svg_attrs, [ 'cx' => true, 'cy' => true, 'r' => true ] );
		$allowed['ellipse']  = array_merge( $common_svg_attrs, [ 'cx' => true, 'cy' => true, 'rx' => true, 'ry' => true ] );
		$allowed['line']     = array_merge( $common_svg_attrs, [ 'x1' => true, 'y1' => true, 'x2' => true, 'y2' => true ] );
		$allowed['polyline'] = array_merge( $common_svg_attrs, [ 'points' => true ] );
		$allowed['polygon']  = array_merge( $common_svg_attrs, [ 'points' => true ] );
		return $allowed;
	}
}
