<?php
/** Verified writes for persistent ACF post-type and taxonomy definitions. */

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

/** @return array<string,mixed> */
function wpagent_acf_model_write_properties( string $kind, bool $create ): array {
	$properties = array(
		'singular_label' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 200 ),
		'plural_label' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 200 ),
		'description' => array( 'type' => 'string', 'maxLength' => 2000 ),
		'active' => array( 'type' => 'boolean' ), 'public' => array( 'type' => 'boolean' ),
		'hierarchical' => array( 'type' => 'boolean' ), 'show_ui' => array( 'type' => 'boolean' ),
		'show_in_rest' => array( 'type' => 'boolean' ), 'allow_ai_access' => array( 'type' => 'boolean' ),
		'ai_description' => array( 'type' => 'string', 'maxLength' => 500 ),
		'rewrite_slug' => array( 'type' => 'string', 'maxLength' => 200 ),
	);
	if ( $create ) {
		$properties[ 'post_type' === $kind ? 'post_type' : 'taxonomy' ] = array(
			'type' => 'string', 'pattern' => '^[a-z][a-z0-9_-]*$', 'maxLength' => 'post_type' === $kind ? 20 : 32,
		);
	} else {
		$properties['key'] = array( 'type' => 'string', 'pattern' => '^' . ( 'post_type' === $kind ? 'post_type' : 'taxonomy' ) . '_[A-Za-z0-9_-]{1,120}$' );
		$properties['expected_hash'] = array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' );
	}
	if ( 'post_type' === $kind ) {
		$properties['supports'] = array( 'type' => 'array', 'maxItems' => 11, 'items' => array( 'type' => 'string' ) );
		$properties['taxonomies'] = array( 'type' => 'array', 'maxItems' => 20, 'items' => array( 'type' => 'string' ) );
		$properties['has_archive'] = array( 'type' => 'boolean' );
	} else {
		$properties['object_types'] = array( 'type' => 'array', 'minItems' => 1, 'maxItems' => 20, 'items' => array( 'type' => 'string' ) );
		$properties['show_admin_column'] = array( 'type' => 'boolean' );
	}
	return $properties;
}

/** @return array<string,mixed> */
function wpagent_acf_model_write_result_schema( string $kind ): array {
	return array(
		'type' => 'object',
		'properties' => array( 'model' => wpagent_acf_model_output_schema( $kind ), 'verified' => array( 'type' => 'boolean' ) ),
		'required' => array( 'model', 'verified' ),
		'additionalProperties' => false,
	);
}

/** @return array<string,mixed> */
function wpagent_acf_model_create_ability( string $kind ): array {
	$is_post_type = 'post_type' === $kind;
	return array(
		'label' => $is_post_type ? __( 'ACF-Inhaltstyp erstellen', 'wp-agent-companion' ) : __( 'ACF-Taxonomie erstellen', 'wp-agent-companion' ),
		'description' => $is_post_type
			? __( 'Erstellt eine begrenzte persistente ACF-Inhaltstypdefinition über die native ACF-API und prüft Definition sowie WordPress-Registrierung erneut.', 'wp-agent-companion' )
			: __( 'Erstellt eine begrenzte persistente ACF-Taxonomiedefinition über die native ACF-API und prüft Definition sowie WordPress-Registrierung erneut.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array(
			'type' => 'object', 'properties' => wpagent_acf_model_write_properties( $kind, true ),
			'required' => array( $is_post_type ? 'post_type' : 'taxonomy', 'singular_label', 'plural_label', $is_post_type ? 'supports' : 'object_types' ),
			'additionalProperties' => false,
		),
		'output_schema' => wpagent_acf_model_write_result_schema( $kind ),
		'execute_callback' => $is_post_type ? 'wpagent_exec_create_acf_post_type' : 'wpagent_exec_create_acf_taxonomy',
		'permission_callback' => 'wpagent_perm_manage_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false ) ),
	);
}

