<?php
/** Shared contract for verified Ocean Extra module switches. */

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

const WPAGENT_OCEAN_EXTRA_MODULES_THEME_VERSION = '4.2.2';
const WPAGENT_OCEAN_EXTRA_MODULES_LOCK_OPTION    = 'wp_agent_ocean_extra_modules_lock';
const WPAGENT_OCEAN_EXTRA_MODULES_LOCK_TTL       = 300;

/** @return array<string,array{option:string,default:string,enabled:string,disabled:string,policy:string}> */
function wpagent_ocean_extra_module_catalog(): array {
	return array(
		'library'                   => array( 'option' => 'oe_library_active_status', 'default' => 'yes', 'enabled' => 'yes', 'disabled' => 'no', 'policy' => 'read_write' ),
		'demo_catalog'              => array( 'option' => 'oe_install_demos_active', 'default' => 'yes', 'enabled' => 'yes', 'disabled' => 'no', 'policy' => 'read_write' ),
		'admin_notifications'       => array( 'option' => 'oe_notification_active_status', 'default' => 'no', 'enabled' => 'no', 'disabled' => 'yes', 'policy' => 'read_write' ),
		'svg_uploads'               => array( 'option' => 'oe_svg_support_active_status', 'default' => 'no', 'enabled' => 'yes', 'disabled' => 'no', 'policy' => 'disable_only' ),
		'front_end_style_editor'    => array( 'option' => 'oe_display_front_end_style_editor_active_status', 'default' => 'no', 'enabled' => 'yes', 'disabled' => 'no', 'policy' => 'disable_only' ),
	);
}

