<?php
/** Shared schemas, state handling and reads for the OceanWP design bridge. */

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

define( 'WPAGENT_OCEANWP_MIN_VERSION', '4.1.0' );
define( 'WPAGENT_OCEANWP_LOCK_OPTION', 'wp_agent_oceanwp_settings_lock' );
define( 'WPAGENT_OCEANWP_LOCK_TTL', 300 );
define( 'WPAGENT_OCEANWP_MAX_OPTION_BYTES', 2097152 );
define( 'WPAGENT_OCEANWP_MAX_ITEMS', 5000 );

/** @return array<string,mixed> */
function wpagent_oceanwp_layout_schema(): array {
	$sidebar = array( 'type' => 'string', 'enum' => array( 'right-sidebar', 'left-sidebar', 'full-width', 'full-screen', 'both-sidebars' ) );
	return array(
		'type'                 => 'object',
		'properties'           => array(
			'site_layout'         => array( 'type' => 'string', 'enum' => array( 'wide', 'boxed', 'separate' ) ),
			'container_width'     => array( 'type' => 'integer', 'minimum' => 1, 'maximum' => 4096 ),
			'container_width_unit' => array( 'type' => 'string', 'enum' => array( 'px', '%', 'em', 'rem', 'vw' ) ),
			'page_layout'         => $sidebar,
			'blog_archive_layout' => $sidebar,
			'blog_single_layout'  => $sidebar,
		),
		'minProperties'        => 1,
		'additionalProperties' => false,
	);
}

/** @return array<string,mixed> */
function wpagent_oceanwp_color_schema(): array {
	return array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 64 );
}

/** @return array<string,mixed> */
function wpagent_oceanwp_colors_schema(): array {
	$color = wpagent_oceanwp_color_schema();
	return array(
		'type'                 => 'object',
		'properties'           => array(
			'primary'       => $color,
			'primary_hover' => $color,
			'links'         => $color,
			'links_hover'   => $color,
			'body_text'     => $color,
			'headings'      => $color,
		),
		'minProperties'        => 1,
		'additionalProperties' => false,
	);
}

/** @return array<string,mixed> */
function wpagent_oceanwp_buttons_schema(): array {
	$color = wpagent_oceanwp_color_schema();
	return array(
		'type'                 => 'object',
		'properties'           => array(
			'background'       => $color,
			'background_hover' => $color,
			'text'             => $color,
			'text_hover'       => $color,
		),
		'minProperties'        => 1,
		'additionalProperties' => false,
	);
}

/** @return array<string,mixed> */
function wpagent_oceanwp_header_schema(): array {
	return array(
		'type'                 => 'object',
		'properties'           => array(
			'style' => array( 'type' => 'string', 'enum' => array( 'minimal', 'transparent', 'top', 'full_screen', 'center', 'medium', 'vertical' ) ),
		),
		'required'             => array( 'style' ),
		'additionalProperties' => false,
	);
}

/** @param string[] $values @return array<string,mixed> */
function wpagent_oceanwp_responsive_enum_schema( array $values ): array {
	$field = array( 'type' => 'string', 'enum' => $values );
	return array(
		'type'                 => 'object',
		'properties'           => array( 'desktop' => $field, 'tablet' => $field, 'mobile' => $field ),
		'minProperties'        => 1,
		'additionalProperties' => false,
	);
}

/** @param string[] $units @return array<string,mixed> */
function wpagent_oceanwp_responsive_number_schema( float $minimum, float $maximum, array $units ): array {
	$number = array( 'type' => 'number', 'minimum' => $minimum, 'maximum' => $maximum );
	return array(
		'type'                 => 'object',
		'properties'           => array(
			'desktop' => $number,
			'tablet'  => $number,
			'mobile'  => $number,
			'unit'    => array( 'type' => 'string', 'enum' => $units ),
		),
		'minProperties'        => 1,
		'additionalProperties' => false,
	);
}

