<?php
/** Shared contract for verified per-post GeneratePress layout overrides. */

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

define( 'WPAGENT_GENERATEPRESS_POST_LOCK_PREFIX', 'wp_agent_generatepress_post_lock_' );

/** @return array<string,array{meta:string,values:list<string>,stored:array<string,string>}> */
function wpagent_generatepress_post_field_catalog(): array {
	return array(
		'sidebar_layout' => array(
			'meta'   => '_generate-sidebar-layout-meta',
			'values' => array( 'inherit', 'right-sidebar', 'left-sidebar', 'no-sidebar', 'both-sidebars', 'both-left', 'both-right' ),
			'stored' => array(),
		),
		'footer_widgets' => array(
			'meta'   => '_generate-footer-widget-meta',
			'values' => array( 'inherit', '0', '1', '2', '3', '4', '5' ),
			'stored' => array(),
		),
		'content_container' => array(
			'meta'   => '_generate-full-width-content',
			'values' => array( 'inherit', 'full-width', 'contained' ),
			'stored' => array( 'full-width' => 'true' ),
		),
		'title_visibility' => array(
			'meta'   => '_generate-disable-headline',
			'values' => array( 'inherit', 'hidden' ),
			'stored' => array( 'hidden' => 'true' ),
		),
	);
}

function wpagent_generatepress_post_override_schema( bool $complete = false ): array {
	$properties = array();
	foreach ( wpagent_generatepress_post_field_catalog() as $field => $definition ) {
		$properties[ $field ] = array( 'type' => 'string', 'enum' => $definition['values'] );
	}
	$schema = array(
		'type'                 => 'object',
		'properties'           => $properties,
		'additionalProperties' => false,
	);
	if ( $complete ) {
		$schema['required'] = array_keys( $properties );
	} else {
		$schema['minProperties'] = 1;
		$schema['maxProperties'] = count( $properties );
	}
	return $schema;
}

