<?php
/** Atomic lock, persistence and verified rollback for Kadence provider state. */

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

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

function wpagent_kadence_release_lock( string $token ): void {
	wpagent_companion_release_option_lock( WPAGENT_KADENCE_LOCK_OPTION, $token );
}

function wpagent_kadence_refresh_lock( string $token ): bool {
	return wpagent_companion_refresh_option_lock( WPAGENT_KADENCE_LOCK_OPTION, $token, WPAGENT_KADENCE_LOCK_TTL );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_kadence_prepare_next_for_write( string $section, array $previous, array $next ) {
	$name = 'palette' === $section ? WPAGENT_KADENCE_PALETTE_OPTION : wpagent_kadence_settings_option_name();
	$key  = 'palette' === $section ? 'palette' : 'settings';
	$old  = $previous[ $key ]['exists'] ? $previous[ $key ]['value'] : false;
	$value = $next[ $key ]['value'];
	try {
		$value = sanitize_option( $name, $value );
		$value = apply_filters( "pre_update_option_{$name}", $value, $old, $name );
		$value = apply_filters( 'pre_update_option', $value, $name, $old );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_kadence_write_precondition_failed', __( 'Kadence konnte den Providerwert vor dem Schreiben nicht sicher vorbereiten.', 'wp-agent-companion' ), array( 'status' => 409, 'mutation_started' => false ) );
	}
	if ( 'palette' === $section ) {
		if ( ! is_string( $value ) ) {
			return wpagent_kadence_invalid_provider_state();
		}
		$decoded = wpagent_kadence_decode_palette( $value );
		if ( is_wp_error( $decoded ) ) {
			return $decoded;
		}
		$next['palette']['value']   = $value;
		$next['palette']['decoded'] = $decoded;
	} else {
		if ( ! is_array( $value ) ) {
			return wpagent_kadence_invalid_provider_state();
		}
		$next['settings']['value'] = $value;
	}
	return $next;
}

function wpagent_kadence_write_conflict(): WP_Error {
	$data     = array( 'status' => 409, 'mutation_started' => false );
	$snapshot = wpagent_kadence_snapshot();
	if ( is_array( $snapshot ) ) {
		$data['current_hash'] = wpagent_kadence_state_hash( $snapshot );
	}
	return new WP_Error(
		'wpagent_kadence_write_conflict',
		__( 'Die Kadence-Einstellungen wurden unmittelbar vor dem Speichern anderweitig geändert. Der wpagently-Write wurde nicht ausgeführt. Lies den aktuellen Zustand neu.', 'wp-agent-companion' ),
		$data
	);
}

/** @return true|WP_Error */
function wpagent_kadence_compare_and_swap_option( string $name, array $before, array $next ) {
	global $wpdb;
	$before_row = wpagent_kadence_option_entry_row( $name, $before );
	$next_row   = wpagent_kadence_option_entry_row( $name, $next );
	if ( null === $next_row ) {
		return new WP_Error( 'wpagent_kadence_write_failed', __( 'Kadence konnte den Providerwert nicht sicher speichern.', 'wp-agent-companion' ), array( 'status' => 502, 'mutation_started' => false ) );
	}
	$current_row = wpagent_companion_read_option_row_direct( $name );
	if ( '' !== $wpdb->last_error || ! wpagent_kadence_option_rows_match( $current_row, $before_row ) ) {
		return wpagent_kadence_write_conflict();
	}
	if ( wpagent_kadence_option_rows_match( $before_row, $next_row ) ) {
		return true;
	}
	if ( null === $before_row ) {
		try {
			$created = wpagent_companion_add_option_create_only( $name, $next['value'], '', null );
		} catch ( Throwable $error ) {
			return wpagent_kadence_write_conflict();
		}
		return $created ? true : wpagent_kadence_write_conflict();
	}

	try {
		do_action( "update_option_{$name}", $before['value'], $next['value'], $name );
	} catch ( Throwable $error ) {
		return wpagent_kadence_write_conflict();
	}
	$current_row = wpagent_companion_read_option_row_direct( $name );
	if ( '' !== $wpdb->last_error || ! wpagent_kadence_option_rows_match( $current_row, $before_row ) ) {
		return wpagent_kadence_write_conflict();
	}
	$changed = $wpdb->query(
		$wpdb->prepare(
			"UPDATE {$wpdb->options} SET option_value = %s, autoload = %s WHERE option_name = %s AND HEX(option_value) = HEX(%s) AND HEX(autoload) = HEX(%s)",
			$next_row['raw'],
			$next_row['autoload'],
			$name,
			$before_row['raw'],
			$before_row['autoload']
		)
	);
	wpagent_companion_forget_option_cache(
		$name,
		in_array( $before_row['autoload'], wp_autoload_values_to_autoload(), true )
			|| in_array( $next_row['autoload'], wp_autoload_values_to_autoload(), true )
	);
	if ( 1 !== $changed ) {
		return false === $changed
			? new WP_Error( 'wpagent_kadence_write_failed', __( 'Kadence konnte den Providerwert nicht sicher speichern.', 'wp-agent-companion' ), array( 'status' => 502, 'mutation_started' => false ) )
			: wpagent_kadence_write_conflict();
	}
	try {
		do_action( 'updated_option', $name, $before['value'], $next['value'] );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_kadence_write_failed', __( 'Kadence konnte den Providerwert nach dem Speichern nicht sicher bestätigen.', 'wp-agent-companion' ), array( 'status' => 502, 'mutation_started' => true ) );
	}
	return true;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_kadence_write_next( string $section, array $previous, array $next ) {
	$mutation_started = false;
	try {
		if ( 'palette' === $section ) {
			$expected_autoload = $next['palette']['autoload'];
			$written = wpagent_kadence_compare_and_swap_option( WPAGENT_KADENCE_PALETTE_OPTION, $previous['palette'], $next['palette'] );
			if ( is_wp_error( $written ) ) {
				return $written;
			}
			$mutation_started = true;
			wp_cache_delete( WPAGENT_KADENCE_PALETTE_OPTION, 'options' );
			$stored = get_option( WPAGENT_KADENCE_PALETTE_OPTION, null );
			if ( ! is_string( $stored ) ) {
				return new WP_Error( 'wpagent_kadence_write_mismatch', __( 'Kadence hat die angeforderte Palette nicht vollständig gespeichert.', 'wp-agent-companion' ), array( 'status' => 502, 'mutation_started' => true ) );
			}
			$decoded = wpagent_kadence_decode_palette( $stored );
			if ( is_wp_error( $decoded ) || maybe_serialize( $decoded ) !== maybe_serialize( $next['palette']['decoded'] ) ) {
				return new WP_Error( 'wpagent_kadence_write_mismatch', __( 'Kadence hat die angeforderte Palette nicht vollständig gespeichert.', 'wp-agent-companion' ), array( 'status' => 502, 'mutation_started' => true ) );
			}
			$autoload = wpagent_kadence_option_autoload( WPAGENT_KADENCE_PALETTE_OPTION );
			if ( null === $autoload || ( is_string( $expected_autoload ) && $expected_autoload !== $autoload ) ) {
				return new WP_Error( 'wpagent_kadence_write_mismatch', __( 'Kadence hat die angeforderte Palette nicht vollständig gespeichert.', 'wp-agent-companion' ), array( 'status' => 502, 'mutation_started' => true ) );
			}
			$next['palette'] = array( 'exists' => true, 'value' => $stored, 'decoded' => $decoded, 'autoload' => $autoload );
		} else {
			$settings_option  = wpagent_kadence_settings_option_name();
			$expected_autoload = $next['settings']['autoload'];
			$written = wpagent_kadence_compare_and_swap_option( $settings_option, $previous['settings'], $next['settings'] );
			if ( is_wp_error( $written ) ) {
				return $written;
			}
			$mutation_started = true;
			wp_cache_delete( $settings_option, 'options' );
			$stored = get_option( $settings_option, null );
			if ( ! is_array( $stored ) || maybe_serialize( $stored ) !== maybe_serialize( $next['settings']['value'] ) ) {
				return new WP_Error( 'wpagent_kadence_write_mismatch', __( 'Kadence hat die angeforderten Einstellungen nicht vollständig gespeichert.', 'wp-agent-companion' ), array( 'status' => 502, 'mutation_started' => true ) );
			}
			$autoload = wpagent_kadence_option_autoload( $settings_option );
			if ( null === $autoload || ( is_string( $expected_autoload ) && $expected_autoload !== $autoload ) ) {
				return new WP_Error( 'wpagent_kadence_write_mismatch', __( 'Kadence hat die angeforderten Einstellungen nicht vollständig gespeichert.', 'wp-agent-companion' ), array( 'status' => 502, 'mutation_started' => true ) );
			}
			$next['settings'] = array( 'exists' => true, 'value' => $stored, 'autoload' => $autoload );
		}
		return $next;
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_kadence_write_failed', __( 'Kadence konnte die Einstellungen nicht sicher speichern.', 'wp-agent-companion' ), array( 'status' => 502, 'mutation_started' => $mutation_started ) );
	}
}

/** @return array{raw:string,autoload:string}|null */
function wpagent_kadence_option_entry_row( string $name, array $entry ): ?array {
	if ( ! $entry['exists'] ) {
		return null;
	}
	$raw      = maybe_serialize( $entry['value'] );
	$autoload = $entry['autoload'];
	if ( ! is_string( $autoload ) ) {
		$autoload = wp_determine_option_autoload_value( $name, $entry['value'], $raw, null );
	}
	return array( 'raw' => $raw, 'autoload' => $autoload );
}

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

/**
 * Restores only the option changed by this request and only while its raw row
 * still equals the exact value this request intended to store. A provider or
 * third-party mutation is retained for manual recovery instead of being
 * overwritten by a stale full-option snapshot.
 */
function wpagent_kadence_restore_option_cas( string $name, array $before, array $expected_current ): bool {
	global $wpdb;
	$before_row  = wpagent_kadence_option_entry_row( $name, $before );
	$expected_row = wpagent_kadence_option_entry_row( $name, $expected_current );
	$current_row = wpagent_companion_read_option_row_direct( $name );
	if ( '' !== $wpdb->last_error ) {
		return false;
	}
	if ( wpagent_kadence_option_rows_match( $current_row, $before_row ) ) {
		return true;
	}
	if ( ! wpagent_kadence_option_rows_match( $current_row, $expected_row ) || null === $current_row ) {
		return false;
	}

	if ( null === $before_row ) {
		$changed = $wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$wpdb->options} WHERE option_name = %s AND HEX(option_value) = HEX(%s) AND HEX(autoload) = HEX(%s)",
				$name,
				$current_row['raw'],
				$current_row['autoload']
			)
		);
	} else {
		$changed = $wpdb->query(
			$wpdb->prepare(
				"UPDATE {$wpdb->options} SET option_value = %s, autoload = %s WHERE option_name = %s AND HEX(option_value) = HEX(%s) AND HEX(autoload) = HEX(%s)",
				$before_row['raw'],
				$before_row['autoload'],
				$name,
				$current_row['raw'],
				$current_row['autoload']
			)
		);
	}
	wpagent_companion_forget_option_cache(
		$name,
		in_array( $current_row['autoload'], wp_autoload_values_to_autoload(), true )
			|| ( is_array( $before_row ) && in_array( $before_row['autoload'], wp_autoload_values_to_autoload(), true ) )
	);
	if ( 1 !== $changed ) {
		return false;
	}
	$restored = wpagent_companion_read_option_row_direct( $name );
	return '' === $wpdb->last_error && wpagent_kadence_option_rows_match( $restored, $before_row );
}

