<?php
/** Conflict-safe update and delete abilities for native Pods models. */

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

/** @return array<string,mixed> */
function wpagent_pods_model_write_properties( bool $create ): array {
	$properties = array(
		'singular_label' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 200 ),
		'label' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 200 ),
		'description' => array( 'type' => 'string', 'maxLength' => 2000 ),
		'public' => array( 'type' => 'boolean' ), 'hierarchical' => array( 'type' => 'boolean' ),
		'show_ui' => array( 'type' => 'boolean' ), 'show_in_rest' => array( 'type' => 'boolean' ),
		'has_archive' => array( 'type' => 'boolean' ), 'show_admin_column' => array( 'type' => 'boolean' ),
		'rewrite_slug' => array( 'type' => 'string', 'maxLength' => 200 ),
		'supports' => array( 'type' => 'array', 'maxItems' => 10, 'items' => array( 'type' => 'string' ) ),
		'taxonomies' => array( 'type' => 'array', 'maxItems' => 20, 'items' => array( 'type' => 'string' ) ),
		'object_types' => array( 'type' => 'array', 'minItems' => 1, 'maxItems' => 20, 'items' => array( 'type' => 'string' ) ),
	);
	if ( $create ) {
		$properties['kind'] = array( 'type' => 'string', 'enum' => array( 'post_type', 'taxonomy' ) );
		$properties['name'] = array( 'type' => 'string', 'pattern' => '^[a-z][a-z0-9_]*$', 'maxLength' => 32 );
	} else {
		$properties['name'] = array( 'type' => 'string', 'pattern' => '^[a-z][a-z0-9_]{0,31}$' );
		$properties['expected_hash'] = array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' );
	}
	return $properties;
}

/** @return array<string,mixed> */
function wpagent_pods_model_write_result_schema(): array {
	return array(
		'type' => 'object',
		'properties' => array(
			'model' => wpagent_pods_model_output_schema(),
			'verified_persistence' => array( 'type' => 'boolean' ),
			'reload_required' => array( 'type' => 'boolean' ),
		),
		'required' => array( 'model', 'verified_persistence', 'reload_required' ),
		'additionalProperties' => false,
	);
}

function wpagent_ability_create_pods_model(): array {
	return array(
		'label'       => __( 'Pods-Modell-Erstellung ist nicht verfügbar', 'wp-agent-companion' ),
		'description' => __( 'Pods 3.3 bietet keine atomare Create-only-API für neue Modell-Definitionen. Der Aufruf wird deshalb ohne Änderung mit einem Konflikt abgelehnt.', 'wp-agent-companion' ),
		'category'    => 'wp-agent',
		'input_schema' => array(
			'type' => 'object', 'properties' => wpagent_pods_model_write_properties( true ),
			'required' => array( 'kind', 'name', 'singular_label', 'label' ), 'additionalProperties' => false,
		),
		'output_schema'       => wpagent_pods_model_write_result_schema(),
		'execute_callback'    => 'wpagent_exec_create_pods_model',
		'permission_callback' => 'wpagent_perm_manage_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false, 'manual_only' => true ) ),
	);
}

function wpagent_ability_update_pods_model(): array {
	return array(
		'label'       => __( 'Pods-Modell aktualisieren', 'wp-agent-companion' ),
		'description' => __( 'Aktualisiert eine eigenständige Pods-Definition nach einer erneuten ID- und Vollhashprüfung unmittelbar vor dem Speichern und verifiziert das Ergebnis per Read-back. Pods bietet kein natives Compare-and-swap: Fremdänderungen nach dieser Prüfung werden als nicht verifizierbar gemeldet und nicht automatisch überschrieben.', 'wp-agent-companion' ),
		'category'    => 'wp-agent',
		'input_schema' => array(
			'type' => 'object', 'properties' => wpagent_pods_model_write_properties( false ),
			'required' => array( 'name', 'expected_hash' ), 'additionalProperties' => false,
		),
		'output_schema'       => wpagent_pods_model_write_result_schema(),
		'execute_callback'    => 'wpagent_exec_update_pods_model',
		'permission_callback' => 'wpagent_perm_manage_options',
		// A provider can persist part of this write before verification fails.
		// Keep it POST-only so the client never retries a potentially partial mutation.
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => true, 'idempotent' => false ) ),
	);
}