/** @return array<string,mixed> */
function wpagent_oceanwp_typography_schema(): array {
	return array(
		'type'                 => 'object',
		'properties'           => array(
			'target'          => array( 'type' => 'string', 'enum' => array( 'body', 'headings' ) ),
			'font_family'     => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 100 ),
			'color'           => wpagent_oceanwp_color_schema(),
			'font_weight'     => wpagent_oceanwp_responsive_enum_schema( array( 'normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900' ) ),
			'font_size'       => wpagent_oceanwp_responsive_number_schema( 1, 400, array( 'px', 'em', 'rem', 'vw' ) ),
			'letter_spacing'  => wpagent_oceanwp_responsive_number_schema( -10, 20, array( 'px', 'em', 'rem' ) ),
			'line_height'     => wpagent_oceanwp_responsive_number_schema( 0.5, 5, array( '', 'px', 'em', 'rem' ) ),
			'text_transform'  => wpagent_oceanwp_responsive_enum_schema( array( 'none', 'capitalize', 'uppercase', 'lowercase' ) ),
			'text_decoration' => array( 'type' => 'string', 'enum' => array( 'none', 'underline', 'overline', 'line-through' ) ),
		),
		'required'             => array( 'target' ),
		'minProperties'        => 2,
		'additionalProperties' => false,
	);
}

/** @return array<string,array<string,mixed>> */
function wpagent_oceanwp_section_schemas(): array {
	return array(
		'layout'     => wpagent_oceanwp_layout_schema(),
		'colors'     => wpagent_oceanwp_colors_schema(),
		'typography' => wpagent_oceanwp_typography_schema(),
		'buttons'    => wpagent_oceanwp_buttons_schema(),
		'header'     => wpagent_oceanwp_header_schema(),
	);
}

function wpagent_oceanwp_is_active(): bool {
	return 'oceanwp' === get_template();
}

function wpagent_oceanwp_version(): string {
	$theme = wp_get_theme( 'oceanwp' );
	return $theme->exists() ? (string) $theme->get( 'Version' ) : '';
}

function wpagent_oceanwp_runtime_ready(): bool {
	return wpagent_oceanwp_is_active()
		&& class_exists( 'OceanWP_Theme_Class' )
		&& defined( 'OCEANWP_THEME_VERSION' )
		&& version_compare( (string) OCEANWP_THEME_VERSION, WPAGENT_OCEANWP_MIN_VERSION, '>=' );
}

/** @return string|WP_Error */
function wpagent_oceanwp_option_name() {
	$stylesheet = (string) get_stylesheet();
	if ( '' === $stylesheet || strlen( $stylesheet ) > 100 || ! preg_match( '/^[a-z0-9._-]+$/i', $stylesheet ) ) {
		return new WP_Error( 'wpagent_oceanwp_invalid_stylesheet', __( 'OceanWP verwendet einen ungültigen Stylesheet-Bezeichner.', 'wp-agent-companion' ), array( 'status' => 502 ) );
	}
	return 'theme_mods_' . $stylesheet;
}

function wpagent_oceanwp_json_safe( $value, int $depth = 0, int &$nodes = 0 ): bool {
	if ( $depth > 12 || ++$nodes > WPAGENT_OCEANWP_MAX_ITEMS ) {
		return false;
	}
	if ( is_array( $value ) ) {
		foreach ( $value as $key => $child ) {
			if ( ( ! is_int( $key ) && ! is_string( $key ) ) || ! wpagent_oceanwp_json_safe( $child, $depth + 1, $nodes ) ) {
				return false;
			}
		}
		return true;
	}
	if ( is_string( $value ) ) {
		return strlen( $value ) <= 65536 && wp_check_invalid_utf8( $value ) === $value && ! preg_match( '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', $value );
	}
	return is_null( $value ) || is_bool( $value ) || is_int( $value ) || is_float( $value );
}