/** @return array<string,mixed> */
function wpagent_acf_model_update_ability( string $kind ): array {
	$is_post_type = 'post_type' === $kind;
	return array(
		'label' => $is_post_type ? __( 'ACF-Inhaltstyp aktualisieren', 'wp-agent-companion' ) : __( 'ACF-Taxonomie aktualisieren', 'wp-agent-companion' ),
		'description' => $is_post_type
			? __( 'Aktualisiert eine portable ACF-Inhaltstypdefinition konfliktgeschützt. Nicht vollständig abbildbare Definitionen bleiben unverändert, Fehler werden zurückgerollt.', 'wp-agent-companion' )
			: __( 'Aktualisiert eine portable ACF-Taxonomiedefinition konfliktgeschützt. Nicht vollständig abbildbare Definitionen bleiben unverändert, Fehler werden zurückgerollt.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array(
			'type' => 'object', 'properties' => wpagent_acf_model_write_properties( $kind, false ),
			'required' => array( 'key', 'expected_hash' ), 'additionalProperties' => false,
		),
		'output_schema' => wpagent_acf_model_write_result_schema( $kind ),
		'execute_callback' => $is_post_type ? 'wpagent_exec_update_acf_post_type' : 'wpagent_exec_update_acf_taxonomy',
		'permission_callback' => 'wpagent_perm_manage_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => true, 'idempotent' => false ) ),
	);
}

/** @return array<string,mixed> */
function wpagent_acf_model_delete_ability( string $kind ): array {
	$is_post_type = 'post_type' === $kind;
	$prefix = $is_post_type ? 'post_type' : 'taxonomy';
	return array(
		'label' => $is_post_type ? __( 'ACF-Inhaltstyp löschen', 'wp-agent-companion' ) : __( 'ACF-Taxonomie löschen', 'wp-agent-companion' ),
		'description' => $is_post_type
			? __( 'Prüft den aktuellen Konflikt-Hash einer persistenten ACF-Inhaltstypdefinition. Die endgültige Remote-Löschung ist deaktiviert, weil ACF keine atomare Compare-and-Delete-Prüfung bietet. Lösche die Definition manuell in der WordPress-Verwaltung.', 'wp-agent-companion' )
			: __( 'Prüft den aktuellen Konflikt-Hash einer persistenten ACF-Taxonomiedefinition. Die endgültige Remote-Löschung ist deaktiviert, weil ACF keine atomare Compare-and-Delete-Prüfung bietet. Lösche die Definition manuell in der WordPress-Verwaltung.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array(
			'type' => 'object',
			'properties' => array(
				'key' => array( 'type' => 'string', 'pattern' => '^' . $prefix . '_[A-Za-z0-9_-]{1,120}$' ),
				'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'confirm' => array( 'type' => 'boolean' ), 'confirm_content' => array( 'type' => 'boolean' ),
			),
			'required' => array( 'key', 'expected_hash', 'confirm' ), 'additionalProperties' => false,
		),
		'output_schema' => array(
			'type' => 'object',
			'properties' => array( 'key' => array( 'type' => 'string' ), 'slug' => array( 'type' => 'string' ), 'deleted' => array( 'type' => 'boolean' ), 'retained_content' => array( 'type' => 'integer' ) ),
			'required' => array( 'key', 'slug', 'deleted', 'retained_content' ),
			'additionalProperties' => false,
		),
		'execute_callback' => $is_post_type ? 'wpagent_exec_delete_acf_post_type' : 'wpagent_exec_delete_acf_taxonomy',
		'permission_callback' => 'wpagent_perm_manage_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => true, 'idempotent' => false, 'manual_only' => true ) ),
	);
}

function wpagent_ability_create_acf_post_type(): array { return wpagent_acf_model_create_ability( 'post_type' ); }
function wpagent_ability_update_acf_post_type(): array { return wpagent_acf_model_update_ability( 'post_type' ); }
function wpagent_ability_delete_acf_post_type(): array { return wpagent_acf_model_delete_ability( 'post_type' ); }
function wpagent_ability_create_acf_taxonomy(): array { return wpagent_acf_model_create_ability( 'taxonomy' ); }
function wpagent_ability_update_acf_taxonomy(): array { return wpagent_acf_model_update_ability( 'taxonomy' ); }
function wpagent_ability_delete_acf_taxonomy(): array { return wpagent_acf_model_delete_ability( 'taxonomy' ); }