/** @return true|WP_Error */
function wpagent_ocean_extra_modules_require_runtime() {
	if ( ! wpagent_oceanwp_is_active() ) {
		return new WP_Error( 'wpagent_oceanwp_inactive', __( 'OceanWP oder ein OceanWP-Child-Theme ist nicht aktiv.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( ! class_exists( 'Ocean_Extra' )
		|| WPAGENT_OCEAN_EXTRA_MODULES_THEME_VERSION !== wpagent_oceanwp_version()
		|| WPAGENT_OCEAN_EXTRA_MIN_VERSION !== wpagent_oceanwp_extra_version() ) {
		return new WP_Error( 'wpagent_ocean_extra_modules_runtime_missing', __( 'Die geprüfte Ocean-Extra-Modulsteuerung erfordert genau OceanWP 4.2.2 und Ocean Extra 2.5.8.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return true;
}

/** @return array{blog_id:int,theme_version:string,plugin_version:string,options:array<string,array{exists:bool,value:string}>}|WP_Error */
function wpagent_ocean_extra_modules_snapshot() {
	global $wpdb;
	$catalog      = wpagent_ocean_extra_module_catalog();
	$option_names = array_values( array_column( $catalog, 'option' ) );
	$placeholders = implode( ', ', array_fill( 0, count( $option_names ), '%s' ) );
	$query        = $wpdb->prepare(
		"SELECT option_name, option_value FROM {$wpdb->options} WHERE option_name IN ({$placeholders})",
		...$option_names
	);
	$rows         = $wpdb->get_results( $query, ARRAY_A );
	if ( ! is_array( $rows ) ) {
		return new WP_Error( 'wpagent_ocean_extra_modules_storage_failed', __( 'Die Ocean-Extra-Moduloptionen konnten nicht sicher gelesen werden.', 'wp-agent-companion' ), array( 'status' => 502 ) );
	}
	$found = array();
	foreach ( $rows as $row ) {
		$name  = is_string( $row['option_name'] ?? null ) ? $row['option_name'] : '';
		$value = is_string( $row['option_value'] ?? null ) ? maybe_unserialize( $row['option_value'] ) : null;
		if ( '' === $name || ! is_string( $value ) || ! in_array( $value, array( 'yes', 'no' ), true ) || isset( $found[ $name ] ) ) {
			return new WP_Error( 'wpagent_ocean_extra_modules_invalid_state', __( 'Ocean Extra enthält einen nicht sicher auswertbaren Modulzustand.', 'wp-agent-companion' ), array( 'status' => 502 ) );
		}
		$found[ $name ] = $value;
	}
	$options = array();
	foreach ( $catalog as $definition ) {
		$name             = $definition['option'];
		$options[ $name ] = isset( $found[ $name ] )
			? array( 'exists' => true, 'value' => $found[ $name ] )
			: array( 'exists' => false, 'value' => $definition['default'] );
	}
	return array(
		'blog_id'        => (int) get_current_blog_id(),
		'theme_version'  => wpagent_oceanwp_version(),
		'plugin_version' => wpagent_oceanwp_extra_version(),
		'options'        => $options,
	);
}

/** @param array{blog_id:int,theme_version:string,plugin_version:string,options:array<string,array{exists:bool,value:string}>} $snapshot */
function wpagent_ocean_extra_modules_state_hash( array $snapshot ): string {
	$state = $snapshot['blog_id'] . "\0" . $snapshot['theme_version'] . "\0" . $snapshot['plugin_version'];
	foreach ( $snapshot['options'] as $name => $entry ) {
		$state .= "\0" . $name . "\0" . ( $entry['exists'] ? '1' : '0' ) . "\0" . $entry['value'];
	}
	return hash( 'sha256', $state );
}

/**
 * @param array{blog_id:int,theme_version:string,plugin_version:string,options:array<string,array{exists:bool,value:string}>} $snapshot
 * @return array<string,array{enabled:bool,write_policy:string}>
 */
function wpagent_ocean_extra_modules_normalize( array $snapshot ): array {
	$modules = array();
	foreach ( wpagent_ocean_extra_module_catalog() as $name => $definition ) {
		$modules[ $name ] = array(
			'enabled'      => $snapshot['options'][ $definition['option'] ]['value'] === $definition['enabled'],
			'write_policy' => $definition['policy'],
		);
	}
	return $modules;
}

/** @return array<string,mixed> */
function wpagent_ocean_extra_modules_output_schema( bool $write = false ): array {
	$module = array(
		'type'                 => 'object',
		'properties'           => array(
			'enabled'      => array( 'type' => 'boolean' ),
			'write_policy' => array( 'type' => 'string', 'enum' => array( 'read_write', 'disable_only' ) ),
		),
		'required'             => array( 'enabled', 'write_policy' ),
		'additionalProperties' => false,
	);
	$properties = array(
		'modules'    => array(
			'type'                 => 'object',
			'properties'           => array_fill_keys( array_keys( wpagent_ocean_extra_module_catalog() ), $module ),
			'required'             => array_keys( wpagent_ocean_extra_module_catalog() ),
			'additionalProperties' => false,
		),
		'state_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
	);
	$required = array( 'modules', 'state_hash' );
	if ( $write ) {
		$properties['verified']                    = array( 'type' => 'boolean' );
		$properties['effective_after_new_request'] = array( 'type' => 'boolean' );
		$required[] = 'verified';
		$required[] = 'effective_after_new_request';
	}
	return array( 'type' => 'object', 'properties' => $properties, 'required' => $required, 'additionalProperties' => false );
}

/** @return string|WP_Error */
function wpagent_ocean_extra_modules_acquire_lock() {
	$token = wpagent_companion_acquire_option_lock( WPAGENT_OCEAN_EXTRA_MODULES_LOCK_OPTION, WPAGENT_OCEAN_EXTRA_MODULES_LOCK_TTL );
	if ( is_string( $token ) ) {
		return $token;
	}
	return new WP_Error( 'wpagent_ocean_extra_modules_locked', __( 'Die Ocean-Extra-Module werden gerade durch einen anderen Vorgang geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

function wpagent_ocean_extra_modules_refresh_lock( string $token ): bool {
	return wpagent_companion_refresh_option_lock( WPAGENT_OCEAN_EXTRA_MODULES_LOCK_OPTION, $token, WPAGENT_OCEAN_EXTRA_MODULES_LOCK_TTL );
}

function wpagent_ocean_extra_modules_release_lock( string $token ): void {
	wpagent_companion_release_option_lock( WPAGENT_OCEAN_EXTRA_MODULES_LOCK_OPTION, $token );
}

function wpagent_ocean_extra_modules_recovery_required(): WP_Error {
	$data     = array( 'status' => 500, 'rollback_verified' => false, 'recovery_required' => true );
	$snapshot = wpagent_ocean_extra_modules_snapshot();
	if ( is_array( $snapshot ) ) {
		$data['current_hash']    = wpagent_ocean_extra_modules_state_hash( $snapshot );
		$data['current_modules'] = wpagent_ocean_extra_modules_normalize( $snapshot );
	}
	return new WP_Error( 'wpagent_ocean_extra_modules_recovery_required', __( 'Die Ocean-Extra-Modulä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 );
}
