<?php
/** Update one verified Ocean Extra module switch. */

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

/** @return array{raw:string,autoload:string}|null|WP_Error */
function wpagent_ocean_extra_modules_raw_option_row( string $option ): array|WP_Error|null {
	global $wpdb;
	$row = wpagent_companion_read_option_row_direct( $option );
	if ( '' !== $wpdb->last_error ) {
		return new WP_Error( 'wpagent_ocean_extra_modules_storage_failed', __( 'Die Ocean-Extra-Moduloption konnte nicht direkt gelesen werden.', 'wp-agent-companion' ), array( 'status' => 502 ) );
	}
	return $row;
}

/** @param array{raw:string,autoload:string}|null $left @param array{raw:string,autoload:string}|null $right */
function wpagent_ocean_extra_modules_option_rows_equal( ?array $left, ?array $right ): bool {
	return null === $left || null === $right
		? null === $left && null === $right
		: $left['raw'] === $right['raw'] && $left['autoload'] === $right['autoload'];
}

/** @return array{raw:string,autoload:string}|WP_Error */
function wpagent_ocean_extra_modules_prepared_option_row( string $option, string $value, ?array $before ) {
	$old = is_array( $before ) ? maybe_unserialize( $before['raw'] ) : false;
	try {
		$prepared = sanitize_option( $option, $value );
		$prepared = apply_filters( "pre_update_option_{$option}", $prepared, $old, $option );
		$prepared = apply_filters( 'pre_update_option', $prepared, $option, $old );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_ocean_extra_modules_write_failed', __( 'Die Ocean-Extra-Moduländerung konnte nicht sicher vorbereitet werden.', 'wp-agent-companion' ), array( 'status' => 502, 'write_started' => false ) );
	}
	if ( ! is_string( $prepared ) || $prepared !== $value ) {
		return new WP_Error( 'wpagent_ocean_extra_modules_conflict', __( 'Ein Filter hat die geprüfte Ocean-Extra-Moduländerung verändert.', 'wp-agent-companion' ), array( 'status' => 409, 'write_started' => false ) );
	}
	$raw = maybe_serialize( $prepared );
	return array( 'raw' => $raw, 'autoload' => is_array( $before ) ? $before['autoload'] : wp_determine_option_autoload_value( $option, $prepared, $raw, null ) );
}

/** @return array{raw:string,autoload:string}|WP_Error */
function wpagent_ocean_extra_modules_cas_option( string $option, string $value, ?array $before ) {
	global $wpdb;
	$next = wpagent_ocean_extra_modules_prepared_option_row( $option, $value, $before );
	if ( is_wp_error( $next ) ) return $next;
	if ( wpagent_ocean_extra_modules_option_rows_equal( $before, $next ) ) return $next;
	if ( null === $before ) {
		try {
			$created = wpagent_companion_add_option_create_only( $option, $value, '', $next['autoload'] );
		} catch ( Throwable $error ) {
			return new WP_Error( 'wpagent_ocean_extra_modules_write_failed', __( 'Die Ocean-Extra-Moduloption konnte nicht atomar angelegt werden.', 'wp-agent-companion' ), array( 'status' => 502, 'write_started' => false ) );
		}
		if ( ! $created ) return new WP_Error( 'wpagent_ocean_extra_modules_conflict', __( 'Die Ocean-Extra-Moduloption wurde gleichzeitig angelegt.', 'wp-agent-companion' ), array( 'status' => 409, 'write_started' => false ) );
		$current = wpagent_ocean_extra_modules_raw_option_row( $option );
		return is_array( $current ) && wpagent_ocean_extra_modules_option_rows_equal( $current, $next )
			? $next
			: new WP_Error( 'wpagent_ocean_extra_modules_write_failed', __( 'Die angelegte Ocean-Extra-Moduloption konnte nicht exakt bestätigt werden.', 'wp-agent-companion' ), array( 'status' => 500, 'write_started' => true ) );
	}
	try {
		do_action( "update_option_{$option}", maybe_unserialize( $before['raw'] ), $value, $option );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_ocean_extra_modules_write_failed', __( 'Die Ocean-Extra-Moduländerung konnte nicht sicher vorbereitet werden.', 'wp-agent-companion' ), array( 'status' => 502, 'write_started' => false ) );
	}
	$changed = $wpdb->query( $wpdb->prepare(
		"UPDATE {$wpdb->options} SET option_value = %s WHERE option_name = %s AND HEX(option_value) = HEX(%s) AND HEX(autoload) = HEX(%s)",
		$next['raw'], $option, $before['raw'], $before['autoload']
	) );
	wpagent_companion_forget_option_cache( $option, in_array( $before['autoload'], wp_autoload_values_to_autoload(), true ) );
	if ( 1 !== $changed ) return new WP_Error( false === $changed ? 'wpagent_ocean_extra_modules_write_failed' : 'wpagent_ocean_extra_modules_conflict', __( 'Die Ocean-Extra-Moduloption wurde gleichzeitig verändert und nicht überschrieben.', 'wp-agent-companion' ), array( 'status' => false === $changed ? 502 : 409, 'write_started' => false ) );
	try {
		do_action( 'updated_option', $option, maybe_unserialize( $before['raw'] ), $value );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_ocean_extra_modules_write_failed', __( 'Die Ocean-Extra-Moduloption wurde gespeichert, aber nicht vollständig bestätigt.', 'wp-agent-companion' ), array( 'status' => 500, 'write_started' => true ) );
	}
	$current = wpagent_ocean_extra_modules_raw_option_row( $option );
	return is_array( $current ) && wpagent_ocean_extra_modules_option_rows_equal( $current, $next )
		? $next
		: new WP_Error( 'wpagent_ocean_extra_modules_write_failed', __( 'Die Ocean-Extra-Moduloption konnte nicht exakt bestätigt werden.', 'wp-agent-companion' ), array( 'status' => 500, 'write_started' => true ) );
}