function wpagent_acf_model_slug_exists( string $kind, string $slug ): bool {
	$config = wpagent_acf_model_kinds()[ $kind ];
	if ( ( 'post_type' === $kind && post_type_exists( $slug ) ) || ( 'taxonomy' === $kind && taxonomy_exists( $slug ) ) ) {
		return true;
	}
	$raw = call_user_func( $config['get_raw'] );
	foreach ( is_array( $raw ) ? $raw : array() as $model ) {
		if ( is_array( $model ) && $slug === (string) ( $model[ $config['slug_field'] ] ?? '' ) ) {
			return true;
		}
	}
	return false;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acf_model_validate_state( string $kind, array $input, ?array $current = null ) {
	$create = null === $current;
	$config = wpagent_acf_model_kinds()[ $kind ];
	$state = $create ? array(
		'key' => '', 'slug' => '', 'singular_label' => '', 'plural_label' => '', 'description' => '',
		'active' => true, 'public' => true, 'hierarchical' => false, 'show_ui' => true, 'show_in_rest' => true,
		'allow_ai_access' => false, 'ai_description' => '', 'rewrite_slug' => '',
	) : $current;
	if ( $create ) {
		$slug_field = $config['slug_field'];
		$slug = wpagent_acf_model_slug( $input[ $slug_field ] ?? null, $kind );
		if ( is_wp_error( $slug ) ) {
			return $slug;
		}
		if ( wpagent_acf_model_slug_exists( $kind, $slug ) ) {
			return new WP_Error( 'wpagent_acf_model_exists', __( 'Dieser Inhaltstyp- oder Taxonomie-Slug wird bereits verwendet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$state['slug'] = $slug;
	}
	foreach ( array( 'singular_label' => 200, 'plural_label' => 200, 'description' => 2000, 'ai_description' => 500 ) as $field => $max ) {
		if ( $create || array_key_exists( $field, $input ) ) {
			$text = wpagent_acf_model_text( $input[ $field ] ?? '', $field, $max, in_array( $field, array( 'description', 'ai_description' ), true ) );
			if ( is_wp_error( $text ) ) {
				return $text;
			}
			$state[ $field ] = $text;
		}
	}
	foreach ( array( 'active', 'public', 'hierarchical', 'show_ui', 'show_in_rest', 'allow_ai_access' ) as $field ) {
		if ( array_key_exists( $field, $input ) ) {
			$state[ $field ] = wpagent_acf_group_boolean( $input[ $field ] );
		}
	}
	if ( ! wpagent_acf_model_supports_ai_settings( $kind ) && ( $state['allow_ai_access'] || '' !== $state['ai_description'] ) ) {
		return new WP_Error( 'wpagent_acf_model_ai_unsupported', __( 'Diese ACF-Version unterstützt die KI-Freigabe für Inhaltstypen und Taxonomien noch nicht.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( $create || array_key_exists( 'rewrite_slug', $input ) ) {
		$rewrite = wpagent_acf_model_rewrite_slug( $input['rewrite_slug'] ?? '' );
		if ( is_wp_error( $rewrite ) ) {
			return $rewrite;
		}
		$state['rewrite_slug'] = $rewrite;
	}
	if ( 'post_type' === $kind ) {
		if ( $create || array_key_exists( 'supports', $input ) ) {
			$supports = wpagent_acf_model_supports( $input['supports'] ?? null );
			if ( is_wp_error( $supports ) ) {
				return $supports;
			}
			$state['supports'] = $supports;
		}
		if ( $create || array_key_exists( 'taxonomies', $input ) ) {
			$taxonomies = wpagent_acf_model_taxonomies( $input['taxonomies'] ?? array() );
			if ( is_wp_error( $taxonomies ) ) {
				return $taxonomies;
			}
			$state['taxonomies'] = $taxonomies;
		}
		if ( array_key_exists( 'has_archive', $input ) ) {
			$state['has_archive'] = wpagent_acf_group_boolean( $input['has_archive'] );
		} elseif ( $create ) {
			$state['has_archive'] = false;
		}
	} else {
		if ( $create || array_key_exists( 'object_types', $input ) ) {
			$objects = wpagent_acf_model_object_types( $input['object_types'] ?? null );
			if ( is_wp_error( $objects ) ) {
				return $objects;
			}
			$state['object_types'] = $objects;
		}
		if ( array_key_exists( 'show_admin_column', $input ) ) {
			$state['show_admin_column'] = wpagent_acf_group_boolean( $input['show_admin_column'] );
		} elseif ( $create ) {
			$state['show_admin_column'] = false;
		}
	}
	return $state;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_create_acf_model( string $kind, $input ) {
	$available = wpagent_acf_model_require_api();
	if ( is_wp_error( $available ) ) {
		return $available;
	}
	$input = is_array( $input ) ? $input : array();
	$state = wpagent_acf_model_validate_state( $kind, $input );
	if ( is_wp_error( $state ) ) {
		return $state;
	}
	$lock_key = 'create:' . $state['slug'];
	$lock = wpagent_acf_model_acquire_lock( $kind, $lock_key );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	$config = wpagent_acf_model_kinds()[ $kind ];
	$saved = null;
	try {
		if ( wpagent_acf_model_slug_exists( $kind, $state['slug'] ) ) {
			return new WP_Error( 'wpagent_acf_model_exists', __( 'Dieser Inhaltstyp- oder Taxonomie-Slug wird bereits verwendet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$state['key'] = wpagent_acf_model_new_key( $kind );
		$definition = wpagent_acf_model_portable_definition( $kind, $state );
		$expected = call_user_func( $config['validate'], $definition );
		$saved = call_user_func( $config['update'], $definition );
		if ( ! is_array( $saved ) || absint( $saved['ID'] ?? 0 ) < 1 ) {
			throw new RuntimeException( 'save failed' );
		}
		$fresh = wpagent_acf_model_get_persistent( $kind, $state['key'] );
		$result = is_wp_error( $fresh ) ? null : wpagent_acf_model_state( $kind, $fresh );
		if ( ! is_array( $result ) || ! wpagent_acf_model_definition_matches( $kind, $fresh, $expected ) || ! $result['portable'] || ! wpagent_acf_model_runtime_matches( $kind, $result ) ) {
			throw new RuntimeException( 'read-back failed' );
		}
		return array( 'model' => $result, 'verified' => true );
	} catch ( Throwable $error ) {
		$saved_id = is_array( $saved ) ? absint( $saved['ID'] ?? 0 ) : 0;
		$existing = ! empty( $state['key'] ) ? call_user_func( $config['get'], $state['key'] ) : false;
		$existing_id = is_array( $existing ) ? absint( $existing['ID'] ?? 0 ) : 0;
		$delete_target = $saved_id > 0 ? $saved_id : $existing_id;
		if ( $delete_target > 0 ) {
			call_user_func( $config['delete'], $delete_target );
		}
		$cleanup_failed = ( $delete_target > 0 && null !== get_post( $delete_target ) ) || ( ! empty( $state['key'] ) && false !== call_user_func( $config['get'], $state['key'] ) );
		return new WP_Error(
			'wpagent_acf_model_create_failed',
			__( 'Die ACF-Definition konnte nicht vollständig und verifiziert gespeichert werden.', 'wp-agent-companion' ),
			array( 'status' => 500, 'cleanup_failed' => $cleanup_failed, 'key' => $cleanup_failed ? (string) ( $state['key'] ?? '' ) : '' )
		);
	} finally {
		wpagent_acf_model_release_lock( $kind, $lock_key, $lock );
	}
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_update_acf_model( string $kind, $input ) {
	$available = wpagent_acf_model_require_api();
	if ( is_wp_error( $available ) ) return $available;
	$input = is_array( $input ) ? $input : array();
	$key = is_string( $input['key'] ?? null ) ? $input['key'] : '';
	$hash = is_string( $input['expected_hash'] ?? null ) ? $input['expected_hash'] : '';
	if ( ! preg_match( '/^[a-f0-9]{64}$/', $hash ) ) {
		return new WP_Error( 'wpagent_acf_model_hash', __( 'expected_hash muss ein SHA-256-Hash sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$model = wpagent_acf_model_get_persistent( $kind, $key );
	if ( is_wp_error( $model ) ) return $model;
	$lock = wpagent_acf_model_acquire_lock( $kind, $key );
	if ( is_wp_error( $lock ) ) return $lock;
	try {
		$model = wpagent_acf_model_get_persistent( $kind, $key );
		return is_wp_error( $model ) ? $model : wpagent_acf_model_update_locked( $kind, $input, $model, $hash );
	} finally {
		wpagent_acf_model_release_lock( $kind, $key, $lock );
	}
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acf_model_update_locked( string $kind, array $input, array $model, string $expected_hash ) {
	$current = wpagent_acf_model_state( $kind, $model );
	if ( ! hash_equals( $current['hash'], $expected_hash ) ) {
		return new WP_Error( 'wpagent_acf_model_conflict', __( 'Die ACF-Definition 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_model_not_portable', __( 'Diese ACF-Definition enthält Einstellungen außerhalb des begrenzten verlustfreien Schreibpfads.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$write_fields = array_diff( array_keys( wpagent_acf_model_write_properties( $kind, false ) ), array( 'key', 'expected_hash' ) );
	if ( ! array_filter( $write_fields, static fn( string $field ): bool => array_key_exists( $field, $input ) ) ) {
		return new WP_Error( 'wpagent_acf_model_no_fields', __( 'Mindestens eine veränderbare Definitionseigenschaft ist erforderlich.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$next = wpagent_acf_model_validate_state( $kind, $input, $current );
	if ( is_wp_error( $next ) ) return $next;
	$config = wpagent_acf_model_kinds()[ $kind ];
	$definition = wpagent_acf_model_portable_definition( $kind, $next, absint( $model['ID'] ) );
	$expected = call_user_func( $config['validate'], $definition );
	$snapshot = $model;
	try {
		$saved = call_user_func( $config['update'], $definition );
		if ( ! is_array( $saved ) || absint( $saved['ID'] ?? 0 ) < 1 ) throw new RuntimeException( 'ACF content-model save failed.' );
		$fresh = wpagent_acf_model_get_persistent( $kind, $current['key'] );
		$result = is_wp_error( $fresh ) ? null : wpagent_acf_model_state( $kind, $fresh );
		if ( ! is_array( $result ) || ! wpagent_acf_model_definition_matches( $kind, $fresh, $expected ) || ! $result['portable'] || ! wpagent_acf_model_runtime_matches( $kind, $result ) ) {
			throw new RuntimeException( 'read-back failed' );
		}
		return array( 'model' => $result, 'verified' => true );
	} catch ( Throwable $error ) {
		$restored = call_user_func( $config['update'], $snapshot );
		$fresh = call_user_func( $config['get'], $current['key'] );
		$rolled_back = is_array( $restored ) && is_array( $fresh ) && hash_equals( $current['hash'], wpagent_acf_model_state( $kind, $fresh )['hash'] );
		return new WP_Error( 'wpagent_acf_model_update_failed', __( 'Die ACF-Definition 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_delete_acf_model( string $kind, $input ) {
	$available = wpagent_acf_model_require_api();
	if ( is_wp_error( $available ) ) return $available;
	$input = is_array( $input ) ? $input : array();
	$key = is_string( $input['key'] ?? null ) ? $input['key'] : '';
	if ( true !== ( $input['confirm'] ?? false ) ) {
		return new WP_Error( 'wpagent_confirmation_required', __( 'Zum endgültigen Löschen der ACF-Definition ist confirm=true erforderlich.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$model = wpagent_acf_model_get_persistent( $kind, $key );
	if ( is_wp_error( $model ) ) return $model;
	$current = wpagent_acf_model_state( $kind, $model );
	$hash = is_string( $input['expected_hash'] ?? null ) ? $input['expected_hash'] : '';
	if ( ! preg_match( '/^[a-f0-9]{64}$/', $hash ) || ! hash_equals( $current['hash'], $hash ) ) {
		return new WP_Error( 'wpagent_acf_model_conflict', __( 'Die ACF-Definition 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_model_delete_manual_only',
		__( 'ACF-Inhaltstyp- und Taxonomiedefinitionen werden nicht remote endgültig gelöscht, weil ACF keine atomare Compare-and-Delete-Prüfung gegen parallele WordPress-Registrierungen anbietet. Lösche die Definition manuell in der WordPress-Verwaltung.', 'wp-agent-companion' ),
		array( 'status' => 409, 'key' => $key, 'slug' => $current['slug'], 'current_hash' => $current['hash'], 'retained_content' => $current['content_count'] )
	);
}

function wpagent_exec_create_acf_post_type( $input ) { return wpagent_exec_create_acf_model( 'post_type', $input ); }
function wpagent_exec_update_acf_post_type( $input ) { return wpagent_exec_update_acf_model( 'post_type', $input ); }
function wpagent_exec_delete_acf_post_type( $input ) { return wpagent_exec_delete_acf_model( 'post_type', $input ); }
function wpagent_exec_create_acf_taxonomy( $input ) { return wpagent_exec_create_acf_model( 'taxonomy', $input ); }
function wpagent_exec_update_acf_taxonomy( $input ) { return wpagent_exec_update_acf_model( 'taxonomy', $input ); }
function wpagent_exec_delete_acf_taxonomy( $input ) { return wpagent_exec_delete_acf_model( 'taxonomy', $input ); }
