<?php
/** Shared infrastructure for the verified Astra design bridge. */

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

define( 'WPAGENT_ASTRA_LOCK_OPTION', 'wp_agent_astra_settings_lock' );
define( 'WPAGENT_ASTRA_LOCK_TTL', 300 );
define( 'WPAGENT_ASTRA_MAX_OPTION_BYTES', 2097152 );
define( 'WPAGENT_ASTRA_MAX_READBACK_BYTES', 131072 );

function wpagent_astra_responsive_font_size_schema(): array {
	$values = array();
	foreach ( array( 'desktop', 'tablet', 'mobile' ) as $device ) {
		$values[ $device ] = array( 'type' => 'string', 'maxLength' => 8, 'pattern' => '^(?:0|[1-9][0-9]{0,2})(?:\.[0-9]{1,3})?$' );
		$values[ $device . '-unit' ] = array( 'type' => 'string', 'enum' => array( 'px', 'em', 'rem', 'vw' ) );
	}
	return array(
		'type'                 => 'object',
		'properties'           => $values,
		'minProperties'        => 1,
		'additionalProperties' => false,
	);
}

function wpagent_astra_font_schema( bool $with_size ): array {
	$properties = array(
		'font_family'    => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 100 ),
		'font_weight'    => array( 'type' => 'string', 'pattern' => '^(?:[1-9]00|normal|bold)$' ),
		'line_height'    => array( 'type' => 'string', 'maxLength' => 12 ),
		'text_transform' => array( 'type' => 'string', 'enum' => array( 'uppercase', 'lowercase', 'capitalize', 'none', '' ) ),
		'letter_spacing' => array( 'type' => 'string', 'maxLength' => 16 ),
	);
	if ( $with_size ) {
		$properties['font_size'] = wpagent_astra_responsive_font_size_schema();
	}
	return array(
		'type'                 => 'object',
		'properties'           => $properties,
		'minProperties'        => 1,
		'additionalProperties' => false,
	);
}

function wpagent_astra_background_schema(): array {
	$device = array(
		'type'                 => 'object',
		'properties'           => array( 'background-color' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 64 ) ),
		'minProperties'        => 1,
		'additionalProperties' => false,
	);
	$background = array(
		'type'                 => 'object',
		'properties'           => array( 'desktop' => $device, 'tablet' => $device, 'mobile' => $device ),
		'minProperties'        => 1,
		'additionalProperties' => false,
	);
	return array(
		'type'                 => 'object',
		'properties'           => array( 'site_background' => $background, 'content_background' => $background ),
		'minProperties'        => 1,
		'additionalProperties' => false,
	);
}

function wpagent_astra_button_schema(): array {
	$properties = array(
		'button_type'            => array( 'type' => 'string', 'enum' => array( 'primary', 'secondary', 'both' ), 'default' => 'primary' ),
		'preset'                 => array( 'type' => 'string', 'enum' => array( 'button_01', 'button_02', 'button_03', 'button_04', 'button_05', 'button_06' ) ),
		'text_color'             => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 64 ),
		'text_hover_color'       => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 64 ),
		'background_color'       => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 64 ),
		'background_hover_color' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 64 ),
		'border_color'           => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 64 ),
		'border_hover_color'     => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 64 ),
	);
	return array(
		'type'                 => 'object',
		'properties'           => $properties,
		'minProperties'        => 1,
		'additionalProperties' => false,
	);
}

