<?php
/**
 * Site editor standards (colors, sizes, fonts) — Spark-shaped storage, CW admin prototype.
 *
 * @package Rsmspark_Editor
 */

defined( 'ABSPATH' ) || exit;

/**
 * Rsmspark_Rich_Text_Standards
 */
final class Rsmspark_Rich_Text_Standards {

	public const OPTION_KEY  = 'rsmspark_editor_standards';
	public const SLOT_COUNT  = 12;

	/**
	 * @return array<string, mixed>
	 */
	public static function get_saved() {
		$raw = get_option( self::OPTION_KEY, array() );
		if ( ! is_array( $raw ) ) {
			$raw = array();
		}

		return array(
			'site_standards_only' => ! empty( $raw['site_standards_only'] ),
			'colors'              => self::normalize_color_slots( $raw['colors'] ?? array() ),
			'sizes'               => self::normalize_size_slots( $raw['sizes'] ?? array() ),
		);
	}

	/**
	 * Merged standards for editor menus and JS bootstrap.
	 *
	 * @return array<string, mixed>
	 */
	public static function get() {
		$saved   = self::get_saved();
		$limits  = Rsmspark_Rich_Text_Registry::custom_size_limits();
		$colors  = self::merge_colors( $saved );
		$sizes   = self::merge_sizes( $saved );

		return array(
			'site_standards_only' => (bool) $saved['site_standards_only'],
			'colors'              => $colors,
			'sizes'               => $sizes,
			'fonts'               => self::font_menu_items(),
			'size_limits'         => $limits,
		);
	}

	/**
	 * @return bool
	 */
	public static function can_configure() {
		$forced = apply_filters( 'rsmspark_editor_standards_admin_enabled', null );
		if ( null !== $forced ) {
			return (bool) $forced;
		}

		return current_user_can( 'manage_options' );
	}

	/**
	 * Whether Pro messaging should show (Spark will enforce licensing later).
	 *
	 * @return bool
	 */
	public static function is_pro_feature() {
		// Free WordPress.org build ships site standards unlocked; no local Pro gate.
		return false;
	}

	/**
	 * Delete saved site standards (colors/sizes slots and flags).
	 *
	 * @return bool
	 */
	public static function reset_to_defaults() {
		if ( ! current_user_can( 'manage_options' ) ) {
			return false;
		}

		return delete_option( self::OPTION_KEY );
	}

	/**
	 * @param string $profile Profile key.
	 * @return array<int, array<string, mixed>>
	 */
	public static function menu_sizes( $profile ) {
		$items = array();

		foreach ( self::menu_palette_sizes() as $size ) {
			$px    = (int) ( $size['px'] ?? 0 );
			$name  = (string) ( $size['name'] ?? '' );
			$label = $px . 'px';

			if ( '' !== $name ) {
				$label .= ' — ' . $name;
			}

			$item = array(
				'label' => $label,
				'px'    => $px,
				'name'  => $name,
			);

			if ( ! empty( $size['class'] ) ) {
				$item['class'] = (string) $size['class'];
			}

			$items[] = $item;
		}

		if ( Rsmspark_Rich_Text_Registry::PROFILE_BODY === $profile && ! empty( $items ) ) {
			array_pop( $items );
		}

		if ( Rsmspark_Rich_Text_Registry::PROFILE_MINIMAL === $profile ) {
			return array_values(
				array_filter(
					$items,
					static function ( $item ) {
						return in_array( (int) $item['px'], array( 16, 18 ), true );
					}
				)
			);
		}

		return $items;
	}

	/**
	 * @return array<int, array<string, mixed>>
	 */
	public static function menu_colors() {
		$items = array();

		foreach ( self::menu_palette_colors() as $color ) {
			$hex  = strtolower( (string) ( $color['hex'] ?? '' ) );
			$name = (string) ( $color['name'] ?? '' );

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

			$item = array(
				'label' => '' !== $name
					? sprintf(
						/* translators: 1: color name, 2: hex value */
						__( '%1$s — %2$s', 'rsm-content-widgets' ),
						$name,
						$hex
					)
					: $hex,
				'hex'   => $hex,
				'name'  => $name,
			);

			if ( ! empty( $color['class'] ) ) {
				$item['class'] = (string) $color['class'];
			}

			$items[] = $item;
		}

		return $items;
	}

