<?php
/** Ability: wp-agent/set-term-seo-meta. */

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

function wpagent_ability_set_term_seo_meta(): array {
	$properties = array(
		'term_id' => array( 'type' => 'integer', 'minimum' => 1 ),
		'taxonomy' => array( 'type' => 'string', 'pattern' => '^[a-z0-9_-]+$' ),
		'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
		'seo_title' => array( 'type' => 'string' ),
		'seo_description' => array( 'type' => 'string' ),
		'focus_keyword' => array( 'type' => 'string' ),
		'canonical_url' => array( 'type' => 'string' ),
		'robots_noindex' => array( 'type' => 'boolean' ),
		'robots_nofollow' => array( 'type' => 'boolean' ),
		'open_graph_title' => array( 'type' => 'string' ),
		'open_graph_description' => array( 'type' => 'string' ),
		'twitter_title' => array( 'type' => 'string' ),
		'twitter_description' => array( 'type' => 'string' ),
	);
	return array(
		'label' => __( 'Term-SEO setzen und verifizieren', 'wp-agent-companion' ),
		'description' => __( 'Aktualisiert Term-SEO über Rank Math, Yoast oder SEOPress mit Konflikthash, Kurzzeitsperre, vollständigem Read-back und verifiziertem Rollback.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array(
			'type' => 'object', 'properties' => $properties,
			'required' => array( 'term_id', 'taxonomy', 'expected_hash' ),
			'additionalProperties' => false,
		),
		'output_schema' => array(
			'type' => 'object',
			'properties' => array(
				'term_id' => array( 'type' => 'integer' ),
				'taxonomy' => array( 'type' => 'string' ),
				'plugin' => array( 'type' => 'string' ),
				'written' => array( 'type' => 'object' ),
				'verified' => array( 'type' => 'boolean' ),
				'indexable_verified' => array( 'type' => array( 'boolean', 'null' ) ),
				'rolled_back' => array( 'type' => 'boolean' ),
				'rollback_verified' => array( 'type' => array( 'boolean', 'null' ) ),
				'state_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
			),
			'required' => array( 'term_id', 'taxonomy', 'plugin', 'written', 'verified', 'indexable_verified', 'rolled_back', 'rollback_verified', 'state_hash' ),
			'additionalProperties' => false,
		),
		'execute_callback' => 'wpagent_exec_set_term_seo_meta',
		'permission_callback' => 'wpagent_perm_edit_posts',
		'meta' => array(
			'show_in_rest' => true,
			'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => true ),
		),
	);
}

/**
 * @param array<string,string|bool|null> $fields
 * @param array<string,mixed>            $before
 */