function wpagent_kadence_recovery_required(): WP_Error {
	$data     = array( 'status' => 500, 'rollback_verified' => false, 'recovery_required' => true );
	$snapshot = wpagent_kadence_snapshot();
	if ( is_array( $snapshot ) ) {
		$data['current_hash'] = wpagent_kadence_state_hash( $snapshot );
	}
	return new WP_Error(
		'wpagent_kadence_recovery_required',
		__( 'Die Kadence-Änderung wurde begonnen, aber nicht vollständig verifiziert. Eine fremde oder unklare Änderung wurde nicht automatisch überschrieben. Prüfe den aktuellen Zustand manuell und lies ihn anschließend neu.', 'wp-agent-companion' ),
		$data
	);
}

function wpagent_kadence_rollback( string $section, array $snapshot, array $expected_current, string $expected_hash, string $lock_token ): bool {
	if ( ! current_user_can( 'manage_options' ) || ! wpagent_kadence_refresh_lock( $lock_token ) ) {
		return false;
	}
	$name = 'palette' === $section ? WPAGENT_KADENCE_PALETTE_OPTION : wpagent_kadence_settings_option_name();
	$key  = 'palette' === $section ? 'palette' : 'settings';
	if ( ! wpagent_kadence_restore_option_cas( $name, $snapshot[ $key ], $expected_current[ $key ] ) ) {
		return false;
	}
	if ( ! wpagent_kadence_refresh_lock( $lock_token ) ) {
		return false;
	}
	$fresh = wpagent_kadence_snapshot();
	return is_array( $fresh )
		&& hash_equals( $expected_hash, wpagent_kadence_state_hash( $fresh ) )
		&& wpagent_kadence_snapshot_matches( $fresh, $snapshot );
}

function wpagent_kadence_fail_after_rollback( string $section, array $snapshot, array $expected_current, string $expected_hash, string $lock_token ): WP_Error {
	if ( ! wpagent_kadence_rollback( $section, $snapshot, $expected_current, $expected_hash, $lock_token ) ) {
		return wpagent_kadence_recovery_required();
	}
	return new WP_Error( 'wpagent_kadence_verification_failed', __( 'Die Kadence-Änderung konnte nicht bestätigt werden. Der exakt zugeordnete vorherige Zustand wurde per Compare-and-Swap wiederhergestellt und verifiziert.', 'wp-agent-companion' ), array( 'status' => 502, 'rollback_verified' => true, 'recovery_required' => false ) );
}

function wpagent_kadence_snapshot_matches( array $left, array $right ): bool {
	return $left['settings']['exists'] === $right['settings']['exists']
		&& maybe_serialize( $left['settings']['value'] ) === maybe_serialize( $right['settings']['value'] )
		&& $left['settings']['autoload'] === $right['settings']['autoload']
		&& $left['palette']['exists'] === $right['palette']['exists']
		&& $left['palette']['value'] === $right['palette']['value']
		&& $left['palette']['autoload'] === $right['palette']['autoload'];
}
