<?php
/**
 * Canvas-derived placeholders for preset HTML templates.
 *
 * Maps canvas element content (text, variable-range prefix) to {slug} tokens
 * so Pro preset HTML can stay in sync with editable canvas values.
 *
 * @package OpusPriceBuilder\Export
 */

declare(strict_types=1);

namespace OpusPriceBuilder\Export;

/**
 * Builds and applies layout-specific placeholder replacements.
 */
class CanvasPlaceholders {

	/**
	 * Replace canvas placeholders in HTML.
	 *
	 * @param string                             $html     Template HTML.
	 * @param array<int, array<string, mixed>> $elements Canvas elements.
	 * @return string
	 */
	public static function apply( string $html, array $elements ): string {
		$replacements = self::buildReplacements( $elements );

		if ( empty( $replacements ) ) {
			return $html;
		}

		return str_replace( array_keys( $replacements ), array_values( $replacements ), $html );
	}

	/**
	 * Build placeholder map from canvas elements.
	 *
	 * - text elements → `{slug}` from element `text`
	 * - variablePriceRange → `{slug}Prefix` from element `prefixText`
	 *
	 * Slug is camelCase from the element `name` (fallback: element `type`).
	 * Duplicate names get a numeric suffix (`text2`, `priceRange3`).
	 *
	 * @param array<int, array<string, mixed>> $elements Canvas elements.
	 * @return array<string, string>
	 */
	public static function buildReplacements( array $elements ): array {
		if ( empty( $elements ) ) {
			return array();
		}

		$replacements = array();
		$slug_counts  = array();

		foreach ( $elements as $element ) {
			if ( ! is_array( $element ) || ! empty( $element['hidden'] ) ) {
				continue;
			}

			$type = (string) ( $element['type'] ?? '' );
			$name = (string) ( $element['name'] ?? $type );
			$slug = self::nameToSlug( $name );

			if ( '' === $slug ) {
				$slug = self::nameToSlug( $type );
			}

			if ( '' === $slug ) {
				continue;
			}

			$slug = self::dedupeSlug( $slug, $slug_counts );

			if ( 'text' === $type ) {
				$replacements[ '{' . $slug . '}' ] = esc_html( (string) ( $element['text'] ?? '' ) );
				continue;
			}

			if ( 'variablePriceRange' === $type ) {
				$display_mode = (string) ( $element['displayMode'] ?? 'range' );
				$default      = 'up-to' === $display_mode ? 'Up to' : 'From';
				$prefix       = (string) ( $element['prefixText'] ?? $default );
				$replacements[ '{' . $slug . 'Prefix}' ] = esc_html( $prefix );
			}
		}

		/**
		 * Filter canvas-derived placeholder replacements before they are applied.
		 *
		 * @param array<string, string>            $replacements Placeholder map.
		 * @param array<int, array<string, mixed>> $elements     Canvas elements.
		 */
		return (array) apply_filters( 'oppb_canvas_placeholders', $replacements, $elements );
	}

	/**
	 * Convert a display name to a camelCase slug.
	 *
	 * @param string $name Element name or type.
	 * @return string
	 */
	private static function nameToSlug( string $name ): string {
		$name = trim( $name );

		if ( '' === $name ) {
			return '';
		}

		$parts = preg_split( '/\s+/', strtolower( $name ), -1, PREG_SPLIT_NO_EMPTY );

		if ( empty( $parts ) ) {
			return '';
		}

		$slug = array_shift( $parts );

		foreach ( $parts as $part ) {
			$slug .= ucfirst( $part );
		}

		return $slug;
	}

	/**
	 * Ensure unique slugs when multiple elements share a name.
	 *
	 * @param string               $slug   Base slug.
	 * @param array<string, int>   $counts Slug occurrence counts (by reference).
	 * @return string
	 */
	private static function dedupeSlug( string $slug, array &$counts ): string {
		if ( ! isset( $counts[ $slug ] ) ) {
			$counts[ $slug ] = 1;
			return $slug;
		}

		++$counts[ $slug ];

		return $slug . $counts[ $slug ];
	}
}