function wpagent_write_term_seo_provider( int $term_id, string $taxonomy, string $plugin, array $fields, array $before ) {
	if ( 'yoast' === $plugin ) {
		if ( null !== $fields['robots_nofollow'] ) {
			return new WP_Error( 'wpagent_yoast_term_nofollow_unsupported', __( 'Yoast unterstützt keinen Nofollow-Override pro Term. Das Feld wurde nicht geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		if ( ! class_exists( 'WPSEO_Taxonomy_Meta' ) ) {
			return new WP_Error( 'wpagent_yoast_term_api_missing', __( 'Yoast stellt seine native Taxonomie-Meta-Schnittstelle nicht bereit.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		// Yoasts set_values() validiert einen vollständigen Termzustand. Bei einer
		// partiellen Map würden nicht erneut übergebene Textfelder auf ihre Defaults
		// zurückfallen. Deshalb den nativen, registrierten Gesamtzustand laden und
		// ausschließlich die angeforderten Felder darin überschreiben.
		$values = WPSEO_Taxonomy_Meta::get_term_meta( $term_id, $taxonomy );
		$values = is_array( $values ) ? $values : array();
		foreach ( wpagent_term_seo_yoast_map() as $field => $key ) {
			if ( null !== $fields[ $field ] ) {
				$values[ $key ] = $fields[ $field ];
			}
		}
		if ( null !== $fields['robots_noindex'] ) {
			$values['wpseo_noindex'] = $fields['robots_noindex'] ? 'noindex' : 'index';
		}
		WPSEO_Taxonomy_Meta::set_values( $term_id, $taxonomy, $values );
		return true;
	}

	$map = wpagent_term_seo_meta_map( $plugin );
	foreach ( $map as $field => $key ) {
		if ( null === $fields[ $field ] ) {
			continue;
		}
		if ( '' === $fields[ $field ] ) {
			delete_term_meta( $term_id, $key );
		} else {
			update_term_meta( $term_id, $key, $fields[ $field ] );
		}
	}
	if ( 'rankmath' === $plugin && ( null !== $fields['robots_noindex'] || null !== $fields['robots_nofollow'] ) ) {
		$current = (array) ( $before['values']['rank_math_robots'][0] ?? array() );
		$current = array_values( array_unique( $current ) );
		$index   = null;
		$follow  = null;
		foreach ( $current as $directive ) {
			if ( in_array( $directive, array( 'index', 'noindex' ), true ) ) {
				$index = $directive;
			} elseif ( in_array( $directive, array( 'follow', 'nofollow' ), true ) ) {
				$follow = $directive;
			}
		}
		if ( null !== $fields['robots_noindex'] ) {
			$index = $fields['robots_noindex'] ? 'noindex' : 'index';
		}
		if ( null !== $fields['robots_nofollow'] ) {
			$follow = $fields['robots_nofollow'] ? 'nofollow' : 'follow';
		}
		$other  = array_values( array_diff( $current, array( 'index', 'noindex', 'follow', 'nofollow' ) ) );
		$robots = array_values( array_filter( array_merge( array( $index, $follow ), $other ) ) );
		update_term_meta( $term_id, 'rank_math_robots', $robots );
	} elseif ( 'seopress' === $plugin ) {
		if ( null !== $fields['robots_noindex'] ) {
			if ( $fields['robots_noindex'] ) {
				update_term_meta( $term_id, '_seopress_robots_index', 'yes' );
			} else {
				delete_term_meta( $term_id, '_seopress_robots_index' );
			}
		}
		if ( null !== $fields['robots_nofollow'] ) {
			if ( $fields['robots_nofollow'] ) {
				update_term_meta( $term_id, '_seopress_robots_follow', 'yes' );
			} else {
				delete_term_meta( $term_id, '_seopress_robots_follow' );
			}
		}
	}
	return true;
}

/** @param mixed $input */
function wpagent_exec_set_term_seo_meta( $input ) {
	$input         = is_array( $input ) ? $input : array();
	$term_id       = (int) ( $input['term_id'] ?? 0 );
	$taxonomy      = sanitize_key( (string) ( $input['taxonomy'] ?? '' ) );
	$expected_hash = strtolower( (string) ( $input['expected_hash'] ?? '' ) );
	if ( ! preg_match( '/^[a-f0-9]{64}$/D', $expected_hash ) ) {
		return new WP_Error( 'wpagent_invalid_expected_hash', __( 'expected_hash muss ein SHA-256-Hash mit 64 Kleinbuchstaben und Ziffern sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$target = wpagent_term_seo_target( $term_id, $taxonomy );
	if ( is_wp_error( $target ) ) {
		return $target;
	}
	$plugin    = wpagent_detect_seo_plugin();
	$supported = wpagent_term_seo_provider_supported( $plugin );
	if ( is_wp_error( $supported ) ) {
		return $supported;
	}
	$fields = wpagent_seo_fields_from_input( $input );
	if ( is_wp_error( $fields ) ) {
		return $fields;
	}
	if ( ! wpagent_seo_fields_have_value( $fields ) ) {
		return new WP_Error( 'wpagent_no_fields', __( 'Kein SEO-Feld übergeben.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}

	$lock = wpagent_term_seo_acquire_lock( $term_id, $taxonomy, $plugin );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$before = wpagent_capture_term_seo_state( $term_id, $taxonomy, $plugin );
		if ( is_wp_error( $before ) ) {
			return $before;
		}
		$before_hash = wpagent_term_seo_state_hash( $term_id, $taxonomy, $plugin, $before );
		if ( ! hash_equals( $before_hash, $expected_hash ) ) {
			return new WP_Error( 'wpagent_term_seo_conflict', __( 'Die SEO-Daten des Terms wurden seit dem Lesen verändert. Bitte erneut lesen.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => $before_hash ) );
		}

		$write = wpagent_write_term_seo_provider( $term_id, $taxonomy, $plugin, $fields, $before );
		if ( is_wp_error( $write ) ) {
			return $write;
		}
		$indexable_verified = null;
		if ( 'yoast' === $plugin ) {
			$indexable_verified = wpagent_refresh_yoast_term_indexable( $term_id );
		}
		$fresh = wpagent_read_term_seo_meta( $term_id, $taxonomy, $plugin );
		if ( is_wp_error( $fresh ) ) {
			$rollback = wpagent_rollback_term_seo_state( $term_id, $taxonomy, $plugin, $before );
			$fresh->add_data( array( 'status' => 500, 'rolled_back' => true, 'rollback_verified' => $rollback ) );
			return $fresh;
		}
		$verification = wpagent_verify_seo_snapshot( $fields, $fresh );
		$verified     = $verification['verified'] && false !== $indexable_verified;
		$rolled_back  = false;
		$rollback_verified = null;
		$state_hash   = $fresh['state_hash'];
		if ( ! $verified ) {
			$rolled_back       = true;
			$rollback_verified = wpagent_rollback_term_seo_state( $term_id, $taxonomy, $plugin, $before );
			if ( ! $rollback_verified ) {
				return new WP_Error(
					'wpagent_term_seo_rollback_failed',
					__( 'Die Term-SEO-Daten konnten nach einem Verifikationsfehler nicht vollständig wiederhergestellt werden. Bitte den Term manuell prüfen.', 'wp-agent-companion' ),
					array( 'status' => 500, 'rolled_back' => true, 'rollback_verified' => false, 'written' => $verification['written'] )
				);
			}
			$state_hash = $before_hash;
		}
		return array(
			'term_id' => $term_id,
			'taxonomy' => $taxonomy,
			'plugin' => $plugin,
			'written' => $verification['written'],
			'verified' => $verified,
			'indexable_verified' => $indexable_verified,
			'rolled_back' => $rolled_back,
			'rollback_verified' => $rollback_verified,
			'state_hash' => $state_hash,
		);
	} catch ( Throwable ) {
		$rollback = isset( $before ) && is_array( $before ) ? wpagent_rollback_term_seo_state( $term_id, $taxonomy, $plugin, $before ) : false;
		return new WP_Error( 'wpagent_term_seo_write_failed', __( 'Das SEO-Plugin konnte die Term-Daten nicht sicher aktualisieren.', 'wp-agent-companion' ), array( 'status' => 500, 'rolled_back' => isset( $before ), 'rollback_verified' => $rollback ) );
	} finally {
		wpagent_term_seo_release_lock( $term_id, $taxonomy, $lock, $plugin );
	}
}
