<?php
/**
 * Hero layout presets — bundled marketing layouts and JSON import/export.
 *
 * @package Rsmspark_Content_Widgets
 */

defined( 'ABSPATH' ) || exit;

/**
 * Rsmspark_Content_Widgets_Hero_Presets
 */
class Rsmspark_Content_Widgets_Hero_Presets {

	const EXPORT_PLUGIN = 'rsmspark-content-widgets-hero';

	/**
	 * Attribute keys stored in a preset / export payload.
	 *
	 * @return string[]
	 */
	public static function attribute_keys() {
		return array(
			'headline',
			'subheadline',
			'background_image',
			'background_image_src',
			'overlay_opacity',
			'min_height',
			'cta_text',
			'cta_url',
			'layout',
			'split_ratio',
			'image_side',
			'vertical_align',
			'mobile_order',
			'focal_preset',
			'focal_x',
			'focal_y',
			'background_layers',
		);
	}

	/**
	 * @return array<string, array{label: string, description?: string, pro?: bool, config: array<string, mixed>}>
	 */
	public static function all() {
		$presets = array(
			'centered-dark-cta' => array(
				'label'       => __( 'Centered dark overlay + CTA', 'rsm-content-widgets' ),
				'description' => __( 'Full-width centered hero with strong overlay — rsmspark homepage style.', 'rsm-content-widgets' ),
				'config'      => array(
					'layout'          => 'centered',
					'overlay_opacity' => 55,
					'min_height'      => 520,
					'focal_preset'    => 'center',
					'headline'        => __( 'Professional WordPress Tools', 'rsm-content-widgets' ),
					'subheadline'     => __( 'Eleven powerful plugins. One suite. Start free.', 'rsm-content-widgets' ),
					'cta_text'        => __( 'Get Started', 'rsm-content-widgets' ),
					'cta_url'         => '#',
				),
			),
			'centered-minimal'  => array(
				'label'       => __( 'Centered minimal overlay', 'rsm-content-widgets' ),
				'description' => __( 'Light overlay so photography stays visible.', 'rsm-content-widgets' ),
				'config'      => array(
					'layout'          => 'centered',
					'overlay_opacity' => 25,
					'min_height'      => 380,
					'focal_preset'    => 'center',
					'headline'        => __( 'Your headline here', 'rsm-content-widgets' ),
					'subheadline'     => __( 'Supporting message under the title.', 'rsm-content-widgets' ),
				),
			),
			'split-product'     => array(
				'label'       => __( 'Split product hero', 'rsm-content-widgets' ),
				'description' => __( '40/60 split with image on the right — product or screenshot focus.', 'rsm-content-widgets' ),
				'config'      => array(
					'layout'         => 'split',
					'split_ratio'    => '40-60',
					'image_side'     => 'right',
					'vertical_align' => 'center',
					'mobile_order'   => 'content-first',
					'min_height'     => 440,
					'overlay_opacity' => 0,
					'headline'       => __( 'Showcase your product', 'rsm-content-widgets' ),
					'subheadline'    => __( 'Pair persuasive copy with a crisp screenshot or photo.', 'rsm-content-widgets' ),
					'cta_text'       => __( 'Learn more', 'rsm-content-widgets' ),
					'cta_url'        => '#',
				),
			),
			'split-wide-image'  => array(
				'label'       => __( 'Split wide image', 'rsm-content-widgets' ),
				'description' => __( '60/40 split with image left; image stacks first on mobile.', 'rsm-content-widgets' ),
				'config'      => array(
					'layout'         => 'split',
					'split_ratio'    => '60-40',
					'image_side'     => 'left',
					'vertical_align' => 'center',
					'mobile_order'   => 'image-first',
					'min_height'     => 420,
					'overlay_opacity' => 0,
					'headline'       => __( 'Lead with visuals', 'rsm-content-widgets' ),
					'subheadline'    => __( 'Wide image column for dashboards, devices, or team photos.', 'rsm-content-widgets' ),
				),
			),
		);

		return apply_filters( 'rsmspark_content_widgets_hero_presets', $presets );
	}