/** @return array{option_name:string,mods:array{exists:bool,value:array}}|WP_Error */
function wpagent_oceanwp_snapshot() {
	$option_name = wpagent_oceanwp_option_name();
	if ( is_wp_error( $option_name ) ) {
		return $option_name;
	}
	$missing = new stdClass();
	$value   = get_option( $option_name, $missing );
	$exists  = ! ( $value instanceof stdClass );
	if ( $exists && ! is_array( $value ) ) {
		return new WP_Error( 'wpagent_oceanwp_invalid_state', __( 'OceanWP enthält einen ungültigen Einstellungsstand.', 'wp-agent-companion' ), array( 'status' => 502 ) );
	}
	$value = $exists ? $value : array();
	$nodes = 0;
	if ( strlen( maybe_serialize( $value ) ) > WPAGENT_OCEANWP_MAX_OPTION_BYTES || ! wpagent_oceanwp_json_safe( $value, 0, $nodes ) ) {
		return new WP_Error( 'wpagent_oceanwp_state_too_large', __( 'Der OceanWP-Einstellungsstand ist zu groß oder ungültig für eine sichere Transaktion.', 'wp-agent-companion' ), array( 'status' => 502 ) );
	}
	return array( 'option_name' => $option_name, 'mods' => array( 'exists' => $exists, 'value' => $value ) );
}

/** @param array{option_name:string,mods:array{exists:bool,value:array}} $snapshot */
function wpagent_oceanwp_state_hash( array $snapshot ): string {
	$parent   = wp_get_theme( get_template() );
	$child    = wp_get_theme( get_stylesheet() );
	$identity = get_template() . '@' . $parent->get( 'Version' ) . '|' . get_stylesheet() . '@' . $child->get( 'Version' );
	return hash( 'sha256', $identity . "\0" . $snapshot['option_name'] . "\0" . ( $snapshot['mods']['exists'] ? '1' : '0' ) . "\0" . maybe_serialize( $snapshot['mods']['value'] ) );
}

/** @return mixed */
function wpagent_oceanwp_mod( array $mods, string $key, $default = '' ) {
	return array_key_exists( $key, $mods ) ? $mods[ $key ] : $default;
}

