<?php
/** Ability: wp-agent/set-post-schema */

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

function wpagent_ability_set_post_schema(): array {
	return array(
		'label'               => __( 'Post-Schema setzen und verifizieren', 'wp-agent-companion' ),
		'description'         => __( 'Erstellt oder aktualisiert ein Rank-Math-Schema oder setzt Yoasts Seiten- und Artikeltyp mit Read-back. Unklare Teilfehler verlangen eine manuelle Wiederherstellung.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type' => 'object',
			'properties' => array(
				'post_id' => array( 'type' => 'integer' ),
				'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'idempotency_key' => array( 'type' => 'string', 'minLength' => 8, 'maxLength' => 64, 'pattern' => '^[A-Za-z0-9._:-]+$' ),
				'schema_id' => array( 'type' => 'string' ),
				'schema' => array( 'type' => 'object' ),
				'page_type' => array( 'type' => 'string' ),
				'article_type' => array( 'type' => 'string' ),
			),
			'required' => array( 'post_id', 'expected_hash' ), 'additionalProperties' => false,
		),
		'output_schema'       => array(
			'type' => 'object',
			'properties' => array(
				'post_id' => array( 'type' => 'integer' ), 'plugin' => array( 'type' => 'string' ),
				'schema_id' => array( 'type' => array( 'string', 'null' ) ),
				'schema' => array( 'type' => array( 'object', 'null' ) ),
				'created' => array( 'type' => array( 'boolean', 'null' ) ),
				'previous_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'state_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				// The provider branch determines which fields are present, but every
				// returned verification key is known and boolean.
				'written' => array(
					'type' => 'object',
					'properties' => array(
						'schema' => array( 'type' => 'boolean' ),
						'page_type' => array( 'type' => 'boolean' ),
						'article_type' => array( 'type' => 'boolean' ),
					),
					'additionalProperties' => false,
				),
				'verified' => array( 'type' => 'boolean' ),
				'indexable_verified' => array( 'type' => array( 'boolean', 'null' ) ),
				'rolled_back' => array( 'type' => 'boolean' ),
				'rollback_verified' => array( 'type' => array( 'boolean', 'null' ) ),
			),
		),
		'execute_callback'    => 'wpagent_exec_set_post_schema',
		'permission_callback' => 'wpagent_perm_edit_posts',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false ) ),
	);
}