	/**
	 * Colors for toolbar menus — configured site slots when present, else merged palette.
	 *
	 * @return array<int, array<string, mixed>>
	 */
	public static function menu_palette_colors() {
		$saved     = self::get_saved();
		$site_rows = self::valid_site_color_rows( $saved );

		if ( ! empty( $site_rows ) ) {
			return self::normalize_palette_color_rows( $site_rows );
		}

		return self::merge_colors( $saved );
	}

	/**
	 * Sizes for toolbar menus — configured site slots when present, else merged palette.
	 *
	 * @return array<int, array<string, mixed>>
	 */
	public static function menu_palette_sizes() {
		$saved     = self::get_saved();
		$site_rows = self::valid_site_size_rows( $saved );

		if ( ! empty( $site_rows ) ) {
			return self::normalize_palette_size_rows( $site_rows );
		}

		return self::merge_sizes( $saved );
	}

	/**
	 * @param array<string, mixed> $saved Saved option payload.
	 * @return array<int, array<string, mixed>>
	 */
	private static function valid_site_color_rows( $saved ) {
		return array_values(
			array_filter(
				$saved['colors'] ?? array(),
				static function ( $row ) {
					return is_array( $row ) && '' !== self::normalize_hex( (string) ( $row['hex'] ?? '' ) );
				}
			)
		);
	}

	/**
	 * @param array<string, mixed> $saved Saved option payload.
	 * @return array<int, array<string, mixed>>
	 */
	private static function valid_site_size_rows( $saved ) {
		return array_values(
			array_filter(
				$saved['sizes'] ?? array(),
				static function ( $row ) {
					return is_array( $row ) && absint( $row['px'] ?? 0 ) > 0;
				}
			)
		);
	}

	/**
	 * @param array<int, array<string, mixed>> $rows Site color rows.
	 * @return array<int, array<string, mixed>>
	 */
	private static function normalize_palette_color_rows( $rows ) {
		$merged = array();

		foreach ( $rows as $row ) {
			$hex = self::normalize_hex( (string) ( $row['hex'] ?? '' ) );
			if ( '' === $hex ) {
				continue;
			}

			$merged[] = array(
				'name'  => sanitize_text_field( (string) ( $row['name'] ?? '' ) ),
				'hex'   => $hex,
				'class' => '',
			);
		}

		return $merged;
	}

	/**
	 * @param array<int, array<string, mixed>> $rows Site size rows.
	 * @return array<int, array<string, mixed>>
	 */
	private static function normalize_palette_size_rows( $rows ) {
		$merged = array();

		foreach ( $rows as $row ) {
			$px = absint( $row['px'] ?? 0 );
			if ( $px <= 0 ) {
				continue;
			}

			$merged[] = array(
				'name'  => sanitize_text_field( (string) ( $row['name'] ?? '' ) ),
				'px'    => $px,
				'class' => '',
			);
		}

		return $merged;
	}

	/**
	 * @return array<int, array<string, string>>
	 */
	public static function font_menu_items() {
		$fonts = apply_filters( 'rsmspark_editor_font_families', array() );
		if ( ! is_array( $fonts ) ) {
			$fonts = array();
		}

		$items = array();
		foreach ( $fonts as $font ) {
			if ( ! is_array( $font ) ) {
				continue;
			}

			$label  = sanitize_text_field( (string) ( $font['label'] ?? '' ) );
			$family = self::sanitize_font_family_stack( (string) ( $font['family'] ?? '' ) );

			if ( '' === $label || '' === $family ) {
				continue;
			}

			$items[] = array(
				'label'  => $label,
				'family' => $family,
			);
		}

		return $items;
	}

