<?php
/** Native, bounded record helpers for Pods Advanced Content Types. */

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

const WPAGENT_PODS_ACT_MAX_FIELDS   = 20;
const WPAGENT_PODS_ACT_MAX_PAGE     = 1000;
const WPAGENT_PODS_ACT_MAX_PER_PAGE = 50;
const WPAGENT_PODS_ACT_MAX_TEXT     = 255;
const WPAGENT_PODS_ACT_LOCK_TTL     = 300;

/** @return true|WP_Error */
function wpagent_pods_act_require_api() {
	$available = wpagent_pods_require_api();
	if ( is_wp_error( $available ) ) {
		return $available;
	}
	if ( ! function_exists( 'pods' ) || ! defined( 'PODS_VERSION' ) || version_compare( PODS_VERSION, '3.3.9', '<' ) || version_compare( PODS_VERSION, '4.0.0', '>=' ) ) {
		return new WP_Error( 'wpagent_pods_act_provider_version', __( 'Für tabellenbasierte Pods Advanced Content Types wird Pods 3.3.9 oder neuer innerhalb der Hauptversion 3 benötigt.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_pods_act_require_writable_database() {
	global $wpdb;
	$server = strtolower( (string) $wpdb->db_server_info() );
	$is_sqlite = ( defined( 'DB_ENGINE' ) && 'sqlite' === strtolower( (string) DB_ENGINE ) )
		|| str_contains( strtolower( get_class( $wpdb ) ), 'sqlite' )
		|| str_contains( $server, 'sqlite' );
	if ( $is_sqlite ) {
		return new WP_Error(
			'wpagent_pods_act_sqlite_write_unsupported',
			__( 'Pods 3.3.9 bestätigt neue Advanced-Content-Type-Datensätze auf SQLite nicht mit einer positiven Datensatz-ID. WPagently lässt deshalb dort nur sichere ACT-Lesevorgänge zu.', 'wp-agent-companion' ),
			array( 'status' => 409 )
		);
	}
	return true;
}

function wpagent_pods_act_empty_setting( $value ): bool {
	return null === $value || '' === $value || false === $value || 0 === $value || '0' === $value || array() === $value;
}

/** @return int|WP_Error */
function wpagent_pods_act_record_id( $value ) {
	if ( ( ! is_int( $value ) && ! ( is_string( $value ) && ctype_digit( $value ) ) ) || 0 >= (int) $value ) {
		return new WP_Error( 'wpagent_pods_act_record_id', __( 'record_id muss eine positive Ganzzahl sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return (int) $value;
}

/** @return int|WP_Error */
function wpagent_pods_act_page_value( $value, string $name, int $default, int $maximum ) {
	if ( null === $value ) {
		return $default;
	}
	if ( ( ! is_int( $value ) && ! ( is_string( $value ) && ctype_digit( $value ) ) ) || 1 > (int) $value || $maximum < (int) $value ) {
		/* translators: 1: input field name, 2: maximum value. */
		return new WP_Error( 'wpagent_pods_act_pagination', sprintf( __( '%1$s muss eine positive Ganzzahl bis %2$d sein.', 'wp-agent-companion' ), $name, $maximum ), array( 'status' => 400 ) );
	}
	return (int) $value;
}

/** @return string|WP_Error */
function wpagent_pods_act_expected_hash( $value, string $name ) {
	if ( ! is_string( $value ) || ! preg_match( '/^[a-f0-9]{64}$/', $value ) ) {
		/* translators: %s: input field name. */
		return new WP_Error( 'wpagent_pods_act_hash', sprintf( __( '%s muss ein SHA-256-Hash sein.', 'wp-agent-companion' ), $name ), array( 'status' => 400 ) );
	}
	return $value;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_pods_act_field_state( object $field ): array|WP_Error {
	$args     = $field->get_args();
	$name     = (string) ( $args['name'] ?? '' );
	$type     = (string) ( $args['type'] ?? '' );
	$multiple = ! empty( $args['repeatable'] );
	$valid_name = wpagent_pods_name( $name );
	if ( is_wp_error( $valid_name ) ) {
		return new WP_Error( 'wpagent_pods_act_schema_unsupported', __( 'Der Advanced Content Type besitzt einen nicht unterstützten Feldbezeichner.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$system   = array(
		'name'      => 'text',
		'created'   => 'datetime',
		'modified'  => 'datetime',
		'author'    => 'pick',
		'permalink' => 'slug',
	);
	if ( isset( $system[ $name ] ) ) {
		if ( $system[ $name ] !== $type || $multiple ) {
			return new WP_Error( 'wpagent_pods_act_schema_unsupported', __( 'Der Advanced Content Type besitzt ein verändertes Systemfeld und wird deshalb nicht verwaltet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		if ( 'author' === $name && ( 'user' !== (string) ( $args['pick_object'] ?? '' ) || 'single' !== (string) ( $args['pick_format_type'] ?? 'single' ) ) ) {
			return new WP_Error( 'wpagent_pods_act_schema_unsupported', __( 'Der Advanced Content Type besitzt ein nicht unterstütztes Autor-Systemfeld.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		if ( 'name' === $name && empty( $args['required'] ) ) {
			return new WP_Error( 'wpagent_pods_act_schema_unsupported', __( 'Der Advanced Content Type benötigt ein verpflichtendes Name-Systemfeld.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		return array( 'system' => true, 'name' => $name, 'args' => $args );
	}
	if ( '' === $name || 'text' !== $type || $multiple ) {
		return new WP_Error( 'wpagent_pods_act_schema_unsupported', __( 'Es werden nur nicht wiederholbare Textfelder in standardmäßigen Advanced Content Types verwaltet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	foreach ( array( 'default_value', 'default_evaluate_tags', 'default_value_parameter', 'default_empty_fields', 'text_allow_html', 'text_allow_shortcode', 'hidden', 'read_only', 'restrict_role', 'restrict_capability', 'restrict_status' ) as $setting ) {
		if ( ! wpagent_pods_act_empty_setting( $args[ $setting ] ?? null ) ) {
			return new WP_Error( 'wpagent_pods_act_schema_unsupported', __( 'Das Advanced-Content-Type-Feld verwendet eine nicht sicher überprüfbare Sonderkonfiguration.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
	}
	$raw_max_length = $args['text_max_length'] ?? WPAGENT_PODS_ACT_MAX_TEXT;
	if ( ( ! is_int( $raw_max_length ) && ! ( is_string( $raw_max_length ) && ctype_digit( $raw_max_length ) ) ) || 1 > (int) $raw_max_length || WPAGENT_PODS_ACT_MAX_TEXT < (int) $raw_max_length ) {
		return new WP_Error( 'wpagent_pods_act_schema_unsupported', __( 'Das Advanced-Content-Type-Feld überschreitet die maximal sicher verwaltete Textlänge.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$max_length = (int) $raw_max_length;
	return array(
		'system' => false,
		'name'   => $name,
		'args'   => $args,
		'output' => array(
			'name' => $name, 'type' => 'text', 'required' => ! empty( $args['required'] ), 'max_length' => $max_length,
		),
	);
}

/**
 * Loads a fresh, narrowly supported ACT through Pods' public APIs only.
 *
 * @return array<string,mixed>|WP_Error
 */
function wpagent_pods_act_context( $model_name, ?int $expected_blog_id = null ) {
	$available = wpagent_pods_act_require_api();
	if ( is_wp_error( $available ) ) {
		return $available;
	}
	$name = wpagent_pods_name( $model_name );
	if ( is_wp_error( $name ) ) {
		return $name;
	}
	$blog_id = get_current_blog_id();
	if ( null !== $expected_blog_id && $expected_blog_id !== $blog_id ) {
		return new WP_Error( 'wpagent_pods_act_subsite_changed', __( 'Die aktive Website hat sich während der Advanced-Content-Type-Operation geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	try {
		$config = pods_api()->load_pod( array( 'name' => $name, 'bypass_cache' => true ) );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_pods_act_read_failed', __( 'Der Advanced Content Type konnte nicht gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( ! is_object( $config ) || ! is_callable( array( $config, 'get_args' ) ) ) {
		return new WP_Error( 'wpagent_pods_act_not_found', __( 'Advanced Content Type nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}
	$args = $config->get_args();
	if ( 'pod' !== (string) ( $args['type'] ?? '' ) || 'table' !== (string) ( $args['storage'] ?? '' ) || '' !== (string) ( $args['object'] ?? '' ) ) {
		return new WP_Error( 'wpagent_pods_act_unsupported', __( 'Nur eigenständige, tabellenbasierte Pods Advanced Content Types werden verwaltet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	foreach ( array( 'table_custom', 'pod_table_custom', 'field_id_custom', 'field_index_custom', 'field_slug_custom', 'field_type_custom', 'field_parent_custom', 'field_parent_select_custom', 'meta_table_custom', 'meta_field_id_custom', 'meta_field_index_custom', 'meta_field_value_custom', 'join_custom', 'where_custom', 'where_default_custom', 'orderby_custom' ) as $setting ) {
		if ( ! wpagent_pods_act_empty_setting( $args[ $setting ] ?? null ) ) {
			return new WP_Error( 'wpagent_pods_act_unsupported', __( 'Advanced Content Types mit abweichender Tabellenkonfiguration werden nicht verwaltet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
	}
	try {
		$native = pods( $name, null, true );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_pods_act_provider_unavailable', __( 'Die native Pods-Record-API ist nicht verfügbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	foreach ( array( 'find', 'fetch', 'raw', 'add', 'save', 'delete', 'total_found' ) as $method ) {
		if ( ! is_object( $native ) || ! is_callable( array( $native, $method ) ) ) {
			return new WP_Error( 'wpagent_pods_act_provider_unavailable', __( 'Die native Pods-Record-API ist nicht vollständig verfügbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
	}
	try {
		$config_fields = wpagent_pods_model_fields( $config );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_pods_act_schema_failed', __( 'Die Advanced-Content-Type-Felder konnten nicht sicher gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( count( $config_fields ) > WPAGENT_PODS_ACT_MAX_FIELDS + 4 ) {
		return new WP_Error( 'wpagent_pods_act_schema_unsupported', __( 'Der Advanced Content Type besitzt zu viele Felder für eine begrenzte Verwaltung.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$system_fields   = array();
	$writable_fields = array();
	$field_material  = array();
	foreach ( $config_fields as $field ) {
		$state = wpagent_pods_act_field_state( $field );
		if ( is_wp_error( $state ) ) {
			return $state;
		}
		if ( isset( $field_material[ $state['name'] ] ) ) {
			return new WP_Error( 'wpagent_pods_act_schema_unsupported', __( 'Der Advanced Content Type enthält doppelte Feldbezeichner und wird deshalb nicht verwaltet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		if ( $state['system'] ) {
			$system_fields[ $state['name'] ] = true;
		} else {
			$writable_fields[ $state['name'] ] = $state['output'];
		}
		$field_material[ $state['name'] ] = $state['args'];
	}
	foreach ( array( 'name', 'created', 'modified', 'author', 'permalink' ) as $required_system_field ) {
		if ( empty( $system_fields[ $required_system_field ] ) ) {
			return new WP_Error( 'wpagent_pods_act_schema_unsupported', __( 'Der Advanced Content Type besitzt nicht alle standardmäßigen Systemfelder.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
	}
	/* The standard name field is the sole writable ACT system field. */
	$writable_fields['name'] = array( 'name' => 'name', 'type' => 'text', 'required' => true, 'max_length' => WPAGENT_PODS_ACT_MAX_TEXT );
	ksort( $writable_fields, SORT_STRING );
	ksort( $field_material, SORT_STRING );
	if ( count( $writable_fields ) > WPAGENT_PODS_ACT_MAX_FIELDS ) {
		return new WP_Error( 'wpagent_pods_act_schema_unsupported', __( 'Der Advanced Content Type besitzt zu viele verwaltbare Felder.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$fields = array_values( $writable_fields );
	return array(
		'name'        => $name,
		'blog_id'     => $blog_id,
		'config'      => $config,
		'native'      => $native,
		'fields'      => $fields,
		'field_map'   => $writable_fields,
		'schema_hash' => wpagent_pods_hash( array( 'provider_version' => PODS_VERSION, 'blog_id' => $blog_id, 'model' => $args, 'fields' => $field_material ) ),
	);
}

/** @return array<string,mixed>|WP_Error */
function wpagent_pods_act_current_record( array $context ): array|WP_Error {
	$id = wpagent_pods_act_record_id( $context['native']->raw( 'id' ) );
	if ( is_wp_error( $id ) ) {
		return new WP_Error( 'wpagent_pods_act_read_failed', __( 'Pods hat keinen gültigen Advanced-Content-Type-Datensatz geliefert.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$values = array();
	foreach ( $context['fields'] as $field ) {
		$value = $context['native']->raw( $field['name'] );
		if ( false === $value || null === $value ) {
			$value = '';
		}
		if ( ! is_string( $value ) ) {
			return new WP_Error( 'wpagent_pods_act_read_failed', __( 'Pods hat einen nicht unterstützten Advanced-Content-Type-Feldwert geliefert.', 'wp-agent-companion' ), array( 'status' => 500 ) );
		}
		$values[ $field['name'] ] = $value;
	}
	return array(
		'id'     => $id,
		'values' => $values,
		'hash'   => wpagent_pods_hash( array( 'blog_id' => $context['blog_id'], 'schema_hash' => $context['schema_hash'], 'record_id' => $id, 'values' => $values ) ),
	);
}

/** @return array<string,mixed>|WP_Error */
function wpagent_pods_act_get_record( array $context, int $record_id ): array|WP_Error {
	try {
		$row = $context['native']->fetch( $record_id, false );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_pods_act_read_failed', __( 'Der Advanced-Content-Type-Datensatz konnte nicht gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( ! is_array( $row ) || array() === $row ) {
		return new WP_Error( 'wpagent_pods_act_record_not_found', __( 'Advanced-Content-Type-Datensatz nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}
	return wpagent_pods_act_current_record( $context );
}

/** @return array<string,string>|WP_Error */
function wpagent_pods_act_validate_values( $value, array $field_map, bool $create ): array|WP_Error {
	if ( ! is_array( $value ) || array_is_list( $value ) || array() === $value || count( $value ) > WPAGENT_PODS_ACT_MAX_FIELDS ) {
		return new WP_Error( 'wpagent_pods_act_values', __( 'values muss ein begrenztes Objekt mit Advanced-Content-Type-Textfeldern sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$values = array();
	foreach ( $value as $name => $raw_value ) {
		if ( ! is_string( $name ) || ! isset( $field_map[ $name ] ) ) {
			return new WP_Error( 'wpagent_pods_act_field', __( 'values enthält ein nicht freigegebenes Advanced-Content-Type-Feld.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$text = wpagent_pods_text( $raw_value, $name, (int) $field_map[ $name ]['max_length'], empty( $field_map[ $name ]['required'] ) );
		if ( is_wp_error( $text ) ) {
			return $text;
		}
		$values[ $name ] = $text;
	}
	if ( $create ) {
		foreach ( $field_map as $name => $field ) {
			if ( ! empty( $field['required'] ) && ! array_key_exists( $name, $values ) ) {
				/* translators: %s: field name. */
				return new WP_Error( 'wpagent_pods_act_field_required', sprintf( __( 'Das verpflichtende Advanced-Content-Type-Feld %s fehlt.', 'wp-agent-companion' ), $name ), array( 'status' => 400 ) );
			}
		}
	}
	return $values;
}

function wpagent_pods_act_lock_name( int $blog_id, string $model_name ): string {
	return 'act:' . $blog_id . ':' . $model_name;
}

function wpagent_pods_act_lock_option_name( string $lock_name ): string {
	return 'wp_agent_pods_model_lock_' . hash( 'sha256', $lock_name );
}

function wpagent_pods_act_lock_owned( string $lock_name, string $token ): bool {
	return wpagent_companion_option_lock_is_owned(
		wpagent_pods_act_lock_option_name( $lock_name ),
		$token
	);
}

function wpagent_pods_act_refresh_lock( string $lock_name, string $token ): bool {
	return wpagent_companion_refresh_option_lock(
		wpagent_pods_act_lock_option_name( $lock_name ),
		$token,
		WPAGENT_PODS_ACT_LOCK_TTL
	);
}

/** @return true|WP_Error */
function wpagent_pods_act_prepare_native_write( string $lock_name, string $token ): bool|WP_Error {
	if ( wpagent_pods_act_refresh_lock( $lock_name, $token ) ) {
		return true;
	}
	return new WP_Error( 'wpagent_pods_act_busy', __( 'Die Advanced-Content-Type-Sperre ging verloren. Der Vorgang wurde nicht gestartet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_pods_act_locked_context( string $model_name, int $blog_id, string $expected_schema_hash, string $lock_name, string $token ): array|WP_Error {
	if ( ! wpagent_pods_act_refresh_lock( $lock_name, $token ) ) {
		return new WP_Error( 'wpagent_pods_act_busy', __( 'Die Advanced-Content-Type-Sperre ging verloren. Der Vorgang wurde nicht gestartet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( ! current_user_can( 'manage_options' ) ) {
		return new WP_Error( 'wpagent_pods_act_forbidden', __( 'Für Advanced-Content-Type-Datensätze sind Administratorrechte erforderlich.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	$context = wpagent_pods_act_context( $model_name, $blog_id );
	if ( is_wp_error( $context ) ) {
		return $context;
	}
	if ( ! hash_equals( $context['schema_hash'], $expected_schema_hash ) ) {
		return new WP_Error( 'wpagent_pods_act_schema_conflict', __( 'Die Advanced-Content-Type-Struktur wurde seit dem Lesen verändert oder der Schema-Hash ist ungültig.', 'wp-agent-companion' ), array( 'status' => 409, 'current_schema_hash' => $context['schema_hash'] ) );
	}
	return $context;
}

function wpagent_pods_act_recovery_required( string $model_name, int $record_id = 0 ): WP_Error {
	return new WP_Error( 'wpagent_pods_act_recovery_required', __( 'Der native Advanced-Content-Type-Write wurde nicht vollständig und exakt verifiziert. Es wurde keine automatische Folgeänderung, Löschung oder Wiederherstellung ausgeführt. Prüfe den aktuellen Zustand manuell.', 'wp-agent-companion' ), array( 'status' => 500, 'recovery_required' => true, 'model_name' => $model_name, 'record_id' => $record_id ) );
}

/** @return array<string,string> */
function wpagent_pods_act_expected_record_values( array $context, array $changes, ?array $current_values = null ): array {
	$expected = array();
	foreach ( $context['fields'] as $field ) {
		$name = (string) $field['name'];
		$expected[ $name ] = is_array( $current_values ) && is_string( $current_values[ $name ] ?? null ) ? $current_values[ $name ] : '';
	}
	foreach ( $changes as $name => $value ) {
		$expected[ $name ] = $value;
	}
	ksort( $expected, SORT_STRING );
	return $expected;
}

function wpagent_pods_act_record_values_match( array $actual_values, array $expected_values ): bool {
	if ( count( $actual_values ) !== count( $expected_values ) ) {
		return false;
	}
	foreach ( $expected_values as $name => $value ) {
		if ( ! is_string( $actual_values[ $name ] ?? null ) || ! hash_equals( $value, $actual_values[ $name ] ) ) {
			return false;
		}
	}
	return true;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_pods_act_verify_written_record( string $model_name, int $blog_id, string $schema_hash, string $lock_name, string $token, int $record_id, array $expected_values ): array|WP_Error {
	$context = wpagent_pods_act_locked_context( $model_name, $blog_id, $schema_hash, $lock_name, $token );
	if ( is_wp_error( $context ) ) {
		return $context;
	}
	$record = wpagent_pods_act_get_record( $context, $record_id );
	if ( is_wp_error( $record ) ) {
		return $record;
	}
	if ( ! wpagent_pods_act_record_values_match( $record['values'], $expected_values ) ) {
		return new WP_Error( 'wpagent_pods_act_readback_mismatch', __( 'Der Advanced-Content-Type-Datensatz weicht nach dem nativen Write vom angeforderten vollständigen Zustand ab.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	return $record;
}