/** @return true|WP_Error */
function wpagent_ocean_extra_modules_write_checkpoint( string $lock, int $blog_id ) {
	$ready = wpagent_ocean_extra_modules_require_runtime();
	if ( is_wp_error( $ready ) ) return $ready;
	if ( (int) get_current_blog_id() !== $blog_id || ! current_user_can( 'manage_options' ) ) {
		return new WP_Error( 'wpagent_ocean_extra_modules_permission_changed', __( 'Die Berechtigung zum Verwalten der Ocean-Extra-Module ist nicht mehr vorhanden.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	if ( ! wpagent_ocean_extra_modules_refresh_lock( $lock ) || ! wpagent_companion_option_lock_is_owned( WPAGENT_OCEAN_EXTRA_MODULES_LOCK_OPTION, $lock ) ) {
		return new WP_Error( 'wpagent_ocean_extra_modules_lock_lost', __( 'Die Ocean-Extra-Moduländerung hat ihre exklusive Sperre verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return true;
}

function wpagent_ability_update_oceanwp_extra_module(): array {
	return array(
		'label'               => __( 'Ocean-Extra-Modul aktualisieren', 'wp-agent-companion' ),
		'description'         => __( 'Ändert genau einen geprüften Ocean-Extra-Modulschalter mit Konfliktschutz und Read-back. Sicherheitsrelevante Module kann der Agent nur deaktivieren.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'                 => 'object',
			'properties'           => array(
				'module'        => array( 'type' => 'string', 'enum' => array_keys( wpagent_ocean_extra_module_catalog() ) ),
				'enabled'       => array( 'type' => 'boolean' ),
				'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
			),
			'required'             => array( 'module', 'enabled', 'expected_hash' ),
			'additionalProperties' => false,
		),
		'output_schema'       => wpagent_ocean_extra_modules_output_schema( true ),
		'execute_callback'    => 'wpagent_exec_update_oceanwp_extra_module',
		'permission_callback' => 'wpagent_perm_manage_options',
		'meta'                => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false ) ),
	);
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_update_oceanwp_extra_module( $input ) {
	$ready = wpagent_ocean_extra_modules_require_runtime();
	if ( is_wp_error( $ready ) ) {
		return $ready;
	}
	$input   = is_array( $input ) ? $input : array();
	$catalog = wpagent_ocean_extra_module_catalog();
	$module  = is_string( $input['module'] ?? null ) ? $input['module'] : '';
	$enabled = $input['enabled'] ?? null;
	if ( ! isset( $catalog[ $module ] ) || ! is_bool( $enabled ) ) {
		return new WP_Error( 'wpagent_ocean_extra_module_invalid', __( 'Das Ocean-Extra-Modul oder sein Zielzustand ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( $enabled && 'disable_only' === $catalog[ $module ]['policy'] ) {
		return new WP_Error( 'wpagent_ocean_extra_module_enable_denied', __( 'Dieses sicherheitsrelevante Ocean-Extra-Modul darf der Agent nur deaktivieren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$lock = wpagent_ocean_extra_modules_acquire_lock();
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$blog_id = (int) get_current_blog_id();
		$ready = wpagent_ocean_extra_modules_require_runtime();
		if ( is_wp_error( $ready ) || ! current_user_can( 'manage_options' ) ) {
			return is_wp_error( $ready ) ? $ready : new WP_Error( 'wpagent_ocean_extra_modules_permission_changed', __( 'Die Berechtigung zum Verwalten der Ocean-Extra-Module ist nicht mehr vorhanden.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		}
		$before = wpagent_ocean_extra_modules_snapshot();
		if ( is_wp_error( $before ) ) {
			return $before;
		}
		$before_hash  = wpagent_ocean_extra_modules_state_hash( $before );
		$expected_hash = strtolower( (string) ( $input['expected_hash'] ?? '' ) );
		if ( ! preg_match( '/^[a-f0-9]{64}$/', $expected_hash ) || ! hash_equals( $before_hash, $expected_hash ) ) {
			return new WP_Error( 'wpagent_ocean_extra_modules_conflict', __( 'Die Ocean-Extra-Module haben sich seit dem Lesen geändert. Lies sie neu und wiederhole den Vorgang.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => $before_hash ) );
		}
		$definition = $catalog[ $module ];
		$option     = $definition['option'];
		$desired    = $enabled ? $definition['enabled'] : $definition['disabled'];
		$raw_before = wpagent_ocean_extra_modules_raw_option_row( $option );
		if ( is_wp_error( $raw_before ) ) return $raw_before;
		if ( ( $before['options'][ $option ]['exists'] && ( ! is_array( $raw_before ) || maybe_unserialize( $raw_before['raw'] ) !== $before['options'][ $option ]['value'] ) )
			|| ( ! $before['options'][ $option ]['exists'] && null !== $raw_before ) ) {
			return new WP_Error( 'wpagent_ocean_extra_modules_conflict', __( 'Die rohe Ocean-Extra-Moduloption änderte sich während der Vorbereitung.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$expected   = $before;
		$expected['options'][ $option ] = array( 'exists' => true, 'value' => $desired );
		$write_started = false;
		if ( $before['options'][ $option ]['exists'] && $before['options'][ $option ]['value'] === $desired ) {
			$checkpoint = wpagent_ocean_extra_modules_write_checkpoint( $lock, $blog_id );
			if ( is_wp_error( $checkpoint ) ) return $checkpoint;
			$fresh = wpagent_ocean_extra_modules_snapshot();
			$current_raw = wpagent_ocean_extra_modules_raw_option_row( $option );
			if ( is_wp_error( $fresh ) || is_wp_error( $current_raw ) || ! wpagent_ocean_extra_modules_option_rows_equal( $raw_before, $current_raw ) || ! hash_equals( $before_hash, wpagent_ocean_extra_modules_state_hash( $fresh ) ) ) {
				return new WP_Error( 'wpagent_ocean_extra_modules_conflict', __( 'Die Ocean-Extra-Module änderten sich vor der No-op-Bestätigung.', 'wp-agent-companion' ), array( 'status' => 409 ) );
			}
		} else {
			$checkpoint = wpagent_ocean_extra_modules_write_checkpoint( $lock, $blog_id );
			if ( is_wp_error( $checkpoint ) ) return $checkpoint;
			$current_raw = wpagent_ocean_extra_modules_raw_option_row( $option );
			if ( is_wp_error( $current_raw ) || ! wpagent_ocean_extra_modules_option_rows_equal( $raw_before, $current_raw ) ) {
				return new WP_Error( 'wpagent_ocean_extra_modules_conflict', __( 'Die Ocean-Extra-Moduloption wurde gleichzeitig verändert und nicht überschrieben.', 'wp-agent-companion' ), array( 'status' => 409 ) );
			}
			$written = wpagent_ocean_extra_modules_cas_option( $option, $desired, $raw_before );
			if ( is_wp_error( $written ) ) {
				$data = $written->get_error_data();
				return is_array( $data ) && ! empty( $data['write_started'] ) ? wpagent_ocean_extra_modules_recovery_required() : $written;
			}
			$write_started = ! wpagent_ocean_extra_modules_option_rows_equal( $raw_before, $written );
			$checkpoint = wpagent_ocean_extra_modules_write_checkpoint( $lock, $blog_id );
			if ( is_wp_error( $checkpoint ) ) return wpagent_ocean_extra_modules_recovery_required();
			$fresh = wpagent_ocean_extra_modules_snapshot();
			$current_raw = wpagent_ocean_extra_modules_raw_option_row( $option );
			if ( is_wp_error( $fresh ) || is_wp_error( $current_raw ) || ! wpagent_ocean_extra_modules_option_rows_equal( $written, $current_raw ) || ! hash_equals( wpagent_ocean_extra_modules_state_hash( $expected ), wpagent_ocean_extra_modules_state_hash( $fresh ) ) ) {
				return wpagent_ocean_extra_modules_recovery_required();
			}
			if ( ! wpagent_ocean_extra_modules_refresh_lock( $lock ) ) {
				return wpagent_ocean_extra_modules_recovery_required();
			}
		}
		$checkpoint = wpagent_ocean_extra_modules_write_checkpoint( $lock, $blog_id );
		if ( is_wp_error( $checkpoint ) ) return $write_started ? wpagent_ocean_extra_modules_recovery_required() : $checkpoint;
		return array(
			'modules'                    => wpagent_ocean_extra_modules_normalize( $fresh ),
			'state_hash'                 => wpagent_ocean_extra_modules_state_hash( $fresh ),
			'verified'                   => true,
			'effective_after_new_request' => true,
		);
	} finally {
		wpagent_ocean_extra_modules_release_lock( $lock );
	}
}