/** @return string */
function wpagent_oceanwp_color_default( string $key, string $fallback ): string {
	if ( function_exists( 'oceanwp_get_theme_mod_default' ) ) {
		$value = oceanwp_get_theme_mod_default( $key, $fallback );
		return is_string( $value ) ? $value : $fallback;
	}
	return $fallback;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_oceanwp_read_section( string $section, array $mods ) {
	switch ( $section ) {
		case 'layout':
			$container_width = wpagent_oceanwp_mod( $mods, 'ocean_main_container_width', 1200 );
			if ( ! is_numeric( $container_width ) || (float) $container_width !== floor( (float) $container_width ) ) {
				return wpagent_oceanwp_invalid_provider_state();
			}
			$result = array(
				'site_layout'          => wpagent_oceanwp_mod( $mods, 'ocean_main_layout_style', 'wide' ),
				'container_width'      => (int) $container_width,
				'container_width_unit' => wpagent_oceanwp_mod( $mods, 'ocean_main_container_width_unit', 'px' ),
				'page_layout'          => wpagent_oceanwp_mod( $mods, 'ocean_page_single_layout', 'right-sidebar' ),
				'blog_archive_layout'  => wpagent_oceanwp_mod( $mods, 'ocean_blog_archives_layout', 'right-sidebar' ),
				'blog_single_layout'   => wpagent_oceanwp_mod( $mods, 'ocean_blog_single_layout', 'right-sidebar' ),
			);
			return wpagent_oceanwp_validate_provider_object( $result, wpagent_oceanwp_layout_schema() );
		case 'colors':
			return wpagent_oceanwp_read_colors( $mods );
		case 'buttons':
			return wpagent_oceanwp_read_buttons( $mods );
		case 'typography':
			return wpagent_oceanwp_read_typography( $mods );
		case 'header':
			$style = wpagent_oceanwp_mod( $mods, 'ocean_header_style', 'minimal' );
			if ( ! is_string( $style ) || ! in_array( $style, array( 'minimal', 'transparent', 'top', 'full_screen', 'center', 'medium', 'vertical', 'custom' ), true ) ) {
				return wpagent_oceanwp_invalid_provider_state();
			}
			return array( 'style' => $style );
	}
	return new WP_Error( 'wpagent_oceanwp_unknown_section', __( 'Unbekannter OceanWP-Designbereich.', 'wp-agent-companion' ), array( 'status' => 400 ) );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_oceanwp_validate_provider_object( array $value, array $schema ) {
	$valid = rest_validate_value_from_schema( $value, $schema, 'provider' );
	return is_wp_error( $valid ) ? wpagent_oceanwp_invalid_provider_state() : $value;
}

function wpagent_oceanwp_invalid_provider_state(): WP_Error {
	return new WP_Error( 'wpagent_oceanwp_invalid_provider_state', __( 'OceanWP enthält einen ungültigen Einstellungsstand.', 'wp-agent-companion' ), array( 'status' => 502 ) );
}

/** @return array<string,string>|WP_Error */
function wpagent_oceanwp_read_colors( array $mods ) {
	$map = array(
		'primary'       => array( 'ocean_primary_color', wpagent_oceanwp_color_default( 'ocean_primary_color', '#13aff0' ) ),
		'primary_hover' => array( 'ocean_hover_primary_color', wpagent_oceanwp_color_default( 'ocean_hover_primary_color', '#0b7cac' ) ),
		'links'         => array( 'ocean_links_color', wpagent_oceanwp_color_default( 'ocean_links_color', '#333333' ) ),
		'links_hover'   => array( 'ocean_links_color_hover', wpagent_oceanwp_color_default( 'ocean_links_color_hover', '#13aff0' ) ),
		'body_text'     => array( 'body_typography', '' ),
		'headings'      => array( 'headings_typography', '' ),
	);
	$result = array();
	foreach ( $map as $public => $definition ) {
		if ( in_array( $public, array( 'body_text', 'headings' ), true ) ) {
			$typography = wpagent_oceanwp_mod( $mods, $definition[0], array() );
			if ( ! is_array( $typography ) ) {
				return wpagent_oceanwp_invalid_provider_state();
			}
			$value = $typography['color'] ?? '';
		} else {
			$value = wpagent_oceanwp_mod( $mods, $definition[0], $definition[1] );
		}
		$color = wpagent_oceanwp_validate_color( $value, true );
		if ( is_wp_error( $color ) ) {
			return wpagent_oceanwp_invalid_provider_state();
		}
		$result[ $public ] = $color;
	}
	return $result;
}

/** @return array<string,string>|WP_Error */
function wpagent_oceanwp_read_buttons( array $mods ) {
	$map = array(
		'background'       => array( 'ocean_theme_button_bg', wpagent_oceanwp_color_default( 'ocean_theme_button_bg', '#13aff0' ) ),
		'background_hover' => array( 'ocean_theme_button_hover_bg', wpagent_oceanwp_color_default( 'ocean_theme_button_hover_bg', '#0b7cac' ) ),
		'text'             => array( 'ocean_theme_button_color', '#ffffff' ),
		'text_hover'       => array( 'ocean_theme_button_hover_color', '#ffffff' ),
	);
	$result = array();
	foreach ( $map as $public => $definition ) {
		$color = wpagent_oceanwp_validate_color( wpagent_oceanwp_mod( $mods, $definition[0], $definition[1] ), false );
		if ( is_wp_error( $color ) ) {
			return wpagent_oceanwp_invalid_provider_state();
		}
		$result[ $public ] = $color;
	}
	return $result;
}