/** @return array<string,array{label:string,read:string,write:string,input_schema:array}> */
function wpagent_astra_operation_catalog(): array {
	return array_merge(
		array(
		'container'         => array(
			'label'        => __( 'Container', 'wp-agent-companion' ),
			'read'         => 'astra/get-container-layout',
			'write'        => 'astra/update-container-layout',
			'input_schema' => array(
				'type'                 => 'object',
				'properties'           => array(
					'container_layout' => array( 'type' => 'string', 'enum' => array( 'normal-width-container', 'narrow-width-container', 'full-width-container' ) ),
					'container_style'  => array( 'type' => 'string', 'enum' => array( 'boxed', 'unboxed' ) ),
				),
				'minProperties'        => 1,
				'additionalProperties' => false,
			),
		),
		'body-font'         => array(
			'label' => __( 'Fließtext-Typografie', 'wp-agent-companion' ), 'read' => 'astra/get-font-body',
			'write' => 'astra/update-font-body', 'input_schema' => wpagent_astra_font_schema( true ),
		),
		'heading-font'      => array(
			'label' => __( 'Überschriften-Typografie', 'wp-agent-companion' ), 'read' => 'astra/get-font-heading',
			'write' => 'astra/update-font-heading', 'input_schema' => wpagent_astra_font_schema( false ),
		),
		'background-colors' => array(
			'label' => __( 'Hintergrundfarben', 'wp-agent-companion' ), 'read' => 'astra/get-color-background',
			'write' => 'astra/update-color-background', 'input_schema' => wpagent_astra_background_schema(),
		),
		'buttons'           => array(
			'label' => __( 'Globale Buttons', 'wp-agent-companion' ), 'read' => 'astra/get-global-buttons',
			'write' => 'astra/update-global-buttons', 'input_schema' => wpagent_astra_button_schema(),
		),
		),
		wpagent_astra_extended_operation_catalog()
	);
}

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

function wpagent_astra_is_active(): bool {
	return 'astra' === get_template();
}

function wpagent_astra_abilities_enabled(): bool {
	if ( ! wpagent_astra_read_abilities_enabled() ) {
		return false;
	}
	$settings = get_option( 'astra_admin_settings', array() );
	return is_array( $settings )
		&& ( ! array_key_exists( 'enable_edit_abilities', $settings ) || ! empty( $settings['enable_edit_abilities'] ) );
}

function wpagent_astra_read_abilities_enabled(): bool {
	$settings = get_option( 'astra_admin_settings', array() );
	return is_array( $settings ) && ! empty( $settings['enable_abilities'] );
}

/**
 * Reads the exact option row alongside its decoded provider value.
 *
 * The raw bytes and autoload flag are part of the concurrency boundary. A
 * decoded array alone cannot distinguish a provider write from a row rewrite
 * that preserves PHP's serialized value but changes cache behaviour.
 *
 * @return array{exists:bool,value:array,raw:string,autoload:string}|WP_Error
 */
function wpagent_astra_option_snapshot( string $option_name, string $invalid_code, string $invalid_message ) {
	$row = wpagent_companion_read_option_row_direct( $option_name );
	if ( ! is_array( $row ) ) {
		return array( 'exists' => false, 'value' => array(), 'raw' => '', 'autoload' => '' );
	}
	if ( strlen( $row['raw'] ) > WPAGENT_ASTRA_MAX_OPTION_BYTES ) {
		return new WP_Error( 'wpagent_astra_state_too_large', __( 'Der Astra-Einstellungsstand ist zu groß für eine sichere Transaktion.', 'wp-agent-companion' ), array( 'status' => 502 ) );
	}
	$value = maybe_unserialize( $row['raw'] );
	if ( ! is_array( $value ) ) {
		return new WP_Error( $invalid_code, $invalid_message, array( 'status' => 502 ) );
	}
	return array( 'exists' => true, 'value' => $value, 'raw' => $row['raw'], 'autoload' => $row['autoload'] );
}

/** @return array{exists:bool,value:array,raw:string,autoload:string,palette_exists:bool,palette_value:array,palette_raw:string,palette_autoload:string}|WP_Error */
function wpagent_astra_snapshot() {
	$settings = wpagent_astra_option_snapshot(
		'astra-settings',
		'wpagent_astra_invalid_state',
		__( 'Astra enthält einen ungültigen Einstellungsstand.', 'wp-agent-companion' )
	);
	$palette  = wpagent_astra_option_snapshot(
		'astra-color-palettes',
		'wpagent_astra_invalid_palette_state',
		__( 'Astra enthält einen ungültigen Palettenstand.', 'wp-agent-companion' )
	);
	if ( is_wp_error( $settings ) || is_wp_error( $palette ) ) {
		return is_wp_error( $settings ) ? $settings : $palette;
	}
	if ( strlen( $settings['raw'] ) + strlen( $palette['raw'] ) > WPAGENT_ASTRA_MAX_OPTION_BYTES ) {
		return new WP_Error( 'wpagent_astra_state_too_large', __( 'Der Astra-Einstellungsstand ist zu groß für eine sichere Transaktion.', 'wp-agent-companion' ), array( 'status' => 502 ) );
	}
	return array(
		'exists'           => $settings['exists'],
		'value'            => $settings['value'],
		'raw'              => $settings['raw'],
		'autoload'         => $settings['autoload'],
		'palette_exists'   => $palette['exists'],
		'palette_value'    => $palette['value'],
		'palette_raw'      => $palette['raw'],
		'palette_autoload' => $palette['autoload'],
	);
}