	/**
	 * @return array<string, string>
	 */
	public static function select_options() {
		$options = array(
			'' => __( '— Select a preset —', 'rsm-content-widgets' ),
		);

		foreach ( self::all() as $slug => $preset ) {
			if ( ! empty( $preset['pro'] ) ) {
				continue;
			}

			$options[ (string) $slug ] = (string) ( $preset['label'] ?? $slug );
		}

		return $options;
	}

	/**
	 * @return array<int, array{slug: string, label: string, description?: string, pro?: bool, config: array<string, mixed>}>
	 */
	public static function choices_for_editor() {
		$choices = array();

		foreach ( self::all() as $slug => $preset ) {
			if ( ! empty( $preset['pro'] ) ) {
				continue;
			}

			$config = is_array( $preset['config'] ?? null ) ? self::sanitize_config( $preset['config'] ) : array();
			$choices[] = array(
				'slug'        => (string) $slug,
				'label'       => (string) ( $preset['label'] ?? $slug ),
				'description' => (string) ( $preset['description'] ?? '' ),
				'pro'         => false,
				'config'      => $config,
			);
		}

		return $choices;
	}

	/**
	 * @param string $slug Preset slug.
	 * @return array<string, mixed>|null
	 */
	public static function get_config( $slug ) {
		$slug    = sanitize_key( (string) $slug );
		$presets = self::all();

		if ( ! isset( $presets[ $slug ] ) ) {
			return null;
		}

		$preset = $presets[ $slug ];

		return is_array( $preset['config'] ?? null ) ? self::sanitize_config( $preset['config'] ) : null;
	}

	/**
	 * Sanitize preset / import config without requiring media attachments.
	 *
	 * @param array<string, mixed> $raw Raw config.
	 * @return array<string, mixed>
	 */
	public static function sanitize_config( array $raw ) {
		if ( ! class_exists( 'Rsmspark_Content_Widgets_Widget_Hero', false ) ) {
			require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/widgets/class-rsmspark-content-widgets-widget-hero.php';
		}
		if ( ! class_exists( 'Rsmspark_Content_Widgets_Hero_Layers', false ) ) {
			require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/class-rsmspark-content-widgets-hero-layers.php';
		}

		$layout = sanitize_key( (string) ( $raw['layout'] ?? 'centered' ) );
		if ( ! in_array( $layout, array( 'centered', 'split' ), true ) ) {
			$layout = 'centered';
		}

		$split_ratio = sanitize_key( (string) ( $raw['split_ratio'] ?? '50-50' ) );
		if ( ! in_array( $split_ratio, array( '50-50', '40-60', '60-40' ), true ) ) {
			$split_ratio = '50-50';
		}

		$image_side = sanitize_key( (string) ( $raw['image_side'] ?? 'right' ) );
		if ( ! in_array( $image_side, array( 'left', 'right' ), true ) ) {
			$image_side = 'right';
		}

		$vertical_align = sanitize_key( (string) ( $raw['vertical_align'] ?? 'center' ) );
		if ( ! in_array( $vertical_align, array( 'top', 'center', 'bottom' ), true ) ) {
			$vertical_align = 'center';
		}

		$mobile_order = sanitize_key( (string) ( $raw['mobile_order'] ?? 'content-first' ) );
		if ( ! in_array( $mobile_order, array( 'content-first', 'image-first' ), true ) ) {
			$mobile_order = 'content-first';
		}

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

		$min_height = isset( $raw['min_height'] ) ? (int) $raw['min_height'] : 320;
		$min_height = max( 200, min( 900, $min_height ) );

		$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 );

		$background_image = $raw['background_image'] ?? '';
		if ( is_numeric( $background_image ) ) {
			$background_image = (string) absint( $background_image );
		} else {
			$background_image = sanitize_text_field( (string) $background_image );
		}