function wpagent_ability_delete_pods_model(): array {
	return array(
		'label'       => __( 'Pods-Modell löschen', 'wp-agent-companion' ),
		'description' => __( 'Prüft die aktuelle Pods-Modell-ID und den Vollhash. Die endgültige Remote-Löschung ist deaktiviert, weil Pods keine atomare Compare-and-Delete-Prüfung gegen parallele Provideränderungen bietet. Lösche das Modell manuell in der WordPress-Verwaltung.', 'wp-agent-companion' ),
		'category'    => 'wp-agent',
		'input_schema' => array(
			'type' => 'object',
			'properties' => array(
				'name' => array( 'type' => 'string', 'pattern' => '^[a-z][a-z0-9_]{0,63}$' ),
				'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'confirm' => array( 'type' => 'boolean' ), 'confirm_content' => array( 'type' => 'boolean' ),
				'confirm_relation_data_loss' => array( 'type' => 'boolean' ),
			),
			'required' => array( 'name', 'expected_hash', 'confirm' ), 'additionalProperties' => false,
		),
		'output_schema' => array(
			'type' => 'object',
			'properties' => array(
				'name' => array( 'type' => 'string' ), 'kind' => array( 'type' => 'string' ), 'deleted' => array( 'type' => 'boolean' ),
				'retained_content' => array( 'type' => 'integer' ), 'retained_field_values' => array( 'type' => 'integer' ),
				'removed_relationships' => array( 'type' => 'integer' ),
			),
			'required' => array( 'name', 'kind', 'deleted', 'retained_content', 'retained_field_values', 'removed_relationships' ),
			'additionalProperties' => false,
		),
		'execute_callback'    => 'wpagent_exec_delete_pods_model',
		'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_pods_validate_model_connections( $value, string $kind ) {
	if ( ! is_array( $value ) || ! array_is_list( $value ) || count( $value ) > 20 || ( 'post_type' === $kind && array() === $value ) ) {
		return new WP_Error( 'wpagent_pods_connections', __( 'Die Verknüpfungen müssen eine begrenzte Liste registrierter WordPress-Objekte sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$result = array();
	foreach ( $value as $name ) {
		if ( ! is_string( $name ) || sanitize_key( $name ) !== $name || ( 'post_type' === $kind ? ! post_type_exists( $name ) : ! taxonomy_exists( $name ) ) ) {
			return new WP_Error( 'wpagent_pods_connection', __( 'Eine Modellverknüpfung ist ungültig oder nicht registriert.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$result[] = $name;
	}
	$result = array_values( array_unique( $result ) );
	sort( $result, SORT_STRING );
	return $result;
}

/** @return string[]|WP_Error */
function wpagent_pods_validate_supports( $value ) {
	$allowed = wpagent_pods_supported_post_supports();
	if ( ! is_array( $value ) || ! array_is_list( $value ) || count( $value ) > count( $allowed ) ) {
		return new WP_Error( 'wpagent_pods_supports', __( 'supports muss eine begrenzte Liste unterstützter WordPress-Funktionen sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$result = array();
	foreach ( $value as $support ) {
		if ( ! is_string( $support ) || ! in_array( $support, $allowed, true ) ) {
			return new WP_Error( 'wpagent_pods_support', __( 'supports enthält eine nicht freigegebene WordPress-Funktion.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$result[] = $support;
	}
	$result = array_values( array_unique( $result ) );
	sort( $result, SORT_STRING );
	return $result;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_pods_validate_model_input( array $input, ?array $current = null ) {
	$create = null === $current;
	$kind = $create ? ( is_string( $input['kind'] ?? null ) ? $input['kind'] : '' ) : (string) $current['kind'];
	if ( ! in_array( $kind, array( 'post_type', 'taxonomy' ), true ) ) {
		return new WP_Error( 'wpagent_pods_kind', __( 'kind muss post_type oder taxonomy sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$name = $create ? wpagent_pods_name( $input['name'] ?? null, $kind ) : (string) $current['name'];
	if ( is_wp_error( $name ) ) {
		return $name;
	}
	$state = $create ? array(
		'name' => $name, 'kind' => $kind, 'singular_label' => '', 'label' => '', 'description' => '',
		'public' => true, 'hierarchical' => false, 'show_ui' => true, 'show_in_rest' => true,
		'has_archive' => false, 'show_admin_column' => false, 'rewrite_slug' => '',
		'supports' => array(), 'taxonomies' => array(), 'object_types' => array(),
	) : $current;
	foreach ( array( 'singular_label' => 200, 'label' => 200, 'description' => 2000 ) as $field => $max ) {
		if ( $create || array_key_exists( $field, $input ) ) {
			$text = wpagent_pods_text( $input[ $field ] ?? '', $field, $max, 'description' === $field );
			if ( is_wp_error( $text ) ) {
				return $text;
			}
			$state[ $field ] = $text;
		}
	}
	foreach ( array( 'public', 'hierarchical', 'show_ui', 'show_in_rest' ) as $field ) {
		if ( array_key_exists( $field, $input ) ) {
			$value = wpagent_pods_boolean( $input[ $field ], $field );
			if ( is_wp_error( $value ) ) {
				return $value;
			}
			$state[ $field ] = $value;
		}
	}
	if ( $create || array_key_exists( 'rewrite_slug', $input ) ) {
		$rewrite = wpagent_pods_rewrite_slug( $input['rewrite_slug'] ?? '' );
		if ( is_wp_error( $rewrite ) ) {
			return $rewrite;
		}
		$state['rewrite_slug'] = $rewrite;
	}
	if ( 'post_type' === $kind ) {
		if ( array_key_exists( 'show_admin_column', $input ) || array_key_exists( 'object_types', $input ) ) {
			return new WP_Error( 'wpagent_pods_kind_fields', __( 'show_admin_column und object_types gelten nur für Taxonomien.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		if ( $create || array_key_exists( 'supports', $input ) ) {
			$supports = wpagent_pods_validate_supports( $input['supports'] ?? array() );
			if ( is_wp_error( $supports ) ) return $supports;
			$state['supports'] = $supports;
		}
		if ( $create || array_key_exists( 'taxonomies', $input ) ) {
			$connections = wpagent_pods_validate_model_connections( $input['taxonomies'] ?? array(), 'taxonomy' );
			if ( is_wp_error( $connections ) ) return $connections;
			$state['taxonomies'] = $connections;
		}
		if ( array_key_exists( 'has_archive', $input ) ) {
			$value = wpagent_pods_boolean( $input['has_archive'], 'has_archive' );
			if ( is_wp_error( $value ) ) return $value;
			$state['has_archive'] = $value;
		}
	} else {
		if ( array_key_exists( 'has_archive', $input ) || array_key_exists( 'supports', $input ) || array_key_exists( 'taxonomies', $input ) ) {
			return new WP_Error( 'wpagent_pods_kind_fields', __( 'has_archive, supports und taxonomies gelten nur für Inhaltstypen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		if ( $create || array_key_exists( 'object_types', $input ) ) {
			$connections = wpagent_pods_validate_model_connections( $input['object_types'] ?? array(), 'post_type' );
			if ( is_wp_error( $connections ) ) return $connections;
			$state['object_types'] = $connections;
		}
		if ( array_key_exists( 'show_admin_column', $input ) ) {
			$value = wpagent_pods_boolean( $input['show_admin_column'], 'show_admin_column' );
			if ( is_wp_error( $value ) ) return $value;
			$state['show_admin_column'] = $value;
		}
	}
	return $state;
}

/** @return array<string,mixed> */
function wpagent_pods_model_api_changes( array $state, array $existing_args = array() ): array {
	$changes = array(
		'name' => $state['name'], 'label' => $state['label'], 'label_singular' => $state['singular_label'],
		'description' => $state['description'], 'type' => $state['kind'], 'storage' => 'meta',
		'public' => $state['public'] ? 1 : 0, 'hierarchical' => $state['hierarchical'] ? 1 : 0,
		'show_ui' => $state['show_ui'] ? 1 : 0, 'rest_enable' => $state['show_in_rest'] ? 1 : 0,
		'rewrite_custom_slug' => $state['rewrite_slug'],
	);
	if ( 'post_type' === $state['kind'] ) {
		$changes['has_archive'] = $state['has_archive'] ? 1 : 0;
		foreach ( wpagent_pods_supported_post_supports() as $support ) {
			$changes[ 'supports_' . str_replace( '-', '_', $support ) ] = in_array( $support, $state['supports'], true ) ? 1 : 0;
		}
		foreach ( $existing_args as $key => $value ) {
			if ( str_starts_with( $key, 'built_in_taxonomies_' ) ) $changes[ $key ] = 0;
		}
		foreach ( $state['taxonomies'] as $taxonomy ) $changes[ 'built_in_taxonomies_' . $taxonomy ] = 1;
	} else {
		$changes['show_admin_column'] = $state['show_admin_column'] ? 1 : 0;
		foreach ( $existing_args as $key => $value ) {
			if ( str_starts_with( $key, 'built_in_post_types_' ) ) $changes[ $key ] = 0;
		}
		foreach ( $state['object_types'] as $post_type ) $changes[ 'built_in_post_types_' . $post_type ] = 1;
	}
	return $changes;
}

function wpagent_pods_model_state_matches( array $actual, array $expected ): bool {
	foreach ( array( 'name', 'kind', 'singular_label', 'label', 'description', 'public', 'hierarchical', 'show_ui', 'show_in_rest', 'has_archive', 'show_admin_column', 'rewrite_slug', 'supports', 'taxonomies', 'object_types' ) as $key ) {
		if ( $actual[ $key ] !== $expected[ $key ] ) return false;
	}
	return true;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_create_pods_model( $input ) {
	$available = wpagent_pods_require_api();
	if ( is_wp_error( $available ) ) return $available;
	$permission = wpagent_pods_require_write_permission();
	if ( is_wp_error( $permission ) ) return $permission;
	return wpagent_pods_provider_atomicity_unavailable( 'model' );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_update_pods_model( $input ) {
	$available = wpagent_pods_require_api();
	if ( is_wp_error( $available ) ) return $available;
	$permission = wpagent_pods_require_write_permission();
	if ( is_wp_error( $permission ) ) return $permission;
	$input = is_array( $input ) ? $input : array();
	$name  = $input['name'] ?? null;
	$lock  = wpagent_pods_acquire_lock( is_string( $name ) ? $name : '' );
	if ( is_wp_error( $lock ) ) return $lock;
	try {
		$pod = wpagent_pods_get_model( $name );
		if ( is_wp_error( $pod ) ) return $pod;
		$current = wpagent_pods_model_state( $pod );
		$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_pods_conflict', __( 'Das Pods-Modell wurde seit dem Lesen verändert oder der Konflikt-Hash ist ungültig.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => $current['hash'] ) );
		}
		if ( $current['registration_conflict'] ) {
			return new WP_Error( 'wpagent_pods_registration_conflict', __( 'Das Pods-Modell kollidiert mit einer fremden WordPress-Registrierung und wird nicht automatisch verändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$writable = array_diff( array_keys( wpagent_pods_model_write_properties( false ) ), array( 'name', 'expected_hash' ) );
		if ( ! array_filter( $writable, static fn( string $field ): bool => array_key_exists( $field, $input ) ) ) {
			return new WP_Error( 'wpagent_pods_no_changes', __( 'Mindestens eine veränderbare Modelloption ist erforderlich.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$next = wpagent_pods_validate_model_input( $input, $current );
		if ( is_wp_error( $next ) ) return $next;
		$target = wpagent_pods_revalidate_model_target( (int) $pod->get_id(), $current['name'], $hash );
		if ( is_wp_error( $target ) ) return $target;
		$pod = $target['pod'];
		$current = $target['state'];
		if ( $current['registration_conflict'] ) {
			return new WP_Error( 'wpagent_pods_registration_conflict', __( 'Das Pods-Modell kollidiert mit einer fremden WordPress-Registrierung und wird nicht automatisch verändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$snapshot = $pod->get_args();
		$changes = wpagent_pods_model_api_changes( $next, $snapshot );
		$changes['id'] = (int) $pod->get_id();
		$permission = wpagent_pods_require_write_permission();
		if ( is_wp_error( $permission ) ) return $permission;
		try {
			$saved = pods_api()->save_pod( $changes );
			if ( is_wp_error( $saved ) || ! $saved ) throw new RuntimeException( 'Pods model save failed.' );
			$fresh = wpagent_pods_get_model_by_identity( (int) $pod->get_id(), $current['name'] );
			$result = is_wp_error( $fresh ) ? null : wpagent_pods_model_state( $fresh );
			if ( ! is_array( $result ) || ! wpagent_pods_model_state_matches( $result, $next ) ) throw new RuntimeException( 'Pods model read-back failed.' );
			return array( 'model' => $result, 'verified_persistence' => true, 'reload_required' => true );
		} catch ( Throwable $error ) {
			unset( $error );
			return new WP_Error( 'wpagent_pods_update_unverified', __( 'Das Pods-Modell konnte nicht verifiziert aktualisiert werden. Eine automatische Rücksetzung unterbleibt, weil Pods keine atomare Konfliktprüfung für eine sichere Wiederherstellung bereitstellt.', 'wp-agent-companion' ), array( 'status' => 500, 'recovery_required' => true ) );
		}
	} finally {
		wpagent_pods_release_lock( is_string( $name ) ? $name : '', $lock );
	}
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_delete_pods_model( $input ) {
	$available = wpagent_pods_require_api();
	if ( is_wp_error( $available ) ) return $available;
	$permission = wpagent_pods_require_write_permission();
	if ( is_wp_error( $permission ) ) return $permission;
	$input = is_array( $input ) ? $input : array();
	if ( true !== ( $input['confirm'] ?? false ) ) {
		return new WP_Error( 'wpagent_confirmation_required', __( 'Zum Löschen des Pods-Modells ist confirm=true erforderlich.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$name = $input['name'] ?? null;
	$pod = wpagent_pods_get_model( $name );
	if ( is_wp_error( $pod ) ) return $pod;
	$current = wpagent_pods_model_state( $pod );
	$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_pods_conflict', __( 'Das Pods-Modell 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_pods_model_delete_manual_only',
		__( 'Pods-Modelldefinitionen werden nicht remote endgültig gelöscht, weil Pods keine atomare Compare-and-Delete-Prüfung gegen parallele Provider- und WordPress-Registrierungsänderungen anbietet. Lösche das Modell manuell in der WordPress-Verwaltung.', 'wp-agent-companion' ),
		array( 'status' => 409, 'name' => $current['name'], 'kind' => $current['kind'], 'current_hash' => $current['hash'], 'retained_content' => $current['content_count'], 'relationship_count' => $current['relationship_count'] )
	);
}