/** @return WP_Post|WP_Error */
function wpagent_generatepress_post_target( int $post_id ) {
	$post = get_post( $post_id );
	if ( ! $post || wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
		return new WP_Error( 'wpagent_generatepress_post_missing', __( 'Der Beitrag wurde nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}
	$post_type = get_post_type_object( $post->post_type );
	if ( 'attachment' === $post->post_type || ! $post_type || ! $post_type->public ) {
		return new WP_Error( 'wpagent_generatepress_post_unsupported', __( 'GeneratePress unterstützt für diesen Inhaltstyp keine individuellen Layoutvorgaben.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( 'page' === $post->post_type && $post_id === absint( get_option( 'page_for_posts', 0 ) ) ) {
		return new WP_Error( 'wpagent_generatepress_post_blog_page', __( 'GeneratePress verwendet für die Beitragsübersicht das globale Blog-Layout und keine individuellen Seitenvorgaben.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( ! current_user_can( 'edit_post', $post_id ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	return $post;
}

/** @return list<string> */
function wpagent_generatepress_post_stored_values( array $definition ): array {
	$values = array();
	foreach ( $definition['values'] as $value ) {
		if ( 'inherit' !== $value ) {
			$values[] = $definition['stored'][ $value ] ?? $value;
		}
	}
	return array_values( array_unique( $values ) );
}

/**
 * @return array{post_id:int,post_type:string,meta:array<string,array{exists:bool,value:string}>}|WP_Error
 */
function wpagent_generatepress_post_snapshot( WP_Post $post ) {
	$fresh_post = get_post( $post->ID );
	if ( ! $fresh_post instanceof WP_Post || $fresh_post->post_type !== $post->post_type ) {
		return new WP_Error( 'wpagent_generatepress_post_changed', __( 'Der GeneratePress-Beitrag ist nicht mehr im gelesenen Zustand vorhanden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$meta = array();
	foreach ( wpagent_generatepress_post_field_catalog() as $definition ) {
		$key    = $definition['meta'];
		$raw    = get_metadata_raw( 'post', $post->ID, $key, false );
		$rows   = null === $raw ? array() : ( is_array( $raw ) ? $raw : array( $raw ) );
		$values = wpagent_generatepress_post_stored_values( $definition );
		if ( count( $rows ) > 1 ) {
			return new WP_Error( 'wpagent_generatepress_post_duplicate_state', __( 'Der Beitrag enthält doppelte GeneratePress-Einzelmetadaten 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 ) || strlen( $value ) > 64 || ( '' !== $value && ! in_array( $value, $values, true ) ) ) ) {
			return new WP_Error( 'wpagent_generatepress_post_invalid_state', __( 'Der Beitrag enthält einen ungültigen GeneratePress-Einstellungsstand.', 'wp-agent-companion' ), array( 'status' => 502 ) );
		}
		$meta[ $key ] = array( 'exists' => $exists, 'value' => (string) $value );
	}
	return array( 'post_id' => (int) $fresh_post->ID, 'post_type' => $fresh_post->post_type, 'meta' => $meta );
}

/** @return string */
function wpagent_generatepress_post_public_to_stored( string $value, array $definition ): string {
	return $definition['stored'][ $value ] ?? $value;
}

/** @return string|WP_Error */
function wpagent_generatepress_post_stored_to_public( string $value, array $definition ) {
	if ( '' === $value ) {
		return 'inherit';
	}
	foreach ( $definition['values'] as $public ) {
		if ( 'inherit' !== $public && wpagent_generatepress_post_public_to_stored( $public, $definition ) === $value ) {
			return $public;
		}
	}
	return new WP_Error( 'wpagent_generatepress_post_invalid_state', __( 'Der Beitrag enthält einen ungültigen GeneratePress-Einstellungsstand.', 'wp-agent-companion' ), array( 'status' => 502 ) );
}

/**
 * @param array{post_id:int,post_type:string,meta:array<string,array{exists:bool,value:string}>} $snapshot
 * @return array<string,string>|WP_Error
 */
function wpagent_generatepress_post_normalize( array $snapshot ) {
	$overrides = array();
	foreach ( wpagent_generatepress_post_field_catalog() as $field => $definition ) {
		$entry = $snapshot['meta'][ $definition['meta'] ];
		$value = ! $entry['exists'] ? 'inherit' : wpagent_generatepress_post_stored_to_public( $entry['value'], $definition );
		if ( is_wp_error( $value ) ) {
			return $value;
		}
		$overrides[ $field ] = $value;
	}
	return $overrides;
}

/**
 * @param array{post_id:int,post_type:string,meta:array<string,array{exists:bool,value:string}>} $snapshot
 * @param array<string,string> $settings
 * @return array{post_id:int,post_type:string,meta:array<string,array{exists:bool,value:string}>}
 */
function wpagent_generatepress_post_expected_snapshot( array $snapshot, array $settings ): array {
	$expected = $snapshot;
	$catalog  = wpagent_generatepress_post_field_catalog();
	foreach ( $settings as $field => $value ) {
		$definition = $catalog[ $field ];
		$expected['meta'][ $definition['meta'] ] = 'inherit' === $value
			? array( 'exists' => false, 'value' => '' )
			: array( 'exists' => true, 'value' => wpagent_generatepress_post_public_to_stored( $value, $definition ) );
	}
	return $expected;
}

/** @param array{post_id:int,post_type:string,meta:array<string,array{exists:bool,value:string}>} $snapshot */
function wpagent_generatepress_post_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 .= "\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 );
}

/** @return array{sidebar_layout:string,footer_widgets:string,content_container:string,title_visibility:string}|WP_Error */
function wpagent_generatepress_post_runtime_state( int $post_id ) {
	foreach ( array( 'generate_get_layout', 'generate_get_footer_widgets', 'generate_show_title', 'generate_body_classes' ) as $function ) {
		if ( ! function_exists( $function ) ) {
			return new WP_Error( 'wpagent_generatepress_post_runtime_missing', __( 'Die aktive GeneratePress-Version stellt den geprüften Beitragslayout-Weg nicht bereit.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
	}
	$target = get_post( $post_id );
	if ( ! $target instanceof WP_Post ) {
		return new WP_Error( 'wpagent_generatepress_post_changed', __( 'Der GeneratePress-Beitrag ist nicht mehr im gelesenen Zustand vorhanden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	global $post, $wp_query;
	$previous_post  = $post;
	$previous_query = $wp_query;
	try {
		$post                        = $target;
		$wp_query                    = new WP_Query();
		$wp_query->is_singular       = true;
		$wp_query->is_page           = 'page' === $target->post_type;
		$wp_query->is_single         = 'page' !== $target->post_type;
		$wp_query->queried_object    = $target;
		$wp_query->queried_object_id = $target->ID;
		$classes = generate_body_classes( array() );
		return array(
			'sidebar_layout'   => (string) generate_get_layout(),
			'footer_widgets'   => (string) generate_get_footer_widgets(),
			'content_container' => in_array( 'full-width-content', $classes, true )
				? 'full-width'
				: ( in_array( 'contained-content', $classes, true ) ? 'contained' : 'inherit' ),
			'title_visibility' => generate_show_title() ? 'inherit' : 'hidden',
		);
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_generatepress_post_runtime_failed', __( 'GeneratePress konnte das individuelle Beitragslayout nicht auswerten.', 'wp-agent-companion' ), array( 'status' => 502 ) );
	} finally {
		$post     = $previous_post;
		$wp_query = $previous_query;
	}
}

/** @return string|WP_Error */
function wpagent_generatepress_post_acquire_lock( int $post_id ) {
	$option = WPAGENT_GENERATEPRESS_POST_LOCK_PREFIX . $post_id;
	$token  = wpagent_companion_acquire_option_lock( $option, WPAGENT_GENERATEPRESS_LOCK_TTL );
	if ( is_string( $token ) ) {
		return $token;
	}
	return new WP_Error( 'wpagent_generatepress_post_locked', __( 'Das GeneratePress-Beitragslayout wird gerade durch einen anderen Vorgang geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

function wpagent_generatepress_post_release_lock( int $post_id, string $token ): void {
	wpagent_companion_release_option_lock( WPAGENT_GENERATEPRESS_POST_LOCK_PREFIX . $post_id, $token );
}

/** @param array{post_id:int,post_type:string,meta:array<string,array{exists:bool,value:string}>} $snapshot @return true|WP_Error */
function wpagent_generatepress_post_rollback( array $snapshot, string $expected_hash ) {
	foreach ( $snapshot['meta'] as $key => $entry ) {
		try {
			if ( $entry['exists'] ) {
				update_post_meta( $snapshot['post_id'], $key, $entry['value'] );
			} else {
				delete_post_meta( $snapshot['post_id'], $key );
			}
		} catch ( Throwable $error ) {
			// Verify the complete persisted state below instead of trusting hooks.
		}
	}
	$post  = get_post( $snapshot['post_id'] );
	$fresh = $post ? wpagent_generatepress_post_snapshot( $post ) : null;
	if ( ! is_array( $fresh ) || ! hash_equals( $expected_hash, wpagent_generatepress_post_state_hash( $fresh ) ) ) {
		return new WP_Error( 'wpagent_generatepress_post_rollback_unverified', __( 'Das GeneratePress-Beitrags-Rollback konnte nicht verifiziert werden. Prüfe den Beitrag sofort manuell.', 'wp-agent-companion' ), array( 'status' => 500, 'rollback_verified' => false ) );
	}
	return true;
}

/** @return WP_Error */
function wpagent_generatepress_post_fail_after_rollback( array $snapshot, string $previous_hash ) {
	$rollback = wpagent_generatepress_post_rollback( $snapshot, $previous_hash );
	if ( is_wp_error( $rollback ) ) {
		return $rollback;
	}
	return new WP_Error( 'wpagent_generatepress_post_write_unverified', __( 'GeneratePress hat die Beitragsänderung nicht exakt bestätigt. Der vorherige Zustand wurde verifiziert wiederhergestellt.', 'wp-agent-companion' ), array( 'status' => 502, 'rollback_verified' => true ) );
}
