<?php
/** Shared state, locking and rollback helpers for WordPress Global Styles. */

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

define( 'WPAGENT_GLOBAL_STYLES_LOCK_OPTION', 'wp_agent_global_styles_lock' );
define( 'WPAGENT_GLOBAL_STYLES_LOCK_TTL', 300 );
define( 'WPAGENT_GLOBAL_STYLES_MAX_BYTES', 524288 );

/** @return array<string,mixed>|WP_Error */
function wpagent_gs_state() {
	if ( ! class_exists( 'WP_Theme_JSON_Resolver' ) ) {
		return new WP_Error( 'wpagent_gs_unsupported', __( 'WP_Theme_JSON_Resolver ist nicht verfügbar (Block-Theme/Global-Styles benötigt).', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$post_id = (int) WP_Theme_JSON_Resolver::get_user_global_styles_post_id();
	if ( $post_id <= 0 ) {
		return new WP_Error( 'wpagent_gs_no_post', __( 'User-Global-Styles-Post konnte nicht ermittelt werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$post = get_post( $post_id );
	if ( ! $post || 'wp_global_styles' !== $post->post_type ) {
		return new WP_Error( 'wpagent_gs_no_post', __( 'wp_global_styles-Post nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$raw      = (string) $post->post_content;
	if ( strlen( $raw ) > WPAGENT_GLOBAL_STYLES_MAX_BYTES ) {
		return new WP_Error( 'wpagent_gs_state_too_large', __( 'Der gespeicherte Global-Styles-Datensatz ist größer als 512 KiB und wird deshalb nicht über den Agenten verarbeitet.', 'wp-agent-companion' ), array( 'status' => 413 ) );
	}
	$snapshot_export = json_decode( $raw );
	$snapshot        = json_decode( $raw, true );
	if ( ! is_object( $snapshot_export ) || ! is_array( $snapshot ) ) {
		return new WP_Error( 'wpagent_gs_invalid_state', __( 'Der gespeicherte Global-Styles-Datensatz ist kein gültiges JSON-Objekt.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$encoded = wp_json_encode( $snapshot_export, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
	if ( false === $encoded ) {
		return new WP_Error( 'wpagent_gs_invalid_state', __( 'Der gespeicherte Global-Styles-Datensatz konnte nicht sicher gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( strlen( $encoded ) > WPAGENT_GLOBAL_STYLES_MAX_BYTES ) {
		return new WP_Error( 'wpagent_gs_state_too_large', __( 'Der gespeicherte Global-Styles-Datensatz ist größer als 512 KiB und wird deshalb nicht über den Agenten verarbeitet.', 'wp-agent-companion' ), array( 'status' => 413 ) );
	}
	$theme = (string) get_stylesheet();
	return array(
		'post_id'         => $post_id,
		'theme'           => $theme,
		'raw'             => $raw,
		'snapshot'        => $snapshot,
		'snapshot_export' => $snapshot_export,
		'snapshot_bytes'  => strlen( $encoded ),
		'state_hash'      => wpagent_gs_state_hash( $theme, $post_id, $raw ),
	);
}

function wpagent_gs_state_hash( string $theme, int $post_id, string $raw ): string {
	return hash( 'sha256', "wpagently-global-styles-v1\0" . $theme . "\0" . $post_id . "\0" . $raw );
}

/** Confirms that the active dataset and its exact decoded content still match the plan. */
function wpagent_gs_final_state_matches( array $fresh, array $initial, array $planned ): bool {
	return (int) ( $fresh['post_id'] ?? 0 ) === (int) ( $initial['post_id'] ?? 0 )
		&& isset( $fresh['snapshot'] )
		&& is_array( $fresh['snapshot'] )
		&& $fresh['snapshot'] === $planned;
}

/**
 * Encodes a user Global Styles document while keeping its object-shaped roots.
 * PHP otherwise serializes an empty associative array as JSON `[]`, although
 * WordPress theme.json requires `settings` and `styles` to remain objects.
 *
 * @return string|WP_Error
 */
function wpagent_gs_encode_user_config( array $data ) {
	$encoded_data             = $data;
	$encoded_data['settings'] = (object) ( isset( $data['settings'] ) && is_array( $data['settings'] ) ? $data['settings'] : array() );
	$encoded_data['styles']   = (object) ( isset( $data['styles'] ) && is_array( $data['styles'] ) ? $data['styles'] : array() );
	$json                     = wp_json_encode( $encoded_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
	if ( false === $json ) {
		return new WP_Error( 'wpagent_gs_encode', __( 'Global-Styles-Daten konnten nicht kodiert werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	return $json;
}

/** @return string|WP_Error */
function wpagent_gs_expected_hash( $value ) {
	$hash = is_string( $value ) ? strtolower( $value ) : '';
	return preg_match( '/^[a-f0-9]{64}$/', $hash )
		? $hash
		: new WP_Error( 'wpagent_gs_expected_hash', __( 'expected_hash muss ein vollständiger SHA-256-Hash sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
}

/** @return string|WP_Error */
function wpagent_gs_acquire_lock() {
	$token = wpagent_companion_acquire_option_lock( WPAGENT_GLOBAL_STYLES_LOCK_OPTION, WPAGENT_GLOBAL_STYLES_LOCK_TTL );
	if ( is_string( $token ) ) {
		return $token;
	}
	return new WP_Error( 'wpagent_gs_busy', __( 'Das globale Design wird gerade geändert. Versuche es gleich erneut.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

function wpagent_gs_release_lock( string $token ): void {
	wpagent_companion_release_option_lock( WPAGENT_GLOBAL_STYLES_LOCK_OPTION, $token );
}

function wpagent_gs_clean_caches( int $post_id ): void {
	clean_post_cache( $post_id );
	WP_Theme_JSON_Resolver::clean_cached_data();
	if ( function_exists( 'wp_clean_theme_json_cache' ) ) {
		wp_clean_theme_json_cache();
	}
}

/** @param array<string,mixed> $snapshot */
function wpagent_gs_restore_snapshot( array $snapshot ): bool {
	$updated = wp_update_post(
		array( 'ID' => (int) $snapshot['post_id'], 'post_content' => wp_slash( (string) $snapshot['raw'] ) ),
		true
	);
	if ( is_wp_error( $updated ) ) {
		return false;
	}
	wpagent_gs_clean_caches( (int) $snapshot['post_id'] );
	clean_post_cache( (int) $snapshot['post_id'] );
	$post = get_post( (int) $snapshot['post_id'] );
	return $post && hash_equals( (string) $snapshot['raw'], (string) $post->post_content );
}

/** @param array<string,mixed> $snapshot */
function wpagent_gs_fail_after_rollback( array $snapshot, WP_Error $failure ): WP_Error {
	if ( wpagent_gs_restore_snapshot( $snapshot ) ) {
		return $failure;
	}
	return new WP_Error(
		'wpagent_gs_rollback_failed',
		__( 'Die Global-Styles-Änderung konnte nicht verifiziert und der vorherige Zustand nicht vollständig wiederhergestellt werden.', 'wp-agent-companion' ),
		array( 'status' => 500, 'original_error' => $failure->get_error_code() )
	);
}

function wpagent_ability_get_global_styles(): array {
	return array(
		'label'               => __( 'Global Styles lesen und exportieren', 'wp-agent-companion' ),
		'description'         => __( 'Liest den vollständigen User-Global-Styles-Datensatz als exportierbaren Snapshot mit exaktem Konflikt-Hash.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array( 'type' => 'object', 'properties' => array(), 'additionalProperties' => false, 'default' => array() ),
		'output_schema'       => array(
			'type' => 'object', 'properties' => array(
				'post_id' => array( 'type' => 'integer' ), 'theme' => array( 'type' => 'string' ),
				'snapshot' => array( 'type' => 'object' ), 'snapshot_bytes' => array( 'type' => 'integer' ),
				'state_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
			),
			'required' => array( 'post_id', 'theme', 'snapshot', 'snapshot_bytes', 'state_hash' ), 'additionalProperties' => false,
		),
		'execute_callback'    => 'wpagent_exec_get_global_styles',
		'permission_callback' => 'wpagent_perm_edit_theme_options',
		'meta'                => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => true, 'destructive' => false, 'idempotent' => true ) ),
	);
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_get_global_styles() {
	$state = wpagent_gs_state();
	if ( is_wp_error( $state ) ) {
		return $state;
	}
	$state['snapshot'] = $state['snapshot_export'];
	unset( $state['raw'] );
	unset( $state['snapshot_export'] );
	return $state;
}
