<?php
/** Shared contract for verified Ocean Extra per-post page-title overrides. */

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

define( 'WPAGENT_OCEAN_EXTRA_MIN_VERSION', '2.5.8' );
define( 'WPAGENT_OCEAN_EXTRA_TITLE_THEME_VERSION', '4.2.2' );
define( 'WPAGENT_OCEAN_EXTRA_TITLE_MAX_TITLE_CHARS', 160 );
define( 'WPAGENT_OCEAN_EXTRA_TITLE_MAX_SUBHEADING_CHARS', 240 );

/** @return array<string,array{meta:string,values:array<string,string>}> */
function wpagent_oceanwp_extra_title_select_catalog(): array {
	return array(
		'page_header' => array(
			'meta'   => 'ocean_disable_title',
			'values' => array( 'show' => 'enable', 'hide' => 'on' ),
		),
		'heading'     => array(
			'meta'   => 'ocean_disable_heading',
			'values' => array( 'show' => 'enable', 'hide' => 'on' ),
		),
		'style'       => array(
			'meta'   => 'ocean_post_title_style',
			'values' => array(
				'default'          => 'default',
				'centered'         => 'centered',
				'centered-minimal' => 'centered-minimal',
				'solid-color'      => 'solid-color',
			),
		),
	);
}

/** @return array<string,array{meta:string,max:int}> */
function wpagent_oceanwp_extra_title_text_catalog(): array {
	return array(
		'custom_title' => array( 'meta' => 'ocean_post_title', 'max' => WPAGENT_OCEAN_EXTRA_TITLE_MAX_TITLE_CHARS ),
		'subheading'   => array( 'meta' => 'ocean_post_subheading', 'max' => WPAGENT_OCEAN_EXTRA_TITLE_MAX_SUBHEADING_CHARS ),
	);
}

/** @return array<string,array{meta:string}> */
function wpagent_oceanwp_extra_title_meta_catalog(): array {
	$catalog = array();
	foreach ( wpagent_oceanwp_extra_title_select_catalog() as $field => $definition ) {
		$catalog[ $field ] = array( 'meta' => $definition['meta'] );
	}
	foreach ( wpagent_oceanwp_extra_title_text_catalog() as $field => $definition ) {
		$catalog[ $field ] = array( 'meta' => $definition['meta'] );
	}
	return $catalog;
}

/** @return array<string,mixed> */
function wpagent_oceanwp_extra_title_text_override_schema( int $maximum ): array {
	return array(
		'type'                 => 'object',
		'properties'           => array(
			'mode'  => array( 'type' => 'string', 'enum' => array( 'inherit', 'set' ) ),
			'value' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => $maximum ),
		),
		'required'             => array( 'mode' ),
		'additionalProperties' => false,
	);
}

/** @return array<string,mixed> */
function wpagent_oceanwp_extra_title_state_schema(): array {
	return array(
		'type'                 => 'object',
		'properties'           => array(
			'page_header' => array( 'type' => 'string', 'enum' => array( 'inherit', 'show', 'hide' ) ),
			'heading'     => array( 'type' => 'string', 'enum' => array( 'inherit', 'show', 'hide' ) ),
			'style'       => array( 'type' => 'string', 'enum' => array( 'inherit', 'default', 'centered', 'centered-minimal', 'solid-color', 'background-image' ) ),
			'custom_title' => wpagent_oceanwp_extra_title_text_override_schema( WPAGENT_OCEAN_EXTRA_TITLE_MAX_TITLE_CHARS ),
			'subheading'   => wpagent_oceanwp_extra_title_text_override_schema( WPAGENT_OCEAN_EXTRA_TITLE_MAX_SUBHEADING_CHARS ),
		),
		'required'             => array( 'page_header', 'heading', 'style', 'custom_title', 'subheading' ),
		'additionalProperties' => false,
	);
}