	/**
	 * @param array<string, mixed> $post Raw POST.
	 * @return void
	 */
	public static function save_from_request( $post ) {
		if ( ! is_array( $post ) || ! current_user_can( 'manage_options' ) ) {
			return;
		}

		$colors = array();
		for ( $i = 0; $i < self::SLOT_COUNT; $i++ ) {
			$name_key = 'rsmspark_editor_standard_color_name_' . $i;
			$hex_key  = 'rsmspark_editor_standard_color_hex_' . $i;
			$colors[] = array(
				'name' => isset( $post[ $name_key ] ) ? sanitize_text_field( wp_unslash( (string) $post[ $name_key ] ) ) : '',
				'hex'  => isset( $post[ $hex_key ] ) ? self::normalize_hex( wp_unslash( (string) $post[ $hex_key ] ) ) : '',
			);
		}

		$sizes = array();
		for ( $i = 0; $i < self::SLOT_COUNT; $i++ ) {
			$name_key = 'rsmspark_editor_standard_size_name_' . $i;
			$px_key   = 'rsmspark_editor_standard_size_px_' . $i;
			$px       = isset( $post[ $px_key ] ) ? absint( wp_unslash( (string) $post[ $px_key ] ) ) : 0;
			$sizes[]  = array(
				'name' => isset( $post[ $name_key ] ) ? sanitize_text_field( wp_unslash( (string) $post[ $name_key ] ) ) : '',
				'px'   => $px,
			);
		}

		update_option(
			self::OPTION_KEY,
			array(
				'site_standards_only' => isset( $post['rsmspark_editor_site_standards_only'] ),
				'colors'              => self::normalize_color_slots( $colors ),
				'sizes'               => self::normalize_size_slots( $sizes ),
			),
			false
		);
	}

	/**
	 * @param mixed $slots Raw color slots.
	 * @return array<int, array{name:string,hex:string}>
	 */
	private static function normalize_color_slots( $slots ) {
		$out = array();
		if ( ! is_array( $slots ) ) {
			$slots = array();
		}

		for ( $i = 0; $i < self::SLOT_COUNT; $i++ ) {
			$row = $slots[ $i ] ?? array();
			if ( ! is_array( $row ) ) {
				$row = array();
			}

			$out[] = array(
				'name' => sanitize_text_field( (string) ( $row['name'] ?? '' ) ),
				'hex'  => self::normalize_hex( (string) ( $row['hex'] ?? '' ) ),
			);
		}

		return $out;
	}

	/**
	 * @param mixed $slots Raw size slots.
	 * @return array<int, array{name:string,px:int}>
	 */
	private static function normalize_size_slots( $slots ) {
		$limits = Rsmspark_Rich_Text_Registry::custom_size_limits();
		$out    = array();

		if ( ! is_array( $slots ) ) {
			$slots = array();
		}

		for ( $i = 0; $i < self::SLOT_COUNT; $i++ ) {
			$row = $slots[ $i ] ?? array();
			if ( ! is_array( $row ) ) {
				$row = array();
			}

			$px = absint( $row['px'] ?? 0 );
			if ( $px > 0 && ( $px < $limits['min'] || $px > $limits['max'] ) ) {
				$px = 0;
			}

			$out[] = array(
				'name' => sanitize_text_field( (string) ( $row['name'] ?? '' ) ),
				'px'   => $px,
			);
		}

		return $out;
	}

	/**
	 * @param array<string, mixed> $saved Saved option payload.
	 * @return array<int, array<string, mixed>>
	 */
	private static function merge_colors( $saved ) {
		$site_only = ! empty( $saved['site_standards_only'] );
		$merged    = array();
		$seen      = array();

		$append = static function ( $rows ) use ( &$merged, &$seen ) {
			foreach ( $rows as $row ) {
				if ( ! is_array( $row ) ) {
					continue;
				}

				$hex = self::normalize_hex( (string) ( $row['hex'] ?? '' ) );
				if ( '' === $hex || isset( $seen[ $hex ] ) ) {
					continue;
				}

				$seen[ $hex ] = true;
				$merged[]     = array(
					'name'  => sanitize_text_field( (string) ( $row['name'] ?? '' ) ),
					'hex'   => $hex,
					'class' => isset( $row['class'] ) ? sanitize_html_class( (string) $row['class'] ) : '',
				);
			}
		};

		$site_rows = array_filter(
			$saved['colors'] ?? array(),
			static function ( $row ) {
				return is_array( $row ) && '' !== self::normalize_hex( (string) ( $row['hex'] ?? '' ) );
			}
		);

		$append( $site_rows );

		if ( ! $site_only ) {
			$theme = apply_filters( 'rsmspark_editor_theme_colors', array() );
			if ( is_array( $theme ) ) {
				$append( $theme );
			}

			foreach ( Rsmspark_Rich_Text_Registry::color_presets() as $preset ) {
				$append(
					array(
						array(
							'name'  => (string) $preset['label'],
							'hex'   => (string) $preset['hex'],
							'class' => (string) $preset['class'],
						),
					)
				);
			}
		}

		if ( empty( $merged ) ) {
			foreach ( Rsmspark_Rich_Text_Registry::color_presets() as $preset ) {
				$append(
					array(
						array(
							'name'  => (string) $preset['label'],
							'hex'   => (string) $preset['hex'],
							'class' => (string) $preset['class'],
						),
					)
				);
			}
		}

		return $merged;
	}

