<?php
/** Verified create, update, duplicate and delete abilities for ACF field groups. */

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

/** @return array<string,mixed> */
function wpagent_acf_group_write_result_schema(): array {
	return array(
		'type' => 'object',
		'properties' => array(
			'group' => wpagent_acf_group_output_schema(),
			'verified' => array( 'type' => 'boolean' ),
		),
	);
}

/** @return array<string,mixed> */
function wpagent_acf_group_write_properties(): array {
	return array(
		'title' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 200 ),
		'active' => array( 'type' => 'boolean' ),
		'post_types' => array( 'type' => 'array', 'minItems' => 1, 'maxItems' => 20, 'items' => array( 'type' => 'string' ) ),
		'fields' => array( 'type' => 'array', 'maxItems' => WPAGENT_ACF_GROUP_MAX_FIELDS, 'items' => wpagent_acf_group_field_schema( true ) ),
	);
}

/** @return array<int,string> */
function wpagent_acf_group_removed_existing_field_keys( array $existing_fields, array $next_fields ): array {
	$next_keys = array();
	foreach ( $next_fields as $field ) {
		if ( is_array( $field ) && is_string( $field['key'] ?? null ) && '' !== $field['key'] ) {
			$next_keys[ $field['key'] ] = true;
		}
	}
	$removed = array();
	foreach ( $existing_fields as $field ) {
		$key = is_array( $field ) && is_string( $field['key'] ?? null ) ? $field['key'] : '';
		if ( '' !== $key && ! isset( $next_keys[ $key ] ) ) {
			$removed[] = $key;
		}
	}
	return $removed;
}

function wpagent_ability_create_acf_field_group(): array {
	return array(
		'label' => __( 'ACF-Feldgruppe erstellen', 'wp-agent-companion' ),
		'description' => __( 'Erstellt eine persistente ACF-Feldgruppe für registrierte Inhaltstypen und prüft Gruppe sowie Felddefinitionen über die native ACF-API erneut. Fehler entfernen die unvollständige Gruppe.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array(
			'type' => 'object',
			'properties' => wpagent_acf_group_write_properties(),
			'required' => array( 'title', 'post_types', 'fields' ),
		),
		'output_schema' => wpagent_acf_group_write_result_schema(),
		'execute_callback' => 'wpagent_exec_create_acf_field_group',
		'permission_callback' => 'wpagent_perm_manage_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false ) ),
	);
}