/** @param array{exists:bool,value:array,raw:string,autoload:string,palette_exists:bool,palette_value:array,palette_raw:string,palette_autoload:string} $snapshot */
function wpagent_astra_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['exists'] ? '1' : '0' ) . "\0" . $snapshot['raw'] . "\0" . $snapshot['autoload'] . "\0" .
		( $snapshot['palette_exists'] ? '1' : '0' ) . "\0" . $snapshot['palette_raw'] . "\0" . $snapshot['palette_autoload']
	);
}

/** @return array<string,array{label:string,read:string,write:string,input_schema:array}> */
function wpagent_astra_available_operations(): array {
	if ( ! wpagent_astra_is_active() || ! wpagent_astra_abilities_enabled() ) {
		return array();
	}
	return array_filter(
		wpagent_astra_operation_catalog(),
		static fn( array $operation ): bool => (bool) wpagent_companion_get_registered_ability( $operation['read'] ) && (bool) wpagent_companion_get_registered_ability( $operation['write'] )
	);
}

/** @return array<string,mixed>|WP_Error */
function wpagent_astra_provider_data( string $ability_name, array $input = array() ) {
	$ability = wpagent_companion_get_registered_ability( $ability_name );
	if ( ! $ability ) {
		return new WP_Error( 'wpagent_astra_provider_missing', __( 'Astra stellt die benötigte native Ability nicht bereit.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$fresh_options = static function ( $options ) {
		$current = get_option( 'astra-settings', array() );
		return is_array( $current ) ? $current : $options;
	};
	add_filter( 'astra_get_option_array', $fresh_options, PHP_INT_MAX );
	try {
		if ( 'astra/update-global-palette' === $ability_name && class_exists( 'Astra_Update_Global_Palette' ) ) {
			// Astra 4.13 declares the numeric color properties as stdClass. The
			// WordPress 7 validator tries to index that declaration as an array and
			// fails before Astra's callback runs. Our stricter named-color schema has
			// already validated the input, so invoke Astra's own implementation
			// directly until the provider schema is corrected.
			$provider = new Astra_Update_Global_Palette();
			$result   = $provider->execute( $input );
		} else {
			$result = $ability->execute( $input );
		}
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_astra_provider_failed', __( 'Die native Astra-Ability ist unerwartet fehlgeschlagen.', 'wp-agent-companion' ), array( 'status' => 502 ) );
	} finally {
		remove_filter( 'astra_get_option_array', $fresh_options, PHP_INT_MAX );
	}
	if ( is_wp_error( $result ) || ! is_array( $result ) || empty( $result['success'] ) || ! is_array( $result['data'] ?? null ) ) {
		return new WP_Error( 'wpagent_astra_provider_failed', __( 'Astra hat keinen bestätigten Einstellungsstand geliefert.', 'wp-agent-companion' ), array( 'status' => 502 ) );
	}
	$data = $result['data'];
	if ( strlen( maybe_serialize( $data ) ) > WPAGENT_ASTRA_MAX_READBACK_BYTES || ! wpagent_astra_json_safe( $data ) ) {
		return new WP_Error( 'wpagent_astra_readback_invalid', __( 'Astra hat einen ungültigen oder zu großen Einstellungsstand geliefert.', 'wp-agent-companion' ), array( 'status' => 502 ) );
	}
	return $data;
}

function wpagent_astra_json_safe( $value, int $depth = 0, int &$nodes = 0 ): bool {
	if ( $depth > 12 || ++$nodes > 5000 ) {
		return false;
	}
	if ( is_array( $value ) ) {
		foreach ( $value as $key => $child ) {
			if ( ( ! is_int( $key ) && ! is_string( $key ) ) || ! wpagent_astra_json_safe( $child, $depth + 1, $nodes ) ) {
				return false;
			}
		}
		return true;
	}
	if ( is_string( $value ) ) {
		return strlen( $value ) <= 8192 && 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 );
}
