<?php
/** Atomic lock and fail-closed persistence for OceanWP theme mods. */

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

/** @return string|WP_Error */
function wpagent_oceanwp_acquire_lock() {
	$token = wpagent_companion_acquire_option_lock( WPAGENT_OCEANWP_LOCK_OPTION, WPAGENT_OCEANWP_LOCK_TTL );
	if ( is_string( $token ) ) {
		return $token;
	}
	return new WP_Error( 'wpagent_oceanwp_locked', __( 'Die OceanWP-Einstellungen werden gerade durch einen anderen Vorgang geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

function wpagent_oceanwp_release_lock( string $token ): void {
	wpagent_companion_release_option_lock( WPAGENT_OCEANWP_LOCK_OPTION, $token );
}

function wpagent_oceanwp_refresh_lock( string $token ): bool {
	return wpagent_companion_refresh_option_lock(
		WPAGENT_OCEANWP_LOCK_OPTION,
		$token,
		WPAGENT_OCEANWP_LOCK_TTL
	);
}

/** @return true|WP_Error */
function wpagent_oceanwp_assert_writable_mode( array $mods ) {
	$mode = $mods['ocean_customzer_styling'] ?? 'head';
	if ( 'file' === $mode ) {
		return new WP_Error(
			'wpagent_oceanwp_file_css_mode',
			__( 'OceanWP schreibt Customizer-CSS derzeit in eine Datei. Stelle unter OceanWP > Customizer > Performance die Customizer-Stile auf den Head-Modus, damit WPAgently Änderungen sicher verifizieren kann.', 'wp-agent-companion' ),
			array( 'status' => 409 )
		);
	}
	if ( 'head' !== $mode ) {
		return new WP_Error( 'wpagent_oceanwp_invalid_css_mode', __( 'OceanWP verwendet einen unbekannten Customizer-CSS-Modus.', 'wp-agent-companion' ), array( 'status' => 502 ) );
	}
	return true;
}

/** @return WP_Error */
function wpagent_oceanwp_write_lock_lost( bool $write_started ): WP_Error {
	return new WP_Error(
		$write_started ? 'wpagent_oceanwp_lock_lost_after_write' : 'wpagent_oceanwp_lock_lost',
		__( 'Die OceanWP-Änderung hat ihre exklusive Sperre verloren.', 'wp-agent-companion' ),
		array( 'status' => $write_started ? 500 : 409, 'write_started' => $write_started )
	);
}

/** @param array{raw:string,autoload:string}|null $left @param array{raw:string,autoload:string}|null $right */
function wpagent_oceanwp_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_oceanwp_prepared_option_row( string $option_name, array $mods, ?array $before ) {
	$old = is_array( $before ) ? maybe_unserialize( $before['raw'] ) : false;
	try {
		$prepared = sanitize_option( $option_name, $mods );
		$prepared = apply_filters( "pre_update_option_{$option_name}", $prepared, $old, $option_name );
		$prepared = apply_filters( 'pre_update_option', $prepared, $option_name, $old );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_oceanwp_write_failed', __( 'OceanWP konnte die Theme-Mod-Änderung nicht sicher vorbereiten.', 'wp-agent-companion' ), array( 'status' => 502, 'write_started' => false ) );
	}
	if ( ! is_array( $prepared ) || maybe_serialize( $prepared ) !== maybe_serialize( $mods ) ) {
		return new WP_Error( 'wpagent_oceanwp_conflict', __( 'Ein Filter hat die geprüfte OceanWP-Theme-Mod-Ä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_name, $prepared, $raw, null ),
	);
}

/** @return array{raw:string,autoload:string}|WP_Error */
function wpagent_oceanwp_write( string $option_name, array $mods, string $lock, ?array $before, int $blog_id ) {
	global $wpdb;
	$checkpoint = static function () use ( $lock, $blog_id ): bool {
		return (int) get_current_blog_id() === $blog_id
			&& wpagent_perm_manage_options()
			&& wpagent_oceanwp_refresh_lock( $lock )
			&& wpagent_companion_option_lock_is_owned( WPAGENT_OCEANWP_LOCK_OPTION, $lock );
	};
	if ( ! $checkpoint() ) {
		return wpagent_oceanwp_write_lock_lost( false );
	}
	$next = wpagent_oceanwp_prepared_option_row( $option_name, $mods, $before );
	if ( is_wp_error( $next ) ) return $next;
	if ( wpagent_oceanwp_option_rows_equal( $before, $next ) ) return $next;
	if ( null === $before ) {
		try {
			$created = wpagent_companion_add_option_create_only( $option_name, $mods, '', $next['autoload'] );
		} catch ( Throwable $error ) {
			return new WP_Error( 'wpagent_oceanwp_write_failed', __( 'OceanWP konnte die Theme-Mod-Option nicht atomar anlegen.', 'wp-agent-companion' ), array( 'status' => 502, 'write_started' => false ) );
		}
		if ( ! $created ) {
			return new WP_Error( 'wpagent_oceanwp_conflict', __( 'Die OceanWP-Theme-Mod-Option wurde gleichzeitig angelegt.', 'wp-agent-companion' ), array( 'status' => 409, 'write_started' => false ) );
		}
		$stored = wpagent_companion_read_option_row_direct( $option_name );
		return $checkpoint() && wpagent_oceanwp_option_rows_equal( $stored, $next )
			? $next
			: new WP_Error( 'wpagent_oceanwp_write_mismatch', __( 'Die angelegte OceanWP-Theme-Mod-Option konnte nicht exakt bestätigt werden.', 'wp-agent-companion' ), array( 'status' => 500, 'write_started' => true ) );
	}
	try {
		do_action( "update_option_{$option_name}", maybe_unserialize( $before['raw'] ), $mods, $option_name );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_oceanwp_write_failed', __( 'OceanWP konnte die Theme-Mod-Änderung nicht sicher vorbereiten.', 'wp-agent-companion' ), array( 'status' => 502, 'write_started' => false ) );
	}
	if ( ! $checkpoint() ) return wpagent_oceanwp_write_lock_lost( 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_name, $before['raw'], $before['autoload']
	) );
	wpagent_companion_forget_option_cache( $option_name, in_array( $before['autoload'], wp_autoload_values_to_autoload(), true ) );
	if ( 1 !== $changed ) {
		return new WP_Error( false === $changed ? 'wpagent_oceanwp_write_failed' : 'wpagent_oceanwp_conflict', __( 'Die OceanWP-Theme-Mods wurden gleichzeitig verändert und nicht überschrieben.', 'wp-agent-companion' ), array( 'status' => false === $changed ? 502 : 409, 'write_started' => false ) );
	}
	try {
		do_action( 'updated_option', $option_name, maybe_unserialize( $before['raw'] ), $mods );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_oceanwp_write_failed', __( 'Die OceanWP-Theme-Mod-Änderung wurde gespeichert, aber nicht vollständig bestätigt.', 'wp-agent-companion' ), array( 'status' => 500, 'write_started' => true ) );
	}
	$stored = wpagent_companion_read_option_row_direct( $option_name );
	return $checkpoint() && wpagent_oceanwp_option_rows_equal( $stored, $next )
		? $next
		: new WP_Error( 'wpagent_oceanwp_write_mismatch', __( 'OceanWP hat die angeforderten Theme-Mods nicht vollständig gespeichert.', 'wp-agent-companion' ), array( 'status' => 500, 'write_started' => true ) );
}

/**
 * Returns a manual-recovery error without writing a possibly stale snapshot.
 *
 * A theme-mod bundle has no portable compare-and-swap primitive. After the
 * first provider write, a Customizer request may change an unrelated key
 * between any check and a restoration. Replaying a previous full option would
 * therefore risk erasing that third-party change. Keep every observed state
 * intact and provide only bounded, validated diagnostic state.
 *
 * @return WP_Error
 */
function wpagent_oceanwp_recovery_required(): WP_Error {
	$data     = array( 'status' => 500, 'rollback_verified' => false, 'recovery_required' => true );
	$snapshot = wpagent_oceanwp_snapshot();
	if ( is_array( $snapshot ) ) {
		$data['current_hash'] = wpagent_oceanwp_state_hash( $snapshot );
		$sections             = array();
		foreach ( array_keys( wpagent_oceanwp_section_schemas() ) as $section ) {
			$current = wpagent_oceanwp_read_section( $section, $snapshot['mods']['value'] );
			if ( is_wp_error( $current ) ) {
				$sections = array();
				break;
			}
			$sections[ $section ] = $current;
		}
		if ( $sections ) {
			$data['current_sections'] = $sections;
		}
	}
	return new WP_Error(
		'wpagent_oceanwp_recovery_required',
		__( 'Die OceanWP-Ä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
	);
}
