<?php
/** Shared helpers for verified persistent ACF post-type and taxonomy definitions. */

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

/** @return array<string,array<string,mixed>> */
function wpagent_acf_model_kinds(): array {
	return array(
		'post_type' => array(
			'acf_type' => 'acf-post-type',
			'key_prefix' => 'post_type',
			'slug_field' => 'post_type',
			'get' => 'acf_get_post_type',
			'get_raw' => 'acf_get_raw_post_types',
			'validate' => 'acf_validate_post_type',
			'update' => 'acf_update_post_type',
			'delete' => 'acf_delete_post_type',
		),
		'taxonomy' => array(
			'acf_type' => 'acf-taxonomy',
			'key_prefix' => 'taxonomy',
			'slug_field' => 'taxonomy',
			'get' => 'acf_get_taxonomy',
			'get_raw' => 'acf_get_raw_taxonomies',
			'validate' => 'acf_validate_taxonomy',
			'update' => 'acf_update_taxonomy',
			'delete' => 'acf_delete_taxonomy',
		),
	);
}

/** @return true|WP_Error */
function wpagent_acf_model_require_api() {
	if ( ! function_exists( 'acf_get_internal_post_type_instance' ) ) {
		return new WP_Error( 'wpagent_acf_model_missing', __( 'Advanced Custom Fields 6.1 oder neuer ist nicht aktiv oder seine Inhaltstyp-API ist nicht verfügbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	foreach ( wpagent_acf_model_kinds() as $config ) {
		foreach ( array( 'get', 'get_raw', 'validate', 'update', 'delete' ) as $operation ) {
			if ( ! function_exists( $config[ $operation ] ) ) {
				return new WP_Error( 'wpagent_acf_model_missing', __( 'Advanced Custom Fields 6.1 oder neuer ist nicht aktiv oder seine Inhaltstyp-API ist nicht verfügbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
			}
		}
		$instance = acf_get_internal_post_type_instance( $config['acf_type'] );
		if ( ! is_object( $instance ) || ! method_exists( $instance, 'get_settings_array' ) ) {
			return new WP_Error( 'wpagent_acf_model_missing', __( 'Advanced Custom Fields 6.1 oder neuer ist nicht aktiv oder seine Inhaltstyp-API ist nicht verfügbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
	}
	return true;
}

function wpagent_acf_model_supports_ai_settings( string $kind ): bool {
	$config = wpagent_acf_model_kinds()[ $kind ];
	$instance = acf_get_internal_post_type_instance( $config['acf_type'] );
	$settings = is_object( $instance ) && method_exists( $instance, 'get_settings_array' ) ? $instance->get_settings_array() : array();
	return array_key_exists( 'allow_ai_access', $settings ) && array_key_exists( 'ai_description', $settings );
}

/** @return mixed */
function wpagent_acf_model_canonicalize( $value ) {
	if ( ! is_array( $value ) ) {
		return $value;
	}
	if ( ! array_is_list( $value ) ) {
		ksort( $value, SORT_STRING );
	}
	foreach ( $value as $key => $item ) {
		$value[ $key ] = wpagent_acf_model_canonicalize( $item );
	}
	return $value;
}

/** @return array<string,mixed> */
function wpagent_acf_model_definition_state( string $kind, array $model ): array {
	$config = wpagent_acf_model_kinds()[ $kind ];
	$model = call_user_func( $config['validate'], $model );
	foreach ( 'post_type' === $kind ? array( 'supports', 'taxonomies' ) : array( 'object_type' ) as $field ) {
		if ( is_array( $model[ $field ] ?? null ) ) {
			sort( $model[ $field ], SORT_STRING );
		}
	}
	unset( $model['ID'], $model['local'], $model['_valid'], $model['not_registered'] );
	return wpagent_acf_model_canonicalize( $model );
}

function wpagent_acf_model_definition_matches( string $kind, array $actual, array $expected ): bool {
	return wpagent_acf_model_definition_state( $kind, $actual ) === wpagent_acf_model_definition_state( $kind, $expected );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acf_model_get_persistent( string $kind, string $key ) {
	$kinds = wpagent_acf_model_kinds();
	if ( ! isset( $kinds[ $kind ] ) ) {
		return new WP_Error( 'wpagent_acf_model_kind', __( 'Unbekannte ACF-Datenmodellart.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$config = $kinds[ $kind ];
	$pattern = '/^' . preg_quote( $config['key_prefix'], '/' ) . '_[A-Za-z0-9_-]{1,120}$/';
	if ( ! preg_match( $pattern, $key ) ) {
		return new WP_Error( 'wpagent_acf_model_key', __( 'key ist kein gültiger ACF-Inhaltstyp- oder Taxonomie-Key.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$model = call_user_func( $config['get'], $key );
	$id = is_array( $model ) ? absint( $model['ID'] ?? 0 ) : 0;
	if ( ! is_array( $model ) || $id < 1 || ! empty( $model['local'] ) || $config['acf_type'] !== get_post_type( $id ) ) {
		return new WP_Error( 'wpagent_acf_model_not_found', __( 'Persistente ACF-Definition nicht gefunden. In PHP oder Local JSON registrierte Definitionen werden nicht verändert.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}
	return $model;
}

/** @return string|WP_Error */
function wpagent_acf_model_text( $value, string $label, int $max, bool $allow_empty = false ) {
	$text = is_string( $value ) ? trim( $value ) : '';
	if ( ( ! $allow_empty && '' === $text ) || wpagent_acf_group_text_length( $text ) > $max || sanitize_text_field( $text ) !== $text ) {
		/* translators: 1: input field name, 2: maximum number of characters. */
		return new WP_Error( 'wpagent_acf_model_text', sprintf( __( '%1$s muss reiner Text mit höchstens %2$d Zeichen sein.', 'wp-agent-companion' ), $label, $max ), array( 'status' => 400 ) );
	}
	return $text;
}

/** @return string|WP_Error */
function wpagent_acf_model_slug( $value, string $kind ) {
	$slug = is_string( $value ) ? $value : '';
	$max = 'post_type' === $kind ? 20 : 32;
	if ( '' === $slug || strlen( $slug ) > $max || ! preg_match( '/^[a-z][a-z0-9_-]*$/', $slug ) || ( function_exists( 'acf_get_wp_reserved_terms' ) && in_array( $slug, acf_get_wp_reserved_terms(), true ) ) ) {
		/* translators: %d: maximum slug length in characters. */
		return new WP_Error( 'wpagent_acf_model_slug', sprintf( __( 'Der Slug muss mit einem Kleinbuchstaben beginnen, höchstens %d Zeichen lang und frei von reservierten WordPress-Begriffen sein.', 'wp-agent-companion' ), $max ), array( 'status' => 400 ) );
	}
	return $slug;
}

/** @return string|WP_Error */
function wpagent_acf_model_rewrite_slug( $value ) {
	$slug = is_string( $value ) ? trim( $value ) : '';
	if ( '' !== $slug && ( strlen( $slug ) > 200 || sanitize_title( $slug ) !== $slug ) ) {
		return new WP_Error( 'wpagent_acf_model_rewrite', __( 'rewrite_slug muss leer oder ein gültiger URL-Slug mit höchstens 200 Zeichen sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return $slug;
}

/** @return string[]|WP_Error */
function wpagent_acf_model_supports( $value ) {
	$allowed = array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments', 'trackbacks', 'revisions', 'page-attributes', 'custom-fields', 'post-formats' );
	if ( ! is_array( $value ) || ! array_is_list( $value ) || count( $value ) > count( $allowed ) ) {
		return new WP_Error( 'wpagent_acf_model_supports', __( 'supports muss eine 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_acf_model_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 string[]|WP_Error */
function wpagent_acf_model_object_types( $value ) {
	if ( ! is_array( $value ) || ! array_is_list( $value ) || array() === $value || count( $value ) > 20 ) {
		return new WP_Error( 'wpagent_acf_model_objects', __( 'object_types muss 1-20 registrierte Inhaltstypen enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$result = array();
	foreach ( $value as $post_type ) {
		if ( ! is_string( $post_type ) || sanitize_key( $post_type ) !== $post_type || ! post_type_exists( $post_type ) ) {
			return new WP_Error( 'wpagent_acf_model_object', __( 'Ein object_type ist nicht registriert oder ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$result[] = $post_type;
	}
	$result = array_values( array_unique( $result ) );
	sort( $result, SORT_STRING );
	return $result;
}

/** @return string[]|WP_Error */
function wpagent_acf_model_taxonomies( $value ) {
	if ( ! is_array( $value ) || ! array_is_list( $value ) || count( $value ) > 20 ) {
		return new WP_Error( 'wpagent_acf_model_taxonomies', __( 'taxonomies muss eine Liste mit höchstens 20 registrierten Taxonomien sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$result = array();
	foreach ( $value as $taxonomy ) {
		if ( ! is_string( $taxonomy ) || sanitize_key( $taxonomy ) !== $taxonomy || ! taxonomy_exists( $taxonomy ) ) {
			return new WP_Error( 'wpagent_acf_model_taxonomy', __( 'Eine Taxonomie ist nicht registriert oder ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$result[] = $taxonomy;
	}
	$result = array_values( array_unique( $result ) );
	sort( $result, SORT_STRING );
	return $result;
}

function wpagent_acf_model_content_count( string $kind, string $slug ): int {
	global $wpdb;
	if ( 'post_type' === $kind ) {
		return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = %s AND post_status NOT IN ('auto-draft','inherit')", $slug ) );
	}
	return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT term_id) FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s", $slug ) );
}

function wpagent_acf_model_runtime_matches( string $kind, array $state ): bool {
	$slug = (string) $state['slug'];
	if ( ! $state['active'] ) {
		return 'post_type' === $kind ? ! post_type_exists( $slug ) : ! taxonomy_exists( $slug );
	}
	if ( 'post_type' === $kind ) {
		$object = get_post_type_object( $slug );
		if ( ! $object
			|| (bool) $object->public !== $state['public']
			|| (bool) $object->publicly_queryable !== $state['public']
			|| (bool) $object->hierarchical !== $state['hierarchical']
			|| (bool) $object->show_ui !== $state['show_ui']
			|| (bool) $object->show_in_rest !== $state['show_in_rest'] ) {
			return false;
		}
		$supports = array_keys( get_all_post_type_supports( $slug ) );
		$supports = array_values( array_diff( $supports, array( 'autosave' ) ) );
		sort( $supports, SORT_STRING );
		if ( $supports !== $state['supports'] ) {
			return false;
		}
		$taxonomies = get_object_taxonomies( $slug, 'names' );
		return array() === array_diff( $state['taxonomies'], $taxonomies );
	}
	$object = get_taxonomy( $slug );
	if ( ! $object
		|| (bool) $object->public !== $state['public']
		|| (bool) $object->publicly_queryable !== $state['public']
		|| (bool) $object->hierarchical !== $state['hierarchical']
		|| (bool) $object->show_ui !== $state['show_ui']
		|| (bool) $object->show_in_rest !== $state['show_in_rest']
		|| (bool) $object->show_admin_column !== $state['show_admin_column'] ) {
		return false;
	}
	$object_types = array_values( (array) $object->object_type );
	return array() === array_diff( $state['object_types'], $object_types );
}

/** @return array<string,mixed> */
function wpagent_acf_model_portable_definition( string $kind, array $state, int $id = 0 ): array {
	$config = wpagent_acf_model_kinds()[ $kind ];
	$instance = acf_get_internal_post_type_instance( $config['acf_type'] );
	$definition = $instance->get_settings_array();
	$definition['ID'] = $id;
	$definition['key'] = $state['key'];
	$definition['title'] = $state['plural_label'];
	$definition['active'] = $state['active'];
	$definition[ $config['slug_field'] ] = $state['slug'];
	$definition['labels']['name'] = $state['plural_label'];
	$definition['labels']['singular_name'] = $state['singular_label'];
	$definition['description'] = $state['description'];
	$definition['public'] = $state['public'];
	$definition['publicly_queryable'] = $state['public'];
	$definition['show_ui'] = $state['show_ui'];
	$definition['show_in_menu'] = $state['show_ui'];
	$definition['show_in_nav_menus'] = $state['public'];
	$definition['show_in_rest'] = $state['show_in_rest'];
	$definition['hierarchical'] = $state['hierarchical'];
	if ( wpagent_acf_model_supports_ai_settings( $kind ) ) {
		$definition['allow_ai_access'] = $state['allow_ai_access'];
		$definition['ai_description'] = $state['ai_description'];
	}
	$definition['rewrite']['slug'] = $state['rewrite_slug'];
	$definition['rewrite']['permalink_rewrite'] = '' === $state['rewrite_slug'] ? $config['slug_field'] . '_key' : 'custom_permalink';
	if ( 'post_type' === $kind ) {
		$definition['exclude_from_search'] = ! $state['public'];
		$definition['show_in_admin_bar'] = $state['show_ui'];
		$definition['supports'] = $state['supports'];
		$definition['taxonomies'] = $state['taxonomies'];
		$definition['has_archive'] = $state['has_archive'];
		$definition['has_archive_slug'] = '';
	} else {
		$definition['object_type'] = $state['object_types'];
		$definition['show_tagcloud'] = $state['show_ui'];
		$definition['show_in_quick_edit'] = $state['show_ui'];
		$definition['show_admin_column'] = $state['show_admin_column'];
	}
	return $definition;
}

/** @return array<string,mixed> */
function wpagent_acf_model_state( string $kind, array $model ): array {
	$config = wpagent_acf_model_kinds()[ $kind ];
	$labels = is_array( $model['labels'] ?? null ) ? $model['labels'] : array();
	$rewrite = is_array( $model['rewrite'] ?? null ) ? $model['rewrite'] : array();
	$slug = (string) ( $model[ $config['slug_field'] ] ?? '' );
	$state = array(
		'id' => absint( $model['ID'] ?? 0 ),
		'key' => (string) ( $model['key'] ?? '' ),
		'slug' => $slug,
		'singular_label' => (string) ( $labels['singular_name'] ?? '' ),
		'plural_label' => (string) ( $labels['name'] ?? $model['title'] ?? '' ),
		'description' => (string) ( $model['description'] ?? '' ),
		'active' => (bool) ( $model['active'] ?? false ),
		'public' => (bool) ( $model['public'] ?? false ),
		'hierarchical' => (bool) ( $model['hierarchical'] ?? false ),
		'show_ui' => (bool) ( $model['show_ui'] ?? false ),
		'show_in_rest' => (bool) ( $model['show_in_rest'] ?? false ),
		'allow_ai_access' => (bool) ( $model['allow_ai_access'] ?? false ),
		'ai_description' => (string) ( $model['ai_description'] ?? '' ),
		'rewrite_slug' => 'custom_permalink' === (string) ( $rewrite['permalink_rewrite'] ?? '' ) ? (string) ( $rewrite['slug'] ?? '' ) : '',
		'content_count' => wpagent_acf_model_content_count( $kind, $slug ),
	);
	$state[ $config['slug_field'] ] = $slug;
	$state['registration_conflict'] = ! empty( $model['not_registered'] );
	if ( 'post_type' === $kind ) {
		$supports = is_array( $model['supports'] ?? null ) ? array_values( array_filter( $model['supports'], 'is_string' ) ) : array();
		$taxonomies = is_array( $model['taxonomies'] ?? null ) ? array_values( array_filter( $model['taxonomies'], 'is_string' ) ) : array();
		sort( $supports, SORT_STRING );
		sort( $taxonomies, SORT_STRING );
		$state['supports'] = $supports;
		$state['taxonomies'] = $taxonomies;
		$state['has_archive'] = (bool) ( $model['has_archive'] ?? false );
	} else {
		$object_types = is_array( $model['object_type'] ?? null ) ? array_values( array_filter( $model['object_type'], 'is_string' ) ) : array();
		sort( $object_types, SORT_STRING );
		$state['object_types'] = $object_types;
		$state['show_admin_column'] = (bool) ( $model['show_admin_column'] ?? false );
	}
	$expected = wpagent_acf_model_portable_definition( $kind, $state, $state['id'] );
	$state['portable'] = ! $state['registration_conflict'] && wpagent_acf_model_definition_matches( $kind, $model, $expected );
	$material = wpagent_acf_model_definition_state( $kind, $model );
	$encoded = wp_json_encode( $material, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
	$state['hash'] = hash( 'sha256', false === $encoded ? serialize( $material ) : $encoded );
	return $state;
}

function wpagent_acf_model_new_key( string $kind ): string {
	$config = wpagent_acf_model_kinds()[ $kind ];
	for ( $attempt = 0; $attempt < 10; ++$attempt ) {
		$key = $config['key_prefix'] . '_wpagently_' . substr( str_replace( '-', '', wp_generate_uuid4() ), 0, 24 );
		if ( ! call_user_func( $config['get'], $key ) ) {
			return $key;
		}
	}
	throw new RuntimeException( 'Could not generate a unique ACF model key.' );
}

/** @return string|WP_Error */
function wpagent_acf_model_acquire_lock( string $kind, string $key ) {
	$option = 'wp_agent_acf_model_lock_' . hash( 'sha256', $kind . ':' . $key );
	$token  = wpagent_companion_acquire_option_lock( $option, 300 );
	if ( is_string( $token ) ) {
		return $token;
	}
	return new WP_Error( 'wpagent_acf_model_busy', __( 'Diese ACF-Definition wird gerade geändert. Versuche es gleich erneut.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

function wpagent_acf_model_release_lock( string $kind, string $key, string $token ): void {
	wpagent_companion_release_option_lock( 'wp_agent_acf_model_lock_' . hash( 'sha256', $kind . ':' . $key ), $token );
}

/** @return array<string,mixed> */
function wpagent_acf_model_output_schema( string $kind ): array {
	$config = wpagent_acf_model_kinds()[ $kind ];
	$properties = array(
		'id' => array( 'type' => 'integer' ), 'key' => array( 'type' => 'string' ), 'slug' => array( 'type' => 'string' ),
		'singular_label' => array( 'type' => 'string' ), 'plural_label' => array( 'type' => 'string' ), 'description' => array( 'type' => 'string' ),
		'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' ), 'rewrite_slug' => array( 'type' => 'string' ), 'content_count' => array( 'type' => 'integer' ),
		'portable' => array( 'type' => 'boolean' ), 'registration_conflict' => array( 'type' => 'boolean' ), 'hash' => array( 'type' => 'string' ),
	);
	$properties[ $config['slug_field'] ] = array( 'type' => 'string' );
	if ( 'post_type' === $kind ) {
		$properties['supports'] = array( 'type' => 'array', 'items' => array( 'type' => 'string' ) );
		$properties['taxonomies'] = array( 'type' => 'array', 'items' => array( 'type' => 'string' ) );
		$properties['has_archive'] = array( 'type' => 'boolean' );
	} else {
		$properties['object_types'] = array( 'type' => 'array', 'items' => array( 'type' => 'string' ) );
		$properties['show_admin_column'] = array( 'type' => 'boolean' );
	}
	return array( 'type' => 'object', 'properties' => $properties, 'required' => array_keys( $properties ), 'additionalProperties' => false );
}