		$config = array(
			'headline'             => sanitize_text_field( (string) ( $raw['headline'] ?? '' ) ),
			'subheadline'          => sanitize_textarea_field( (string) ( $raw['subheadline'] ?? '' ) ),
			'background_image'     => $background_image,
			'background_image_src' => esc_url_raw( trim( (string) ( $raw['background_image_src'] ?? '' ) ) ),
			'overlay_opacity'      => $overlay,
			'min_height'           => $min_height,
			'cta_text'             => sanitize_text_field( (string) ( $raw['cta_text'] ?? '' ) ),
			'cta_url'              => esc_url_raw( trim( (string) ( $raw['cta_url'] ?? '' ) ) ),
			'layout'               => $layout,
			'split_ratio'          => $split_ratio,
			'image_side'           => $image_side,
			'vertical_align'       => $vertical_align,
			'mobile_order'         => $mobile_order,
			'focal_preset'         => $focal_preset,
			'focal_x'              => $focal_x,
			'focal_y'              => $focal_y,
			'background_layers'    => self::sanitize_layers_for_preset( $raw['background_layers'] ?? array() ),
		);

		return self::apply_pro_gates( $config );
	}

	/**
	 * @param mixed $items Raw layer rows.
	 * @return array<int, array<string, mixed>>
	 */
	private static function sanitize_layers_for_preset( $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 ) {
			if ( ! is_array( $row ) ) {
				continue;
			}

			$type = 'image';

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

			$image = $row['image'] ?? 0;
			if ( is_numeric( $image ) ) {
				$image = absint( $image );
			} else {
				$image = sanitize_text_field( (string) $image );
			}

			$out[] = array(
				'type'         => $type,
				'image'        => $image,
				'image_src'    => esc_url_raw( trim( (string) ( $row['image_src'] ?? '' ) ) ),
				'opacity'      => $opacity,
				'blend_mode'   => Rsmspark_Content_Widgets_Hero_Layers::sanitize_blend_mode( $row['blend_mode'] ?? 'normal' ),
				'focal_preset' => Rsmspark_Content_Widgets_Focal_Point::sanitize_preset( $row['focal_preset'] ?? 'center' ),
				'focal_x'      => Rsmspark_Content_Widgets_Focal_Point::sanitize_coord( $row['focal_x'] ?? 50 ),
				'focal_y'      => Rsmspark_Content_Widgets_Focal_Point::sanitize_coord( $row['focal_y'] ?? 50 ),
				'parallax'     => false,
			);

			if ( count( $out ) >= Rsmspark_Content_Widgets_Hero_Layers::MAX_LAYERS ) {
				break;
			}
		}

		return $out;
	}

	/**
	 * Strip Pro-only fields when license is inactive.
	 *
	 * @param array<string, mixed> $config Config.
	 * @return array<string, mixed>
	 */
	public static function apply_pro_gates( array $config ) {
		$config['background_video_url'] = '';
		$config['parallax']             = false;

		if ( ! empty( $config['background_layers'] ) && is_array( $config['background_layers'] ) ) {
			$layers = array();
			foreach ( $config['background_layers'] as $layer ) {
				if ( ! is_array( $layer ) ) {
					continue;
				}
				if ( 'video' === ( $layer['type'] ?? 'image' ) ) {
					continue;
				}
				$layer['parallax'] = false;
				$layers[]          = $layer;
			}
			$config['background_layers'] = $layers;
		}

		return $config;
	}

	/**
	 * Merge preset config into attributes, optionally preserving existing media.
	 *
	 * @param array<string, mixed> $config  Preset config.
	 * @param array<string, mixed> $current Current attributes.
	 * @param bool                 $preserve_media Keep current image/video when preset omits them.
	 * @return array<string, mixed>
	 */
	public static function merge_config( array $config, array $current = array(), $preserve_media = true ) {
		$config  = self::sanitize_config( $config );
		$current = is_array( $current ) ? $current : array();
		$merged  = array_merge( $current, $config );

		if ( ! $preserve_media ) {
			return self::pick_attributes( $merged );
		}

		$media_keys = array(
			'background_image',
			'background_image_src',
			'background_video_url',
		);

		foreach ( $media_keys as $key ) {
			$preset_has_media = self::config_has_media_value( $key, $config );
			if ( ! $preset_has_media && array_key_exists( $key, $current ) ) {
				$merged[ $key ] = $current[ $key ];
			}
		}

		if ( empty( $config['background_layers'] ) && ! empty( $current['background_layers'] ) ) {
			$merged['background_layers'] = $current['background_layers'];
		} elseif ( ! empty( $config['background_layers'] ) && is_array( $config['background_layers'] ) ) {
			$merged['background_layers'] = self::merge_layer_media( $config['background_layers'], $current['background_layers'] ?? array() );
		}

		return self::pick_attributes( $merged );
	}

	/**
	 * @param string               $key    Attribute key.
	 * @param array<string, mixed> $config Config slice.
	 * @return bool
	 */
	private static function config_has_media_value( $key, array $config ) {
		if ( ! array_key_exists( $key, $config ) ) {
			return false;
		}

		$value = $config[ $key ];
		if ( 'background_image' === $key ) {
			if ( is_numeric( $value ) ) {
				return (int) $value > 0;
			}
			return '' !== trim( (string) $value );
		}

		return '' !== trim( (string) $value );
	}

	/**
	 * Keep existing layer images when preset layer rows have empty media.
	 *
	 * @param array<int, array<string, mixed>> $preset_layers Preset layers.
	 * @param mixed                            $current_layers Current layers.
	 * @return array<int, array<string, mixed>>
	 */
	private static function merge_layer_media( array $preset_layers, $current_layers ) {
		if ( ! is_array( $current_layers ) ) {
			return $preset_layers;
		}

		$merged = array();
		foreach ( $preset_layers as $index => $layer ) {
			if ( ! is_array( $layer ) ) {
				continue;
			}

			$current = isset( $current_layers[ $index ] ) && is_array( $current_layers[ $index ] )
				? $current_layers[ $index ]
				: array();

			$has_image = self::layer_has_image( $layer );
			$has_video = '' !== trim( (string) ( $layer['video_url'] ?? '' ) );

			if ( ! $has_image && ! $has_video && ! empty( $current ) ) {
				if ( self::layer_has_image( $current ) ) {
					$layer['image']     = $current['image'] ?? 0;
					$layer['image_src'] = $current['image_src'] ?? '';
				}
				if ( '' !== trim( (string) ( $current['video_url'] ?? '' ) ) && 'video' === ( $layer['type'] ?? '' ) ) {
					$layer['video_url'] = $current['video_url'];
				}
			}

			$merged[] = $layer;
		}

		return $merged;
	}

	/**
	 * @param array<string, mixed> $layer Layer row.
	 * @return bool
	 */
	private static function layer_has_image( array $layer ) {
		if ( is_numeric( $layer['image'] ?? 0 ) && (int) $layer['image'] > 0 ) {
			return true;
		}
		return '' !== trim( (string) ( $layer['image_src'] ?? '' ) );
	}

	/**
	 * @param array<string, mixed> $attrs Raw attributes.
	 * @return array<string, mixed>
	 */
	public static function pick_attributes( array $attrs ) {
		$out = array();
		foreach ( self::attribute_keys() as $key ) {
			if ( array_key_exists( $key, $attrs ) ) {
				$out[ $key ] = $attrs[ $key ];
			}
		}
		return $out;
	}

	/**
	 * @param array<string, mixed> $attrs Block / widget attributes.
	 * @param string               $label Export label.
	 * @return array<string, mixed>
	 */
	public static function build_export_payload( array $attrs, $label = '' ) {
		$config = self::pick_attributes( $attrs );
		$config = self::sanitize_config( $config );

		return array(
			'plugin'      => self::EXPORT_PLUGIN,
			'version'     => RSMSPARK_CONTENT_WIDGETS_VERSION,
			'exported_at' => gmdate( DATE_ATOM ),
			'label'       => sanitize_text_field( (string) $label ),
			'config'      => $config,
		);
	}

	/**
	 * Extract hero config from any supported export envelope.
	 *
	 * @param array<string, mixed> $payload Decoded export payload.
	 * @return array<string, mixed>|null
	 */
	public static function extract_config_from_payload( array $payload ) {
		$plugin = (string) ( $payload['plugin'] ?? '' );

		if ( self::EXPORT_PLUGIN === $plugin && is_array( $payload['config'] ?? null ) ) {
			return $payload['config'];
		}

		if (
			'rsmspark-content-widgets' === $plugin
			&& 'hero' === sanitize_key( (string) ( $payload['block'] ?? '' ) )
			&& is_array( $payload['attributes'] ?? null )
		) {
			return self::pick_attributes( $payload['attributes'] );
		}

		return null;
	}

	/**
	 * @param mixed $payload Decoded JSON or array.
	 * @return array{config: array<string, mixed>, label: string}|null
	 */
	public static function parse_import_payload( $payload ) {
		if ( is_string( $payload ) ) {
			$payload = json_decode( $payload, true );
		}

		if ( ! is_array( $payload ) ) {
			return null;
		}

		$config = self::extract_config_from_payload( $payload );
		if ( null === $config ) {
			return null;
		}

		return array(
			'label'  => sanitize_text_field( (string) ( $payload['label'] ?? '' ) ),
			'config' => self::sanitize_config( $config ),
		);
	}

	/**
	 * Flatten SiteOrigin sectioned instance into preset merge input.
	 *
	 * @param array<string, mixed> $instance SO widget instance.
	 * @return array<string, mixed>
	 */
	public static function flatten_siteorigin_instance( array $instance ) {
		if ( ! class_exists( 'Rsmspark_Content_Widgets_Widget_Hero', false ) ) {
			require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/widgets/class-rsmspark-content-widgets-widget-hero.php';
		}
		if ( ! class_exists( 'Rsmspark_Content_Widgets_Hero_Layers', false ) ) {
			require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/class-rsmspark-content-widgets-hero-layers.php';
		}

		$content    = is_array( $instance['content'] ?? null ) ? $instance['content'] : array();
		$appearance = is_array( $instance['appearance'] ?? null ) ? $instance['appearance'] : array();
		$flat       = array_merge( $content, $appearance );

		if ( isset( $flat['cta_url'] ) ) {
			$flat['cta_url'] = Rsmspark_Content_Widgets_Render::link_url( $flat['cta_url'] );
		}

		if ( empty( $flat['background_image_src'] ) && ! empty( $flat['background_image'] ) ) {
			$flat['background_image_src'] = Rsmspark_Content_Widgets_Widget_Hero::background_image_url(
				$flat['background_image'],
				''
			);
		}

		if ( ! empty( $flat['background_layers'] ) && is_array( $flat['background_layers'] ) ) {
			$layers = array();
			foreach ( $flat['background_layers'] as $layer ) {
				if ( ! is_array( $layer ) ) {
					continue;
				}
				if ( empty( $layer['image_src'] ) && ! empty( $layer['image'] ) ) {
					$layer['image_src'] = Rsmspark_Content_Widgets_Widget_Hero::background_image_url(
						$layer['image'],
						''
					);
				}
				$layers[] = $layer;
			}
			$flat['background_layers'] = $layers;
		}

		return $flat;
	}

	/**
	 * Apply preset config onto a SiteOrigin hero instance structure.
	 *
	 * @param array<string, mixed> $instance Current instance.
	 * @param array<string, mixed> $config   Preset config.
	 * @return array<string, mixed>
	 */
	public static function merge_siteorigin_instance( array $instance, array $config ) {
		$flat    = self::flatten_siteorigin_instance( $instance );
		$merged  = self::merge_config( $config, $flat, true );
		$content_keys = array(
			'headline',
			'subheadline',
			'background_image',
			'cta_text',
			'cta_url',
			'layout',
			'split_ratio',
			'image_side',
			'vertical_align',
			'mobile_order',
			'focal_preset',
			'focal_x',
			'focal_y',
		);
		$appearance_keys = array(
			'overlay_opacity',
			'min_height',
			'background_video_url',
			'parallax',
			'background_layers',
		);

		$content    = is_array( $instance['content'] ?? null ) ? $instance['content'] : array();
		$appearance = is_array( $instance['appearance'] ?? null ) ? $instance['appearance'] : array();

		foreach ( $content_keys as $key ) {
			if ( array_key_exists( $key, $merged ) ) {
				$content[ $key ] = $merged[ $key ];
			}
		}

		foreach ( $appearance_keys as $key ) {
			if ( array_key_exists( $key, $merged ) ) {
				$appearance[ $key ] = $merged[ $key ];
			}
		}

		$instance['content']    = $content;
		$instance['appearance'] = $appearance;

		return $instance;
	}
}