	/**
	 * @param array<string, mixed> $saved Saved option payload.
	 * @return array<int, array<string, mixed>>
	 */
	private static function merge_sizes( $saved ) {
		$site_only = ! empty( $saved['site_standards_only'] );
		$merged    = array();
		$seen      = array();

		$append = static function ( $rows ) use ( &$merged, &$seen ) {
			foreach ( $rows as $row ) {
				if ( ! is_array( $row ) ) {
					continue;
				}

				$px = absint( $row['px'] ?? 0 );
				if ( $px <= 0 || isset( $seen[ $px ] ) ) {
					continue;
				}

				$seen[ $px ] = true;
				$merged[]    = array(
					'name'  => sanitize_text_field( (string) ( $row['name'] ?? '' ) ),
					'px'    => $px,
					'class' => isset( $row['class'] ) ? sanitize_html_class( (string) $row['class'] ) : '',
				);
			}
		};

		$site_rows = array_filter(
			$saved['sizes'] ?? array(),
			static function ( $row ) {
				return is_array( $row ) && absint( $row['px'] ?? 0 ) > 0;
			}
		);

		$append( $site_rows );

		if ( ! $site_only ) {
			$theme = apply_filters( 'rsmspark_editor_theme_sizes', array() );
			if ( is_array( $theme ) ) {
				$append( $theme );
			}

			foreach ( Rsmspark_Rich_Text_Registry::size_presets() as $preset ) {
				$append(
					array(
						array(
							'name'  => (string) $preset['label'],
							'px'    => (int) $preset['px'],
							'class' => (string) $preset['class'],
						),
					)
				);
			}
		}

		if ( empty( $merged ) ) {
			foreach ( Rsmspark_Rich_Text_Registry::size_presets() as $preset ) {
				$append(
					array(
						array(
							'name'  => (string) $preset['label'],
							'px'    => (int) $preset['px'],
							'class' => (string) $preset['class'],
						),
					)
				);
			}
		}

		return $merged;
	}

	/**
	 * @param string $value Raw hex.
	 * @return string Normalized #rrggbb or empty.
	 */
	private static function normalize_hex( $value ) {
		$value = trim( (string) $value );
		if ( '' === $value ) {
			return '';
		}

		if ( '#' !== $value[0] ) {
			$value = '#' . $value;
		}

		if ( ! preg_match( '/^#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value, $matches ) ) {
			return '';
		}

		$hex = strtolower( $matches[1] );
		if ( 3 === strlen( $hex ) ) {
			$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
		}

		return '#' . $hex;
	}

	/**
	 * @param string $stack Font-family stack.
	 * @return string Sanitized stack or empty.
	 */
	public static function sanitize_font_family_stack( $stack ) {
		$stack = trim( (string) $stack );
		if ( '' === $stack ) {
			return '';
		}

		$parts = array_map( 'trim', explode( ',', $stack ) );
		$safe  = array();

		foreach ( $parts as $part ) {
			$part = trim( $part, " \t\n\r\0\x0B\"'" );
			if ( '' === $part ) {
				continue;
			}

			if ( preg_match( '/^[-a-z0-9 ]+$/i', $part ) ) {
				$safe[] = $part;
			}
		}

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

/**
 * Public standards accessor for other plugins (Spark, Buttons, etc.).
 *
 * @return array<string, mixed>
 */
function rsmspark_editor_get_standards() {
	return Rsmspark_Rich_Text_Standards::get();
}