function wpagent_ability_update_acf_field_group(): array {
	$properties = wpagent_acf_group_write_properties();
	$properties['group_key'] = array( 'type' => 'string', 'pattern' => '^group_[A-Za-z0-9_-]{1,120}$' );
	$properties['expected_hash'] = array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' );
	return array(
		'label' => __( 'ACF-Feldgruppe aktualisieren', 'wp-agent-companion' ),
		'description' => __( 'Nimmt keine Remote-Änderung an bestehenden ACF-Feldgruppen vor, weil ACF keine atomare Compare-and-Write-Operation gegen parallele Bearbeitungen bereitstellt. Prüfe und speichere die Änderung im WordPress-Backend.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array( 'type' => 'object', 'properties' => $properties, 'required' => array( 'group_key', 'expected_hash' ), 'maxProperties' => 6, 'additionalProperties' => false ),
		'output_schema' => array( 'type' => 'object', 'properties' => array(), 'additionalProperties' => false ),
		'execute_callback' => 'wpagent_exec_update_acf_field_group',
		'permission_callback' => 'wpagent_perm_manage_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => true, 'idempotent' => false, 'manual_only' => true ) ),
	);
}

/** @return WP_Error */
function wpagent_acf_group_manual_update_only(): WP_Error {
	return new WP_Error(
		'wpagent_acf_group_update_manual_write_required',
		__( 'ACF stellt für bestehende Feldgruppen keine nachweisbar atomare Compare-and-Write-Operation bereit. WPAgently ändert diese Feldgruppe deshalb nicht. Speichere die Änderung im WordPress-Backend und lies die Feldgruppe anschließend neu.', 'wp-agent-companion' ),
		array( 'status' => 409, 'manual_only' => true )
	);
}

function wpagent_ability_duplicate_acf_field_group(): array {
	return array(
		'label' => __( 'ACF-Feldgruppe duplizieren', 'wp-agent-companion' ),
		'description' => __( 'Dupliziert eine portable persistente ACF-Feldgruppe mit neuen Gruppen- und Feld-Keys und prüft die Kopie vollständig per Read-back.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array(
			'type' => 'object',
			'properties' => array(
				'group_key' => array( 'type' => 'string', 'pattern' => '^group_[A-Za-z0-9_-]{1,120}$' ),
				'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'title' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 200 ),
			),
			'required' => array( 'group_key', 'expected_hash' ),
		),
		'output_schema' => wpagent_acf_group_write_result_schema(),
		'execute_callback' => 'wpagent_exec_duplicate_acf_field_group',
		'permission_callback' => 'wpagent_perm_manage_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false ) ),
	);
}

function wpagent_ability_delete_acf_field_group(): array {
	return array(
		'label' => __( 'ACF-Feldgruppe löschen', 'wp-agent-companion' ),
		'description' => __( 'Prüft den aktuellen Konflikt-Hash einer persistenten ACF-Feldgruppe. Die endgültige Remote-Löschung ist deaktiviert, weil ACF keine atomare Compare-and-Delete-Prüfung bietet. Entferne die Gruppe manuell in der WordPress-Verwaltung.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array(
			'type' => 'object',
			'properties' => array(
				'group_key' => array( 'type' => 'string', 'pattern' => '^group_[A-Za-z0-9_-]{1,120}$' ),
				'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'confirm' => array( 'type' => 'boolean' ),
			),
			'required' => array( 'group_key', 'expected_hash', 'confirm' ),
		),
		'output_schema' => array(
			'type' => 'object',
			'properties' => array( 'group_key' => array( 'type' => 'string' ), 'deleted' => array( 'type' => 'boolean' ) ),
		),
		'execute_callback' => 'wpagent_exec_delete_acf_field_group',
		'permission_callback' => 'wpagent_perm_manage_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => true, 'idempotent' => false, 'manual_only' => true ) ),
	);
}

/** @return string|WP_Error */
function wpagent_acf_group_validate_title( $value ) {
	$title = is_string( $value ) ? trim( $value ) : '';
	if ( '' === $title || wpagent_acf_group_text_length( $title ) > 200 || sanitize_text_field( $title ) !== $title ) {
		return new WP_Error( 'wpagent_acf_group_title', __( 'Der Feldgruppentitel muss reiner Text mit 1-200 Zeichen sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return $title;
}

/** @return array<int,array<string,mixed>> */
function wpagent_acf_group_write_fields( int $group_id, array $fields, array $existing_fields = array() ): array {
	$existing_by_key = array();
	foreach ( $existing_fields as $existing ) {
		if ( is_array( $existing ) && is_string( $existing['key'] ?? null ) ) {
			$existing_by_key[ $existing['key'] ] = $existing;
		}
	}
	$expected = array();
	foreach ( $fields as $order => $field ) {
		$key = (string) ( $field['key'] ?? '' );
		if ( '' === $key ) {
			$key = wpagent_acf_group_new_key( 'field' );
		}
		$existing = $existing_by_key[ $key ] ?? array();
		$definition = array_merge(
			$field,
			array(
				'ID' => absint( $existing['ID'] ?? 0 ),
				'key' => $key,
				'parent' => $group_id,
				'menu_order' => $order,
				'conditional_logic' => 0,
				'wrapper' => array( 'width' => '', 'class' => '', 'id' => '' ),
			)
		);
		$expected_field = acf_parse_types( wp_unslash( acf_validate_field( $definition ) ) );
		$expected[] = wpagent_acf_group_field_state( $expected_field );
		$saved = acf_update_field( $definition );
		if ( ! is_array( $saved ) || absint( $saved['ID'] ?? 0 ) < 1 ) {
			throw new RuntimeException( 'ACF field save failed.' );
		}
	}
	return $expected;
}

/** @return bool */
function wpagent_acf_group_state_matches( array $state, string $title, bool $active, array $post_types, array $expected_fields ): bool {
	return $title === $state['title']
		&& $active === $state['active']
		&& $post_types === $state['post_types']
		&& wp_json_encode( wpagent_acf_group_canonicalize( $expected_fields ) ) === wp_json_encode( wpagent_acf_group_canonicalize( $state['fields'] ) );
}

/** @return array{group:array<string,mixed>,fields:array<int,array<string,mixed>>,hash:string} */
function wpagent_acf_group_snapshot( array $group ): array {
	$fields = acf_get_fields( $group );
	return array(
		'group' => $group,
		'fields' => is_array( $fields ) ? $fields : array(),
		'hash' => wpagent_acf_group_state( $group )['hash'],
	);
}

function wpagent_acf_group_restore_snapshot( array $snapshot ): bool {
	$group = $snapshot['group'] ?? null;
	if ( ! is_array( $group ) || absint( $group['ID'] ?? 0 ) < 1 ) {
		return false;
	}
	try {
		$current = acf_get_field_group( (string) $group['key'] );
		if ( ! is_array( $current ) ) {
			return false;
		}
		$current_fields = acf_get_fields( $current );
		foreach ( is_array( $current_fields ) ? $current_fields : array() as $field ) {
			if ( ! acf_delete_field( absint( $field['ID'] ?? 0 ) ) ) {
				return false;
			}
		}
		$restored_group = acf_update_field_group( $group );
		if ( ! is_array( $restored_group ) || absint( $restored_group['ID'] ?? 0 ) < 1 ) {
			return false;
		}
		foreach ( $snapshot['fields'] ?? array() as $field ) {
			if ( ! is_array( $field ) ) {
				return false;
			}
			$field['ID'] = 0;
			$field['parent'] = absint( $restored_group['ID'] );
			$restored_field = acf_update_field( $field );
			if ( ! is_array( $restored_field ) || absint( $restored_field['ID'] ?? 0 ) < 1 ) {
				return false;
			}
		}
		$fresh = acf_get_field_group( (string) $group['key'] );
		return is_array( $fresh ) && hash_equals( (string) $snapshot['hash'], wpagent_acf_group_state( $fresh )['hash'] );
	} catch ( Throwable ) {
		return false;
	}
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acf_group_create_internal( string $title, array $post_types, array $fields, bool $active ) {
	$key = wpagent_acf_group_new_key( 'group' );
	$definition = array(
			'key' => $key,
			'title' => $title,
			'active' => $active,
			'location' => wpagent_acf_group_location_for_post_types( $post_types ),
			'menu_order' => 0,
			'position' => 'normal',
			'style' => 'default',
			'label_placement' => 'top',
			'instruction_placement' => 'label',
			'hide_on_screen' => array(),
			'description' => '',
	);
	$expected_group = acf_validate_field_group( $definition );
	try {
		$group = acf_update_field_group( $definition );
		if ( ! is_array( $group ) || absint( $group['ID'] ?? 0 ) < 1 ) {
			throw new RuntimeException( 'save failed' );
		}
		$expected_fields = wpagent_acf_group_write_fields( absint( $group['ID'] ), $fields );
		$fresh = wpagent_acf_group_get_persistent( $key );
		$state = is_wp_error( $fresh ) ? null : wpagent_acf_group_state( $fresh );
		if ( ! is_array( $state ) || ! wpagent_acf_group_definition_matches( $fresh, $expected_group ) || ! wpagent_acf_group_state_matches( $state, $title, $active, $post_types, $expected_fields ) ) {
			throw new RuntimeException( 'ACF field-group read-back failed.' );
		}
		return array( 'group' => $state, 'verified' => true );
	} catch ( Throwable ) {
		$existing = acf_get_field_group( $key );
		$removed = ! is_array( $existing ) || true === acf_delete_field_group( $key );
		$cleanup_failed = ! $removed || false !== acf_get_field_group( $key );
		return new WP_Error(
			'wpagent_acf_group_create_failed',
			__( 'Die ACF-Feldgruppe konnte nicht vollständig und verifiziert gespeichert werden.', 'wp-agent-companion' ),
			array( 'status' => 500, 'cleanup_failed' => $cleanup_failed, 'group_key' => $cleanup_failed ? $key : '' )
		);
	}
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_create_acf_field_group( $input ) {
	$available = wpagent_acf_group_require_api();
	if ( is_wp_error( $available ) ) {
		return $available;
	}
	$input = is_array( $input ) ? $input : array();
	$title = wpagent_acf_group_validate_title( $input['title'] ?? null );
	$post_types = wpagent_acf_group_validate_post_types( $input['post_types'] ?? null );
	$fields = wpagent_acf_group_validate_fields( $input['fields'] ?? null );
	foreach ( array( $title, $post_types, $fields ) as $validated ) {
		if ( is_wp_error( $validated ) ) {
			return $validated;
		}
	}
	return wpagent_acf_group_create_internal( $title, $post_types, $fields, ! array_key_exists( 'active', $input ) || wpagent_acf_group_boolean( $input['active'] ) );
}

/** @return WP_Error */
function wpagent_exec_update_acf_field_group( $input ) {
	// Do not read, lock, or normalize a provider definition here. ACF exposes
	// only unconditional saves, so none of those steps could make a remote
	// update or snapshot restore safe against a concurrent wp-admin editor.
	return wpagent_acf_group_manual_update_only();
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acf_group_update_locked( array $input, array $group, string $expected_hash ) {
	$current = wpagent_acf_group_state( $group );
	if ( ! hash_equals( $current['hash'], $expected_hash ) ) {
		return new WP_Error( 'wpagent_acf_group_conflict', __( 'Die ACF-Feldgruppe wurde seit dem Lesen verändert.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => $current['hash'] ) );
	}
	if ( ! $current['portable'] ) {
		return new WP_Error( 'wpagent_acf_group_not_portable', __( 'Diese Feldgruppe enthält komplexe Location-Regeln, Feldtypen oder bedingte Logik und wird deshalb nicht über den begrenzten Schreibpfad verändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$write_keys = array( 'title', 'active', 'post_types', 'fields' );
	if ( ! array_filter( $write_keys, static fn( string $key ): bool => array_key_exists( $key, $input ) ) ) {
		return new WP_Error( 'wpagent_acf_group_no_fields', __( 'title, active, post_types oder fields ist erforderlich.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$existing_fields = acf_get_fields( $group );
	$existing_fields = is_array( $existing_fields ) ? $existing_fields : array();
	$title = array_key_exists( 'title', $input ) ? wpagent_acf_group_validate_title( $input['title'] ) : $current['title'];
	$post_types = array_key_exists( 'post_types', $input ) ? wpagent_acf_group_validate_post_types( $input['post_types'] ) : $current['post_types'];
	$fields = array_key_exists( 'fields', $input ) ? wpagent_acf_group_validate_fields( $input['fields'], $existing_fields ) : null;
	foreach ( array( $title, $post_types, $fields ) as $validated ) {
		if ( is_wp_error( $validated ) ) {
			return $validated;
		}
	}
	if ( null !== $fields ) {
		$removed_field_keys = wpagent_acf_group_removed_existing_field_keys( $existing_fields, $fields );
		if ( array() !== $removed_field_keys ) {
			return new WP_Error(
				'wpagent_acf_group_field_delete_manual_only',
				__( 'Bestehende ACF-Felddefinitionen werden nicht remote entfernt, weil ACF keine atomare Compare-and-Delete-Prüfung gegen parallele Administratoränderungen anbietet. Entferne Felder manuell in der WordPress-Verwaltung.', 'wp-agent-companion' ),
				array( 'status' => 409, 'field_keys' => $removed_field_keys )
			);
		}
	}
	$active = array_key_exists( 'active', $input ) ? wpagent_acf_group_boolean( $input['active'] ) : $current['active'];
	$snapshot = wpagent_acf_group_snapshot( $group );
	try {
		$updated_group = $group;
		$updated_group['title'] = $title;
		$updated_group['active'] = $active;
		$updated_group['location'] = wpagent_acf_group_location_for_post_types( $post_types );
		$expected_group = acf_validate_field_group( $updated_group );
		$updated_group = acf_update_field_group( $updated_group );
		if ( ! is_array( $updated_group ) || absint( $updated_group['ID'] ?? 0 ) < 1 ) {
			throw new RuntimeException( 'ACF field-group save failed.' );
		}
		$expected_fields = null === $fields ? $current['fields'] : wpagent_acf_group_write_fields( absint( $updated_group['ID'] ), $fields, $existing_fields );
		$fresh = wpagent_acf_group_get_persistent( (string) $group['key'] );
		$state = is_wp_error( $fresh ) ? null : wpagent_acf_group_state( $fresh );
		if ( ! is_array( $state ) || ! wpagent_acf_group_definition_matches( $fresh, $expected_group ) || ! wpagent_acf_group_state_matches( $state, $title, $active, $post_types, $expected_fields ) ) {
			throw new RuntimeException( 'ACF field-group read-back failed.' );
		}
		return array( 'group' => $state, 'verified' => true );
	} catch ( Throwable ) {
		$rolled_back = wpagent_acf_group_restore_snapshot( $snapshot );
		return new WP_Error( 'wpagent_acf_group_update_failed', __( 'Die ACF-Feldgruppe konnte nicht verifiziert aktualisiert werden. Der vorherige Zustand wurde nach Möglichkeit wiederhergestellt.', 'wp-agent-companion' ), array( 'status' => 500, 'rolled_back' => $rolled_back ) );
	}
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_duplicate_acf_field_group( $input ) {
	$available = wpagent_acf_group_require_api();
	if ( is_wp_error( $available ) ) {
		return $available;
	}
	$input = is_array( $input ) ? $input : array();
	$group_key = is_string( $input['group_key'] ?? null ) ? $input['group_key'] : '';
	$expected_hash = is_string( $input['expected_hash'] ?? null ) ? $input['expected_hash'] : '';
	if ( ! preg_match( '/^[a-f0-9]{64}$/', $expected_hash ) ) {
		return new WP_Error( 'wpagent_acf_group_hash', __( 'expected_hash muss ein SHA-256-Hash sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$group = wpagent_acf_group_get_persistent( $group_key );
	if ( is_wp_error( $group ) ) {
		return $group;
	}
	$lock = wpagent_acf_group_acquire_lock( $group_key );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$group = wpagent_acf_group_get_persistent( $group_key );
		return is_wp_error( $group ) ? $group : wpagent_acf_group_duplicate_locked( $input, $group, $expected_hash );
	} finally {
		wpagent_acf_group_release_lock( $group_key, $lock );
	}
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acf_group_duplicate_locked( array $input, array $group, string $expected_hash ) {
	$state = wpagent_acf_group_state( $group );
	if ( ! hash_equals( $state['hash'], $expected_hash ) ) {
		return new WP_Error( 'wpagent_acf_group_conflict', __( 'Die ACF-Feldgruppe wurde seit dem Lesen verändert.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => $state['hash'] ) );
	}
	if ( ! $state['portable'] ) {
		return new WP_Error( 'wpagent_acf_group_not_portable', __( 'Diese Feldgruppe ist nicht verlustfrei über den begrenzten Duplizierungspfad abbildbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$title = array_key_exists( 'title', $input ) ? wpagent_acf_group_validate_title( $input['title'] ) : wpagent_acf_group_validate_title( $state['title'] . ' Kopie' );
	if ( is_wp_error( $title ) ) {
		return $title;
	}
	$fields = $state['fields'];
	foreach ( $fields as &$field ) {
		unset( $field['key'] );
	}
	unset( $field );
	$fields = wpagent_acf_group_validate_fields( $fields );
	return is_wp_error( $fields ) ? $fields : wpagent_acf_group_create_internal( $title, $state['post_types'], $fields, $state['active'] );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_delete_acf_field_group( $input ) {
	$available = wpagent_acf_group_require_api();
	if ( is_wp_error( $available ) ) {
		return $available;
	}
	$input = is_array( $input ) ? $input : array();
	$group_key = is_string( $input['group_key'] ?? null ) ? $input['group_key'] : '';
	$group = wpagent_acf_group_get_persistent( $group_key );
	if ( is_wp_error( $group ) ) {
		return $group;
	}
	$expected_hash = is_string( $input['expected_hash'] ?? null ) ? $input['expected_hash'] : '';
	if ( true !== ( $input['confirm'] ?? false ) ) {
		return new WP_Error( 'wpagent_confirmation_required', __( 'Zum endgültigen Löschen ist confirm=true erforderlich.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$current = wpagent_acf_group_state( $group );
	if ( ! preg_match( '/^[a-f0-9]{64}$/', $expected_hash ) || ! hash_equals( $current['hash'], $expected_hash ) ) {
		return new WP_Error( 'wpagent_acf_group_conflict', __( 'Die ACF-Feldgruppe wurde seit dem Lesen verändert oder der Konflikt-Hash ist ungültig.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => $current['hash'] ) );
	}
	return new WP_Error(
		'wpagent_acf_group_delete_manual_only',
		__( 'ACF-Feldgruppen werden nicht remote endgültig gelöscht, weil ACF keine atomare Compare-and-Delete-Prüfung gegen parallele Administratoränderungen anbietet. Lösche die Feldgruppe manuell in der WordPress-Verwaltung.', 'wp-agent-companion' ),
		array( 'status' => 409, 'group_key' => $group_key, 'current_hash' => $current['hash'], 'manual_only' => true )
	);
}
