<?php
/**
 * Hero background layer helpers (stacked images / video).
 *
 * @package Rsmspark_Content_Widgets
 */

defined( 'ABSPATH' ) || exit;

/**
 * Rsmspark_Content_Widgets_Hero_Layers
 */
class Rsmspark_Content_Widgets_Hero_Layers {

	const MAX_LAYERS = 5;

	/**
	 * Allowed CSS mix-blend-mode values.
	 *
	 * @return string[]
	 */
	public static function blend_mode_keys() {
		return array(
			'normal',
			'multiply',
			'screen',
			'overlay',
			'darken',
			'lighten',
			'soft-light',
			'hard-light',
			'color-dodge',
			'color-burn',
			'difference',
			'exclusion',
		);
	}

	/**
	 * @return array<string, string>
	 */
	public static function blend_mode_options() {
		return array(
			'normal'      => __( 'Normal', 'rsm-content-widgets' ),
			'multiply'    => __( 'Multiply', 'rsm-content-widgets' ),
			'screen'      => __( 'Screen', 'rsm-content-widgets' ),
			'overlay'     => __( 'Overlay', 'rsm-content-widgets' ),
			'darken'      => __( 'Darken', 'rsm-content-widgets' ),
			'lighten'     => __( 'Lighten', 'rsm-content-widgets' ),
			'soft-light'  => __( 'Soft light', 'rsm-content-widgets' ),
			'hard-light'  => __( 'Hard light', 'rsm-content-widgets' ),
			'color-dodge' => __( 'Color dodge', 'rsm-content-widgets' ),
			'color-burn'  => __( 'Color burn', 'rsm-content-widgets' ),
			'difference'  => __( 'Difference', 'rsm-content-widgets' ),
			'exclusion'   => __( 'Exclusion', 'rsm-content-widgets' ),
		);
	}

	/**
	 * @param string $mode Raw blend mode.
	 * @return string
	 */
	public static function sanitize_blend_mode( $mode ) {
		$key = sanitize_key( (string) $mode );
		return in_array( $key, self::blend_mode_keys(), true ) ? $key : 'normal';
	}

	/**
	 * @param mixed $raw Raw layer row.
	 * @return array<string, mixed>
	 */
	public static function sanitize_layer( $raw ) {
		if ( ! is_array( $raw ) ) {
			return array();
		}

		$type = sanitize_key( (string) ( $raw['type'] ?? 'image' ) );
		if ( ! in_array( $type, array( 'image', 'video' ), true ) ) {
			$type = 'image';
		}

		$opacity = isset( $raw['opacity'] ) ? (int) $raw['opacity'] : 100;
		$opacity = max( 0, min( 100, $opacity ) );

		$focal_preset = Rsmspark_Content_Widgets_Focal_Point::sanitize_preset( $raw['focal_preset'] ?? 'center' );
		$focal_x      = Rsmspark_Content_Widgets_Focal_Point::sanitize_coord( $raw['focal_x'] ?? 50 );
		$focal_y      = Rsmspark_Content_Widgets_Focal_Point::sanitize_coord( $raw['focal_y'] ?? 50 );

		$layer = array(
			'type'           => $type,
			'image'          => $raw['image'] ?? '',
			'image_src'      => esc_url_raw( trim( (string) ( $raw['image_src'] ?? '' ) ) ),
			'video_url'      => esc_url_raw( trim( (string) ( $raw['video_url'] ?? '' ) ) ),
			'opacity'        => $opacity,
			'blend_mode'     => self::sanitize_blend_mode( $raw['blend_mode'] ?? 'normal' ),
			'focal_preset'   => $focal_preset,
			'focal_x'        => $focal_x,
			'focal_y'        => $focal_y,
			'parallax'       => Rsmspark_Content_Widgets_Render::boolish( $raw['parallax'] ?? false ),
			'focal_position' => Rsmspark_Content_Widgets_Focal_Point::resolve_position( $focal_preset, $focal_x, $focal_y ),
		);

		if ( 'video' === $type ) {
			if ( '' === $layer['video_url'] ) {
				return array();
			}
			return $layer;
		}

		$url = Rsmspark_Content_Widgets_Widget_Hero::background_image_url( $layer['image'], $layer['image_src'] );
		if ( '' === $url ) {
			return array();
		}

		$layer['image_url'] = $url;
		return $layer;
	}