/** @param mixed $input @return array<string,mixed>|WP_Error */
function wpagent_exec_set_post_schema( $input ) {
	$input   = is_array( $input ) ? $input : array();
	$post_id = absint( $input['post_id'] ?? 0 );
	if ( ! current_user_can( 'edit_post', $post_id ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	if ( ! get_post( $post_id ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	$expected_hash = strtolower( (string) ( $input['expected_hash'] ?? '' ) );
	if ( ! preg_match( '/^[a-f0-9]{64}$/', $expected_hash ) ) {
		return new WP_Error( 'wpagent_schema_hash', __( 'expected_hash muss ein vollständiger SHA-256-Hash aus get-post-schema sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$plugin = wpagent_detect_seo_plugin();
	if ( ! in_array( $plugin, array( 'rankmath', 'yoast' ), true ) ) {
		return new WP_Error( 'wpagent_schema_provider_unsupported', __( 'Das aktive SEO-Plugin stellt keinen bestätigten Post-Schema-Schreibpfad bereit.', 'wp-agent-companion' ), array( 'status' => 409, 'plugin' => $plugin ) );
	}
	$lock = wpagent_seo_acquire_lock( $post_id );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		if ( ! wpagent_seo_lock_checkpoint( $post_id, $lock ) ) {
			return wpagent_seo_recovery_required( $post_id, $expected_hash, 'lock_lost_before_schema_write' );
		}
		$result = 'rankmath' === $plugin
			? wpagent_set_rankmath_post_schema( $post_id, $input, $lock )
			: wpagent_set_yoast_post_schema( $post_id, $input, $lock );
		if ( is_wp_error( $result ) ) {
			return $result;
		}
		if ( ! wpagent_seo_lock_checkpoint( $post_id, $lock ) ) {
			return wpagent_seo_recovery_required( $post_id, $expected_hash, 'lock_lost_after_schema_write' );
		}
		return $result;
	} finally {
		wpagent_seo_release_lock( $post_id, $lock );
	}
}

/** @param array<string,mixed> $input @return array<string,mixed>|WP_Error */
function wpagent_set_rankmath_post_schema( int $post_id, array $input, string $lock ) {
	if ( ! array_key_exists( 'schema', $input ) || array_key_exists( 'page_type', $input ) || array_key_exists( 'article_type', $input ) ) {
		return new WP_Error( 'wpagent_invalid_schema_input', __( 'Rank Math benötigt genau ein Schema-Objekt. Yoast-Typfelder sind hier nicht zulässig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( ! class_exists( 'RankMath\\Rest\\Shared' ) || ! class_exists( 'RankMath\\Rest\\Sanitize' ) || ! class_exists( 'RankMath\\Schema\\DB' ) ) {
		return new WP_Error( 'wpagent_schema_api_missing', __( 'Rank Math stellt seinen nativen Schema-Schreibpfad nicht bereit.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( ! class_exists( 'RankMath\\Helper' ) || ! \RankMath\Helper::has_cap( 'onpage_snippet' ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Rank Math erlaubt diesem Benutzer keine Schemaänderungen.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	$schema_id = isset( $input['schema_id'] ) ? sanitize_key( (string) $input['schema_id'] ) : '';
	if ( '' !== $schema_id && ! preg_match( '/^schema-[1-9][0-9]*$/', $schema_id ) ) {
		return new WP_Error( 'wpagent_invalid_schema_id', __( 'Die Rank-Math-Schema-ID ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$existing = \RankMath\Schema\DB::get_schemas( $post_id, 'postmeta', true );
	if ( '' !== $schema_id && ! isset( $existing[ $schema_id ] ) ) {
		return new WP_Error( 'wpagent_schema_not_found', __( 'Das Rank-Math-Schema wurde nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}
	$schema = wpagent_validate_rankmath_schema( $input['schema'], '' === $schema_id );
	if ( is_wp_error( $schema ) ) {
		return $schema;
	}
	$expected = \RankMath\Rest\Sanitize::get()->sanitize( 'rank_math_schema', $schema );
	$expected = wp_kses_post_deep( $expected );
	$current_state = wpagent_read_post_schema( $post_id, 'rankmath' );
	if ( is_wp_error( $current_state ) ) {
		return $current_state;
	}
	$previous_hash = $current_state['state_hash'];
	$idempotency_key = isset( $input['idempotency_key'] ) ? (string) $input['idempotency_key'] : '';
	if ( '' === $schema_id ) {
		if ( ! preg_match( '/^[A-Za-z0-9._:-]{8,64}$/', $idempotency_key ) ) {
			return new WP_Error( 'wpagent_schema_idempotency', __( 'Neue Rank-Math-Schemas benötigen einen idempotency_key mit 8 bis 64 sicheren Zeichen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$mapping_key = wpagent_rankmath_schema_idempotency_meta_key( $idempotency_key );
		$mappings = get_post_meta( $post_id, $mapping_key, false );
		if ( count( $mappings ) > 1 ) {
			return new WP_Error( 'wpagent_schema_idempotency_duplicate', __( 'Der Idempotenzschlüssel ist mehrfach gespeichert und muss manuell bereinigt werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
		}
		if ( 1 === count( $mappings ) ) {
			$mapping = $mappings[0];
			$mapped_id = is_array( $mapping ) ? (string) ( $mapping['schema_id'] ?? '' ) : '';
			$payload_hash = wpagent_rankmath_schema_payload_hash( $expected );
			if ( ! is_array( $mapping ) || ! isset( $existing[ $mapped_id ] ) || ! hash_equals( (string) ( $mapping['payload_hash'] ?? '' ), $payload_hash ) || $existing[ $mapped_id ] !== $expected ) {
				return new WP_Error( 'wpagent_schema_idempotency_conflict', __( 'Der Idempotenzschlüssel gehört zu einem anderen oder nachträglich veränderten Schema.', 'wp-agent-companion' ), array( 'status' => 409 ) );
			}
			return array(
				'post_id' => $post_id, 'plugin' => 'rankmath', 'schema_id' => $mapped_id, 'schema' => $existing[ $mapped_id ],
				'created' => false, 'previous_hash' => $previous_hash, 'state_hash' => $previous_hash,
				'written' => array( 'schema' => true ), 'verified' => true, 'indexable_verified' => null,
				'rolled_back' => false, 'rollback_verified' => null,
			);
		}
	} elseif ( '' !== $idempotency_key ) {
		return new WP_Error( 'wpagent_schema_idempotency_update', __( 'idempotency_key ist nur beim Anlegen eines Rank-Math-Schemas zulässig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( ! hash_equals( $previous_hash, strtolower( (string) $input['expected_hash'] ) ) ) {
		return new WP_Error( 'wpagent_schema_conflict', __( 'Die Post-Schemadaten wurden seit dem Lesen verändert. Bitte erneut lesen und den neuen state_hash verwenden.', 'wp-agent-companion' ), array( 'status' => 409, 'state_hash' => $previous_hash ) );
	}
	$shortcode = isset( $schema['metadata']['shortcode'] ) ? (string) $schema['metadata']['shortcode'] : '';
	if ( '' !== $schema_id ) {
		$before_shortcode = isset( $existing[ $schema_id ]['metadata']['shortcode'] ) ? (string) $existing[ $schema_id ]['metadata']['shortcode'] : '';
		if ( $shortcode !== $before_shortcode ) {
			return new WP_Error( 'wpagent_schema_shortcode_immutable', __( 'Die interne Rank-Math-Shortcode-ID darf bei einer Aktualisierung nicht verändert werden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
	} elseif ( '' !== $shortcode && metadata_exists( 'post', $post_id, 'rank_math_shortcode_schema_' . $shortcode ) ) {
		return new WP_Error( 'wpagent_schema_shortcode_exists', __( 'Diese interne Rank-Math-Shortcode-ID wird bereits verwendet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( ! empty( $schema['metadata']['isPrimary'] ) ) {
		foreach ( $existing as $other_id => $other ) {
			if ( $other_id !== $schema_id && ! empty( $other['metadata']['isPrimary'] ) ) {
				return new WP_Error( 'wpagent_primary_schema_exists', __( 'Der Beitrag besitzt bereits ein anderes primäres Rank-Math-Schema.', 'wp-agent-companion' ), array( 'status' => 409, 'schema_id' => $other_id ) );
			}
		}
	}

	$before     = '' !== $schema_id ? $existing[ $schema_id ] : null;
	$request_id = '' !== $schema_id ? $schema_id : 'new-wpagently-' . wp_generate_uuid4();
	$request    = new WP_REST_Request( 'POST', '/rankmath/v1/updateSchemas' );
	$request->set_param( 'objectID', $post_id );
	$request->set_param( 'objectType', 'post' );
	$request->set_param( 'schemas', array( $request_id => $schema ) );
	if ( ! wpagent_seo_lock_checkpoint( $post_id, $lock ) ) {
		return wpagent_seo_recovery_required( $post_id, $previous_hash, 'lock_lost_before_rankmath_write' );
	}
	try {
		$result = ( new \RankMath\Rest\Shared() )->update_schemas( $request );
	} catch ( Throwable ) {
		return wpagent_seo_recovery_required( $post_id, $previous_hash, 'rankmath_provider_exception' );
	}
	if ( '' === $schema_id ) {
		$new_mid   = absint( is_array( $result ) ? ( $result[ $request_id ] ?? 0 ) : 0 );
		$schema_id = $new_mid ? 'schema-' . $new_mid : '';
		if ( ! $new_mid ) {
			return wpagent_seo_recovery_required( $post_id, $previous_hash, 'rankmath_create_id_unavailable' );
		}
	}
	$fresh     = \RankMath\Schema\DB::get_schemas( $post_id, 'postmeta', true );
	$persisted = '' !== $schema_id ? ( $fresh[ $schema_id ] ?? null ) : null;
	$verified  = is_array( $persisted ) && $persisted === $expected;
	if ( ! $verified ) {
		return wpagent_seo_recovery_required( $post_id, $previous_hash, 'rankmath_readback_unverified' );
	}
	if ( '' !== $idempotency_key ) {
		$mapping_key = wpagent_rankmath_schema_idempotency_meta_key( $idempotency_key );
		$mapping = array( 'schema_id' => $schema_id, 'payload_hash' => wpagent_rankmath_schema_payload_hash( $persisted ) );
		if ( false === add_post_meta( $post_id, $mapping_key, $mapping, true ) ) {
			return wpagent_seo_recovery_required( $post_id, $previous_hash, 'rankmath_idempotency_record_unavailable' );
		}
	}
	$fresh_state = wpagent_rankmath_schema_state_from_raw( $post_id, $fresh );
	return array(
		'post_id' => $post_id, 'plugin' => 'rankmath', 'schema_id' => $schema_id, 'schema' => $persisted,
		'created' => '' !== $idempotency_key, 'previous_hash' => $previous_hash, 'state_hash' => $fresh_state['state_hash'],
		'written' => array( 'schema' => true ), 'verified' => true, 'indexable_verified' => null,
		'rolled_back' => false, 'rollback_verified' => null,
	);
}

/** @param array<string,mixed> $input @return array<string,mixed>|WP_Error */
function wpagent_set_yoast_post_schema( int $post_id, array $input, string $lock ) {
	if ( array_key_exists( 'schema', $input ) || array_key_exists( 'schema_id', $input ) || array_key_exists( 'idempotency_key', $input ) ) {
		return new WP_Error( 'wpagent_custom_schema_unsupported', __( 'Yoast unterstützt über diesen bestätigten Pfad nur Seiten- und Artikeltypen, keine Rank-Math-Schemaobjekte.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$page    = array_key_exists( 'page_type', $input ) ? sanitize_text_field( (string) $input['page_type'] ) : null;
	$article = array_key_exists( 'article_type', $input ) ? sanitize_text_field( (string) $input['article_type'] ) : null;
	if ( null === $page && null === $article ) {
		return new WP_Error( 'wpagent_no_schema_fields', __( 'Kein Yoast-Schemafeld übergeben.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( null !== $page && '' !== $page && ! in_array( $page, wpagent_yoast_page_schema_types(), true ) ) {
		return new WP_Error( 'wpagent_invalid_page_schema_type', __( 'Der Yoast-Seitentyp ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( null !== $article && '' !== $article && ! in_array( $article, wpagent_yoast_article_schema_types(), true ) ) {
		return new WP_Error( 'wpagent_invalid_article_schema_type', __( 'Der Yoast-Artikeltyp ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( ( null !== $page && '' === $page ) || ( null !== $article && '' === $article ) ) {
		return new WP_Error(
			'wpagent_yoast_schema_delete_manual_only',
			__( 'Vorhandene Yoast-Schemawerte werden nicht remote entfernt, weil Yoast keine atomare Compare-and-Delete-Prüfung gegen parallele Post-Meta-Änderungen anbietet. Entferne den Schemawert manuell in der WordPress-Verwaltung.', 'wp-agent-companion' ),
			array( 'status' => 409, 'post_id' => $post_id )
		);
	}
	$keys   = array();
	if ( null !== $page ) {
		$keys[] = '_yoast_wpseo_schema_page_type';
	}
	if ( null !== $article ) {
		$keys[] = '_yoast_wpseo_schema_article_type';
	}
	$current_state = wpagent_read_post_schema( $post_id, 'yoast' );
	if ( is_wp_error( $current_state ) ) return $current_state;
	$previous_hash = $current_state['state_hash'];
	if ( ! hash_equals( $previous_hash, strtolower( (string) $input['expected_hash'] ) ) ) {
		return new WP_Error( 'wpagent_schema_conflict', __( 'Die Post-Schemadaten wurden seit dem Lesen verändert. Bitte erneut lesen und den neuen state_hash verwenden.', 'wp-agent-companion' ), array( 'status' => 409, 'state_hash' => $previous_hash ) );
	}
	$before = wpagent_capture_seo_postmeta( $post_id, $keys );
	if ( ! wpagent_seo_lock_checkpoint( $post_id, $lock ) ) {
		return wpagent_seo_recovery_required( $post_id, $previous_hash, 'lock_lost_before_yoast_write' );
	}
	if ( null !== $page ) {
		WPSEO_Meta::set_value( 'schema_page_type', $page, $post_id );
	}
	if ( null !== $article ) {
		WPSEO_Meta::set_value( 'schema_article_type', $article, $post_id );
	}
	$refresh = wp_update_post( array( 'ID' => $post_id ), true );
	$indexable_verified = ! is_wp_error( $refresh ) && wpagent_refresh_yoast_schema_indexable( $post_id, $page, $article );
	$fresh = wpagent_read_post_schema( $post_id, 'yoast' );
	if ( ! wpagent_seo_lock_checkpoint( $post_id, $lock ) ) {
		return wpagent_seo_recovery_required( $post_id, $previous_hash, 'lock_lost_after_yoast_write' );
	}
	$written = array();
	if ( null !== $page ) {
		$written['page_type'] = ! is_wp_error( $fresh ) && $fresh['page_type'] === $page;
	}
	if ( null !== $article ) {
		$written['article_type'] = ! is_wp_error( $fresh ) && $fresh['article_type'] === $article;
	}
	$verified = $indexable_verified && ! in_array( false, $written, true );
	$rollback_verified = null;
	if ( ! $verified ) {
		return wpagent_seo_recovery_required( $post_id, $previous_hash, 'yoast_schema_readback_unverified' );
	}
	$final_state = wpagent_read_post_schema( $post_id, 'yoast' );
	$state_hash = is_wp_error( $final_state ) ? $previous_hash : $final_state['state_hash'];
	return array(
		'post_id' => $post_id, 'plugin' => 'yoast', 'schema_id' => null, 'schema' => null,
		'created' => null, 'previous_hash' => $previous_hash, 'state_hash' => $state_hash,
		'written' => $written, 'verified' => $verified, 'indexable_verified' => $indexable_verified,
		'rolled_back' => ! $verified, 'rollback_verified' => $rollback_verified,
	);
}