/** @return array<string,mixed> */
function wpagent_oceanwp_extra_title_settings_schema(): array {
	return array(
		'type'                 => 'object',
		'properties'           => array(
			'page_header' => array( 'type' => 'string', 'enum' => array( 'inherit', 'show', 'hide' ) ),
			'heading'     => array( 'type' => 'string', 'enum' => array( 'inherit', 'show', 'hide' ) ),
			'style'       => array( 'type' => 'string', 'enum' => array( 'inherit', 'default', 'centered', 'centered-minimal', 'solid-color' ) ),
			'custom_title' => wpagent_oceanwp_extra_title_text_override_schema( WPAGENT_OCEAN_EXTRA_TITLE_MAX_TITLE_CHARS ),
			'subheading'   => wpagent_oceanwp_extra_title_text_override_schema( WPAGENT_OCEAN_EXTRA_TITLE_MAX_SUBHEADING_CHARS ),
		),
		'minProperties'        => 1,
		'maxProperties'        => 5,
		'additionalProperties' => false,
	);
}

function wpagent_oceanwp_extra_version(): string {
	if ( ! class_exists( 'Ocean_Extra' ) ) {
		return '';
	}
	try {
		$file = ( new ReflectionClass( 'Ocean_Extra' ) )->getFileName();
	} catch ( Throwable $error ) {
		return '';
	}
	if ( ! is_string( $file ) || ! is_readable( $file ) ) {
		return '';
	}
	$data    = get_file_data( $file, array( 'Version' => 'Version' ) );
	$version = is_array( $data ) ? (string) ( $data['Version'] ?? '' ) : '';
	return preg_match( '/^\d+(?:\.\d+){1,3}(?:[-+][a-z0-9.-]+)?$/i', $version ) ? $version : '';
}

function wpagent_oceanwp_extra_title_runtime_ready(): bool {
	return wpagent_oceanwp_runtime_ready()
		&& class_exists( 'Ocean_Extra' )
		&& WPAGENT_OCEAN_EXTRA_TITLE_THEME_VERSION === wpagent_oceanwp_version()
		&& WPAGENT_OCEAN_EXTRA_MIN_VERSION === wpagent_oceanwp_extra_version()
		&& function_exists( 'oceanwp_has_page_header' )
		&& function_exists( 'oceanwp_has_page_header_heading' )
		&& function_exists( 'oceanwp_page_header_style' );
}