	/**
	 * @param mixed $items Raw repeater rows.
	 * @return array<int, array<string, mixed>>
	 */
	public static function coerce_layers( $items ) {
		if ( is_string( $items ) && '' !== trim( $items ) ) {
			$items = Rsmspark_Content_Widgets_Render::parse_json_attr( $items, array() );
		}

		if ( ! is_array( $items ) ) {
			return array();
		}

		$out = array();
		foreach ( $items as $row ) {
			$layer = self::sanitize_layer( $row );
			if ( empty( $layer ) ) {
				continue;
			}
			$out[] = $layer;
			if ( count( $out ) >= self::MAX_LAYERS ) {
				break;
			}
		}

		return $out;
	}

	/**
	 * Build layers from legacy single-image / video fields.
	 *
	 * @param string $image_url Resolved primary image URL.
	 * @param string $video_url Background video URL.
	 * @param string $focal_position Object position for image layer.
	 * @param bool   $video_allowed Whether Pro video is allowed.
	 * @return array<int, array<string, mixed>>
	 */
	public static function legacy_layers( $image_url, $video_url, $focal_position, $video_allowed ) {
		$layers = array();

		if ( '' !== $image_url ) {
			$layers[] = array(
				'type'           => 'image',
				'image_url'      => $image_url,
				'opacity'        => 100,
				'blend_mode'     => 'normal',
				'focal_position' => $focal_position,
				'parallax'       => false,
			);
		}

		if ( $video_allowed && '' !== $video_url ) {
			$layers[] = array(
				'type'       => 'video',
				'video_url'  => $video_url,
				'opacity'    => 100,
				'blend_mode' => 'normal',
				'parallax'   => false,
			);
		}

		return $layers;
	}

	/**
	 * Apply Pro gates and prepare render-ready layer rows.
	 *
	 * @param array<int, array<string, mixed>> $layers Coerced layers.
	 * @param bool                              $can_media Pro advanced_animations.
	 * @return array<int, array<string, mixed>>
	 */
	public static function prepare_for_render( array $layers, $can_media ) {
		$prepared = array();

		foreach ( $layers as $layer ) {
			if ( 'video' === ( $layer['type'] ?? 'image' ) ) {
				if ( ! $can_media || empty( $layer['video_url'] ) ) {
					continue;
				}
				$layer['parallax'] = $can_media && ! empty( $layer['parallax'] );
			} else {
				if ( empty( $layer['image_url'] ) ) {
					continue;
				}
				$layer['parallax'] = $can_media && ! empty( $layer['parallax'] );
			}

			$prepared[] = $layer;
		}

		return $prepared;
	}

	/**
	 * @param array<int, array<string, mixed>> $layers Prepared layers.
	 * @return bool
	 */
	public static function has_parallax_layer( array $layers ) {
		foreach ( $layers as $layer ) {
			if ( ! empty( $layer['parallax'] ) ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Inline style for a single layer surface.
	 *
	 * @param array<string, mixed> $layer Layer row.
	 * @return string
	 */
	public static function layer_inline_style( array $layer ) {
		$parts = array();

		if ( 'image' === ( $layer['type'] ?? 'image' ) && ! empty( $layer['image_url'] ) ) {
			$parts[] = 'background-image:url(' . esc_url( $layer['image_url'] ) . ')';
			$parts[] = 'background-position:' . esc_attr( (string) ( $layer['focal_position'] ?? 'center center' ) );
		}

		$opacity = isset( $layer['opacity'] ) ? (int) $layer['opacity'] : 100;
		$opacity = max( 0, min( 100, $opacity ) );
		if ( 100 !== $opacity ) {
			$parts[] = 'opacity:' . ( $opacity / 100 );
		}

		$blend = self::sanitize_blend_mode( $layer['blend_mode'] ?? 'normal' );
		if ( 'normal' !== $blend ) {
			$parts[] = 'mix-blend-mode:' . $blend;
		}

		return implode( ';', $parts );
	}
}