/** @return true|WP_Error */
function wpagent_oceanwp_extra_title_require_runtime() {
	if ( ! wpagent_oceanwp_is_active() ) {
		return new WP_Error( 'wpagent_oceanwp_inactive', __( 'OceanWP oder ein OceanWP-Child-Theme ist nicht aktiv.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( ! wpagent_oceanwp_extra_title_runtime_ready() ) {
		return new WP_Error( 'wpagent_ocean_extra_title_runtime_missing', __( 'Der geprüfte Seitentitel-Weg erfordert genau OceanWP 4.2.2 und Ocean Extra 2.5.8.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return true;
}

function wpagent_oceanwp_extra_title_text_is_safe( $value, int $maximum ): bool {
	if ( ! is_string( $value ) || '' === $value || '0' === $value || trim( $value ) !== $value || wp_check_invalid_utf8( $value, true ) !== $value || preg_match( '/[\[\]\x00-\x1F\x7F]/u', $value ) ) {
		return false;
	}
	if ( sanitize_text_field( $value ) !== $value ) {
		return false;
	}
	$length = function_exists( 'mb_strlen' ) ? mb_strlen( $value, 'UTF-8' ) : strlen( $value );
	return $length <= $maximum;
}

/**
 * @return array{blog_id:int,post_id:int,post_type:string,version:string,meta:array<string,array{exists:bool,value:string}>}|WP_Error
 */
function wpagent_oceanwp_extra_title_snapshot( WP_Post $post ) {
	$blog_id = (int) get_current_blog_id();
	if ( $blog_id < 1 ) {
		return new WP_Error( 'wpagent_ocean_extra_title_site_changed', __( 'Die Websitezuordnung hat sich während des Ocean-Extra-Titelvorgangs geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$fresh_post = get_post( $post->ID );
	if ( ! $fresh_post instanceof WP_Post || $fresh_post->post_type !== $post->post_type ) {
		return new WP_Error( 'wpagent_ocean_extra_title_post_changed', __( 'Der Beitrag ist nicht mehr im gelesenen Zustand vorhanden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$version = wpagent_oceanwp_extra_version();
	if ( WPAGENT_OCEAN_EXTRA_MIN_VERSION !== $version || WPAGENT_OCEAN_EXTRA_TITLE_THEME_VERSION !== wpagent_oceanwp_version() ) {
		return new WP_Error( 'wpagent_ocean_extra_title_provider_changed', __( 'Die Ocean-Extra-Version hat sich während des Vorgangs geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$meta = array();
	foreach ( wpagent_oceanwp_extra_title_meta_catalog() as $field => $definition ) {
		$key  = $definition['meta'];
		$raw  = get_metadata_raw( 'post', $post->ID, $key, false );
		$rows = null === $raw ? array() : ( is_array( $raw ) ? $raw : array( $raw ) );
		if ( count( $rows ) > 1 ) {
			return new WP_Error( 'wpagent_ocean_extra_title_duplicate_state', __( 'Der Beitrag enthält doppelte Ocean-Extra-Titelmetadaten und kann nicht sicher bearbeitet werden.', 'wp-agent-companion' ), array( 'status' => 502 ) );
		}
		$exists = 1 === count( $rows );
		$value  = $exists ? $rows[0] : '';
		if ( $exists && ! is_string( $value ) ) {
			return new WP_Error( 'wpagent_ocean_extra_title_invalid_state', __( 'Der Beitrag enthält einen ungültigen Ocean-Extra-Titelzustand.', 'wp-agent-companion' ), array( 'status' => 502 ) );
		}
		if ( $exists && ! wpagent_oceanwp_extra_title_raw_value_is_safe( $field, (string) $value ) ) {
			return new WP_Error( 'wpagent_ocean_extra_title_invalid_state', __( 'Der Beitrag enthält einen nicht sicher auswertbaren Ocean-Extra-Titelzustand.', 'wp-agent-companion' ), array( 'status' => 502 ) );
		}
		$meta[ $key ] = array( 'exists' => $exists, 'value' => (string) $value );
	}
	if ( (int) get_current_blog_id() !== $blog_id ) {
		return new WP_Error( 'wpagent_ocean_extra_title_site_changed', __( 'Die Websitezuordnung hat sich während des Ocean-Extra-Titelvorgangs geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return array(
		'blog_id'   => $blog_id,
		'post_id'   => (int) $fresh_post->ID,
		'post_type' => $fresh_post->post_type,
		'version'   => $version,
		'meta'      => $meta,
	);
}

function wpagent_oceanwp_extra_title_raw_value_is_safe( string $field, string $value ): bool {
	if ( '' === $value ) {
		return true;
	}
	$select = wpagent_oceanwp_extra_title_select_catalog();
	if ( isset( $select[ $field ] ) ) {
		if ( 'style' === $field && 'background-image' === $value ) {
			return true;
		}
		return in_array( $value, $select[ $field ]['values'], true );
	}
	$text = wpagent_oceanwp_extra_title_text_catalog();
	return isset( $text[ $field ] ) && wpagent_oceanwp_extra_title_text_is_safe( $value, $text[ $field ]['max'] );
}

/**
 * @param array{blog_id:int,post_id:int,post_type:string,version:string,meta:array<string,array{exists:bool,value:string}>} $snapshot
 * @return array{page_header:string,heading:string,style:string,custom_title:array{mode:string,value?:string},subheading:array{mode:string,value?:string}}
 */
function wpagent_oceanwp_extra_title_normalize( array $snapshot ): array {
	$result = array();
	foreach ( wpagent_oceanwp_extra_title_select_catalog() as $field => $definition ) {
		$entry = $snapshot['meta'][ $definition['meta'] ];
		$value = $entry['exists'] ? $entry['value'] : '';
		if ( '' === $value ) {
			$result[ $field ] = 'inherit';
		} elseif ( 'style' === $field && 'background-image' === $value ) {
			$result[ $field ] = 'background-image';
		} else {
			$result[ $field ] = (string) array_search( $value, $definition['values'], true );
		}
	}
	foreach ( wpagent_oceanwp_extra_title_text_catalog() as $field => $definition ) {
		$entry = $snapshot['meta'][ $definition['meta'] ];
		$result[ $field ] = ( $entry['exists'] && '' !== $entry['value'] )
			? array( 'mode' => 'set', 'value' => $entry['value'] )
			: array( 'mode' => 'inherit' );
	}
	return $result;
}

/** @param array{blog_id:int,post_id:int,post_type:string,version:string,meta:array<string,array{exists:bool,value:string}>} $snapshot */
function wpagent_oceanwp_extra_title_state_hash( array $snapshot ): string {
	$parent = wp_get_theme( get_template() );
	$child  = wp_get_theme( get_stylesheet() );
	$state  = get_template() . '@' . $parent->get( 'Version' ) . '|' . get_stylesheet() . '@' . $child->get( 'Version' );
	$state .= '|ocean-extra@' . $snapshot['version'] . "\0blog\0" . $snapshot['blog_id'] . "\0" . $snapshot['post_id'] . "\0" . $snapshot['post_type'];
	foreach ( $snapshot['meta'] as $key => $entry ) {
		$state .= "\0" . $key . "\0" . ( $entry['exists'] ? '1' : '0' ) . "\0" . $entry['value'];
	}
	return hash( 'sha256', $state );
}

/** @param array<string,mixed> $settings */
function wpagent_oceanwp_extra_title_settings_are_safe( array $settings ): bool {
	foreach ( wpagent_oceanwp_extra_title_text_catalog() as $field => $definition ) {
		if ( ! array_key_exists( $field, $settings ) ) {
			continue;
		}
		$override = $settings[ $field ];
		if ( ! is_array( $override ) || ! isset( $override['mode'] ) || ! is_string( $override['mode'] ) ) {
			return false;
		}
		if ( 'inherit' === $override['mode'] ) {
			if ( array_key_exists( 'value', $override ) ) {
				return false;
			}
			continue;
		}
		if ( 'set' !== $override['mode'] || ! array_key_exists( 'value', $override ) || ! wpagent_oceanwp_extra_title_text_is_safe( $override['value'], $definition['max'] ) ) {
			return false;
		}
	}
	return true;
}

/**
 * @param array{blog_id:int,post_id:int,post_type:string,version:string,meta:array<string,array{exists:bool,value:string}>} $snapshot
 * @param array<string,mixed> $settings
 * @return array{blog_id:int,post_id:int,post_type:string,version:string,meta:array<string,array{exists:bool,value:string}>}
 */
function wpagent_oceanwp_extra_title_expected_snapshot( array $snapshot, array $settings ): array {
	$expected = $snapshot;
	foreach ( wpagent_oceanwp_extra_title_select_catalog() as $field => $definition ) {
		if ( ! array_key_exists( $field, $settings ) ) {
			continue;
		}
		$value = $settings[ $field ];
		$expected['meta'][ $definition['meta'] ] = 'inherit' === $value
			? array( 'exists' => false, 'value' => '' )
			: array( 'exists' => true, 'value' => $definition['values'][ $value ] );
	}
	foreach ( wpagent_oceanwp_extra_title_text_catalog() as $field => $definition ) {
		if ( ! array_key_exists( $field, $settings ) ) {
			continue;
		}
		$override = $settings[ $field ];
		$expected['meta'][ $definition['meta'] ] = 'inherit' === $override['mode']
			? array( 'exists' => false, 'value' => '' )
			: array( 'exists' => true, 'value' => $override['value'] );
	}
	return $expected;
}

/** @return array{page_header:bool,heading:bool,style:string|false,title:string,subheading:string}|WP_Error */
function wpagent_oceanwp_extra_title_runtime_state( int $post_id ) {
	if ( ! wpagent_oceanwp_extra_title_runtime_ready() ) {
		return new WP_Error( 'wpagent_ocean_extra_title_runtime_missing', __( 'Ocean Extra stellt den geprüften Seitentitel-Weg nicht bereit.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$target = static fn( $current_id = null ) => $post_id;
	add_filter( 'ocean_post_id', $target, PHP_INT_MAX );
	try {
		$state = array(
			'page_header' => oceanwp_has_page_header(),
			'heading'     => oceanwp_has_page_header_heading(),
			'style'       => oceanwp_page_header_style(),
			'title'       => apply_filters( 'ocean_title', '__wpagent_ocean_title_probe__' ),
			'subheading'  => apply_filters( 'ocean_post_subheading', '__wpagent_ocean_subheading_probe__' ),
		);
		// OceanWP 4.2.2 deliberately returns false when neither the post nor
		// the Customizer supplies a page-header style. The theme partial treats
		// that as its native inherited state (`if ( $style )`), so reject neither
		// it nor coerce it to a writable style value.
		if ( ! is_bool( $state['page_header'] ) || ! is_bool( $state['heading'] ) || ( ! is_string( $state['style'] ) && false !== $state['style'] ) || ! is_string( $state['title'] ) || ! is_string( $state['subheading'] ) ) {
			throw new UnexpectedValueException( 'Ocean Extra returned an invalid title runtime state.' );
		}
		return $state;
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_ocean_extra_title_runtime_failed', __( 'Ocean Extra konnte die Seitentitel-Laufzeitwirkung nicht auswerten.', 'wp-agent-companion' ), array( 'status' => 502 ) );
	} finally {
		remove_filter( 'ocean_post_id', $target, PHP_INT_MAX );
	}
}

/** @param array<string,mixed> $settings @param array{page_header:bool,heading:bool,style:string|false,title:string,subheading:string} $runtime */
function wpagent_oceanwp_extra_title_runtime_matches( array $settings, array $runtime ): bool {
	if ( isset( $settings['page_header'] ) && 'inherit' !== $settings['page_header'] && $runtime['page_header'] !== ( 'show' === $settings['page_header'] ) ) {
		return false;
	}
	if ( isset( $settings['heading'] ) && 'inherit' !== $settings['heading'] && $runtime['heading'] !== ( 'show' === $settings['heading'] ) ) {
		return false;
	}
	if ( isset( $settings['style'] ) && 'inherit' !== $settings['style'] && $runtime['style'] !== $settings['style'] ) {
		return false;
	}
	foreach ( wpagent_oceanwp_extra_title_text_catalog() as $field => $definition ) {
		if ( isset( $settings[ $field ] ) && 'set' === $settings[ $field ]['mode'] && $runtime[ 'custom_title' === $field ? 'title' : 'subheading' ] !== $settings[ $field ]['value'] ) {
			return false;
		}
	}
	return true;
}

/** @return WP_Error */
function wpagent_oceanwp_extra_title_recovery_required( int $post_id ) {
	$post     = get_post( $post_id );
	$snapshot = $post instanceof WP_Post ? wpagent_oceanwp_extra_title_snapshot( $post ) : null;
	$data     = array( 'status' => 500, 'rollback_verified' => false, 'recovery_required' => true );
	if ( is_array( $snapshot ) ) {
		$data['current_hash']  = wpagent_oceanwp_extra_title_state_hash( $snapshot );
		$data['current_title'] = wpagent_oceanwp_extra_title_normalize( $snapshot );
	}
	return new WP_Error( 'wpagent_ocean_extra_title_recovery_required', __( 'Die Ocean-Extra-Seitentiteländerung wurde nicht vollständig verifiziert. Es wurde kein automatisches Rollback ausgeführt. Wiederhole den Vorgang nicht automatisch und prüfe den aktuellen Zustand manuell.', 'wp-agent-companion' ), $data );
}
