<?php
/** Shared helpers for native ACPT Lite content-model definitions. */

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

/** @return array<string,string>|WP_Error */
function wpagent_acpt_provider() {
	$provider = array(
		'post_repository' => 'ACPT_Lite\\Core\\Repository\\CustomPostTypeRepository',
		'tax_repository' => 'ACPT_Lite\\Core\\Repository\\TaxonomyRepository',
		'post_model' => 'ACPT_Lite\\Core\\Models\\CustomPostType\\CustomPostTypeModel',
		'tax_model' => 'ACPT_Lite\\Core\\Models\\Taxonomy\\TaxonomyModel',
		'reserved_terms' => 'ACPT_Lite\\Constants\\ReservedTerms',
		'database' => 'ACPT_Lite\\Includes\\ACPT_Lite_DB',
		'version' => defined( 'ACPT_LITE_PLUGIN_VERSION' ) ? (string) ACPT_LITE_PLUGIN_VERSION : '',
	);
	if ( '' === $provider['version'] || version_compare( $provider['version'], '2.0.0', '<' ) ) {
		return new WP_Error( 'wpagent_acpt_missing', __( 'ACPT Lite 2.0 oder neuer ist nicht aktiv.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	foreach ( array( 'post_repository', 'tax_repository', 'post_model', 'tax_model', 'reserved_terms', 'database' ) as $key ) {
		if ( ! class_exists( $provider[ $key ] ) ) {
			return new WP_Error( 'wpagent_acpt_missing', __( 'ACPT Lite 2.0 oder neuer ist nicht aktiv.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
	}
	foreach ( array( 'get', 'count', 'exists', 'save', 'delete' ) as $method ) {
		if ( ! is_callable( array( $provider['post_repository'], $method ) ) || ! is_callable( array( $provider['tax_repository'], $method ) ) ) {
			return new WP_Error( 'wpagent_acpt_api', __( 'Die benötigte native ACPT-Lite-Repository-API ist nicht verfügbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
	}
	if ( ! is_callable( array( $provider['post_repository'], 'getId' ) ) || ! is_callable( array( $provider['tax_repository'], 'getId' ) ) || ! is_callable( array( $provider['tax_repository'], 'assocToPostType' ) ) || ! is_callable( array( $provider['tax_repository'], 'removeAssocPost' ) ) || ! is_callable( array( $provider['reserved_terms'], 'list' ) ) || ! is_callable( array( $provider['database'], 'flushCache' ) ) ) {
		return new WP_Error( 'wpagent_acpt_api', __( 'Die benötigte native ACPT-Lite-Zuordnungs-API ist nicht verfügbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return $provider;
}

/** @return true|WP_Error */
function wpagent_acpt_flush_provider_cache( array $provider ) {
	try {
		$provider['database']::flushCache();
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_acpt_cache', __( 'Der ACPT-Lite-Providercache konnte nicht zuverlässig geleert werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	return true;
}

function wpagent_acpt_is_not_found_error( $value ): bool {
	return is_wp_error( $value ) && in_array( 'wpagent_acpt_not_found', $value->get_error_codes(), true );
}

function wpagent_acpt_text_length( string $value ): int {
	return function_exists( 'mb_strlen' ) ? mb_strlen( $value, 'UTF-8' ) : strlen( $value );
}

/** @return string|WP_Error */
function wpagent_acpt_text( $value, string $label, int $max, bool $allow_empty = false ) {
	$text = is_string( $value ) ? trim( $value ) : '';
	if ( ( ! $allow_empty && '' === $text ) || wpagent_acpt_text_length( $text ) > $max || sanitize_text_field( $text ) !== $text ) {
		/* translators: 1: input field name, 2: maximum number of characters. */
		return new WP_Error( 'wpagent_acpt_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_acpt_slug( $value, string $kind ) {
	$slug = is_string( $value ) ? $value : '';
	$max = 'post_type' === $kind ? 20 : 32;
	if ( '' === $slug || strlen( $slug ) > $max || ! preg_match( '/^[a-z0-9][a-z0-9_-]*$/', $slug ) || sanitize_key( $slug ) !== $slug ) {
		/* translators: %d: maximum identifier length. */
		return new WP_Error( 'wpagent_acpt_slug', sprintf( __( 'Der ACPT-Bezeichner muss mit einer Zahl oder einem Kleinbuchstaben beginnen und darf höchstens %d Zeichen aus a-z, 0-9, Bindestrich und Unterstrich enthalten.', 'wp-agent-companion' ), $max ), array( 'status' => 400 ) );
	}
	return $slug;
}

/** @return string|WP_Error */
function wpagent_acpt_rewrite_slug( $value ) {
	$slug = is_string( $value ) ? trim( $value ) : '';
	if ( '' !== $slug && ( strlen( $slug ) > 200 || sanitize_title( $slug ) !== $slug ) ) {
		return new WP_Error( 'wpagent_acpt_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 true|WP_Error */
function wpagent_acpt_validate_rewrite_path( string $slug, string $current_kind = '', string $current_slug = '' ) {
	if ( get_page_by_path( $slug, OBJECT, 'page' ) ) {
		return new WP_Error( 'wpagent_acpt_page_conflict', __( 'Eine WordPress-Seite verwendet den geplanten Rewrite-Pfad bereits.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$registries = array(
		'post_type' => get_post_types( array(), 'objects' ),
		'taxonomy' => get_taxonomies( array(), 'objects' ),
	);
	foreach ( $registries as $kind => $objects ) {
		foreach ( $objects as $object ) {
			$name = isset( $object->name ) && is_string( $object->name ) ? $object->name : '';
			if ( $kind === $current_kind && $name === $current_slug ) {
				continue;
			}
			$rewrite = $object->rewrite ?? false;
			$registered_slug = is_array( $rewrite ) && is_string( $rewrite['slug'] ?? null ) ? trim( $rewrite['slug'], '/' ) : '';
			if ( '' !== $registered_slug && $registered_slug === $slug ) {
				return new WP_Error( 'wpagent_acpt_rewrite_conflict', __( 'Ein registrierter Inhaltstyp oder eine Taxonomie verwendet den geplanten Rewrite-Pfad bereits.', 'wp-agent-companion' ), array( 'status' => 409 ) );
			}
		}
	}
	return true;
}

/** @return string[] */
function wpagent_acpt_supported_post_supports(): array {
	return array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions', 'page-attributes', 'post-formats' );
}

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

function wpagent_acpt_hash( $value ): string {
	$value = wpagent_acpt_canonicalize( $value );
	$encoded = wp_json_encode( $value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
	return hash( 'sha256', false === $encoded ? serialize( $value ) : $encoded );
}

/** @return array<int,object>|WP_Error */
function wpagent_acpt_models( string $kind ) {
	$provider = wpagent_acpt_provider();
	if ( is_wp_error( $provider ) ) {
		return $provider;
	}
	$cache = wpagent_acpt_flush_provider_cache( $provider );
	if ( is_wp_error( $cache ) ) {
		return $cache;
	}
	$repository = 'post_type' === $kind ? $provider['post_repository'] : $provider['tax_repository'];
	try {
		$models = $repository::get( array() );
		$count = $repository::count();
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_acpt_read', __( 'Die ACPT-Lite-Definitionen konnten nicht vollständig gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( ! is_array( $models ) || ! is_int( $count ) || $count < 0 || $count > 500 || count( $models ) !== $count ) {
		return new WP_Error( 'wpagent_acpt_storage', __( 'ACPT Lite lieferte einen unvollständigen oder zu großen Definitionsbestand.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	foreach ( $models as $model ) {
		if ( ! is_object( $model ) || ! method_exists( $model, 'getId' ) || ! method_exists( $model, 'isNative' ) ) {
			return new WP_Error( 'wpagent_acpt_storage', __( 'ACPT Lite lieferte eine beschädigte Definition.', 'wp-agent-companion' ), array( 'status' => 500 ) );
		}
	}
	return $models;
}

/** @return object|WP_Error */
function wpagent_acpt_model( string $kind, string $slug ) {
	$provider = wpagent_acpt_provider();
	if ( is_wp_error( $provider ) ) {
		return $provider;
	}
	$cache = wpagent_acpt_flush_provider_cache( $provider );
	if ( is_wp_error( $cache ) ) {
		return $cache;
	}
	$repository = 'post_type' === $kind ? $provider['post_repository'] : $provider['tax_repository'];
	$key = 'post_type' === $kind ? 'postType' : 'taxonomy';
	try {
		$models = $repository::get( array( $key => $slug ) );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_acpt_read', __( 'Die ACPT-Lite-Definition konnte nicht gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( ! is_array( $models ) || count( $models ) > 1 ) {
		return new WP_Error( 'wpagent_acpt_storage', __( 'ACPT Lite lieferte eine mehrdeutige Definition.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( array() === $models ) {
		return new WP_Error( 'wpagent_acpt_not_found', __( 'Persistente ACPT-Lite-Definition nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}
	$model = reset( $models );
	if ( ! is_object( $model ) ) {
		return new WP_Error( 'wpagent_acpt_storage', __( 'ACPT Lite lieferte eine beschädigte Definition.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	try {
		$native = $model->isNative();
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_acpt_storage', __( 'ACPT Lite lieferte eine beschädigte Definition.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( ! is_bool( $native ) ) {
		return new WP_Error( 'wpagent_acpt_storage', __( 'ACPT Lite lieferte eine beschädigte Definition.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( $native ) {
		return new WP_Error( 'wpagent_acpt_native', __( 'Von ACPT Lite synchronisierte WordPress- oder Fremddefinitionen werden nicht verändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return $model;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acpt_raw_model( string $kind, object $model ) {
	try {
		$id = $model->getId();
		$singular = $model->getSingular();
		$plural = $model->getPlural();
		$native = $model->isNative();
		$labels = $model->getLabels();
		$settings = $model->getSettings();
		if ( ! is_string( $id ) || '' === $id || ! is_string( $singular ) || ! is_string( $plural ) || ! is_bool( $native ) || ! is_array( $labels ) || ! is_array( $settings ) ) {
			throw new UnexpectedValueException( 'Malformed ACPT provider model.' );
		}
		if ( 'post_type' === $kind ) {
			$slug = $model->getName();
			$icon = $model->getIcon();
			if ( ! is_string( $slug ) || '' === $slug || ! is_string( $icon ) ) {
				throw new UnexpectedValueException( 'Malformed ACPT post-type model.' );
			}
			$associations = array_map( static fn( object $item ): string => $item->getSlug(), $model->getTaxonomies() );
			$supports = array_values( array_unique( array_map( 'strval', $model->getSupports() ) ) );
			sort( $associations, SORT_STRING );
			sort( $supports, SORT_STRING );
			return array(
				'id' => $id, 'slug' => $slug, 'singular' => $singular, 'plural' => $plural,
				'icon' => $icon, 'native' => $native, 'supports' => $supports, 'labels' => $labels,
				'settings' => $settings, 'associations' => $associations,
			);
		}
		$slug = $model->getSlug();
		if ( 'taxonomy' !== $kind || ! is_string( $slug ) || '' === $slug ) {
			throw new UnexpectedValueException( 'Malformed ACPT taxonomy model.' );
		}
		$associations = array_map( static fn( object $item ): string => $item->getName(), $model->getCustomPostTypes() );
		sort( $associations, SORT_STRING );
		return array(
			'id' => $id, 'slug' => $slug, 'singular' => $singular, 'plural' => $plural,
			'native' => $native, 'labels' => $labels, 'settings' => $settings, 'associations' => $associations,
		);
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_acpt_storage', __( 'Die ACPT-Lite-Definition enthält ungültige Providerdaten.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
}

/** @return array<string,int>|WP_Error */
function wpagent_acpt_content_counts( string $kind, array $slugs ) {
	$slugs = array_values( array_unique( array_filter( array_map( 'strval', $slugs ) ) ) );
	$counts = array_fill_keys( $slugs, 0 );
	if ( array() === $slugs ) {
		return $counts;
	}
	global $wpdb;
	$placeholders = implode( ',', array_fill( 0, count( $slugs ), '%s' ) );
	$wpdb->last_error = '';
	if ( 'post_type' === $kind ) {
		$query = $wpdb->prepare( "SELECT post_type AS slug, COUNT(ID) AS amount FROM {$wpdb->posts} WHERE post_type IN ({$placeholders}) AND post_status NOT IN ('auto-draft','inherit') GROUP BY post_type", $slugs );
	} else {
		$query = $wpdb->prepare( "SELECT taxonomy AS slug, COUNT(DISTINCT term_id) AS amount FROM {$wpdb->term_taxonomy} WHERE taxonomy IN ({$placeholders}) GROUP BY taxonomy", $slugs );
	}
	$rows = $wpdb->get_results( $query, ARRAY_A );
	if ( '' !== $wpdb->last_error || ! is_array( $rows ) ) {
		return new WP_Error( 'wpagent_acpt_count', __( 'Die Inhaltszahlen der ACPT-Lite-Definitionen konnten nicht zuverlässig gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	foreach ( $rows as $row ) {
		if ( ! is_array( $row ) || ! isset( $row['slug'], $row['amount'] ) ) {
			return new WP_Error( 'wpagent_acpt_count', __( 'Die Inhaltszahlen der ACPT-Lite-Definitionen konnten nicht zuverlässig gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
		}
		if ( isset( $counts[ $row['slug'] ] ) ) {
			$counts[ $row['slug'] ] = (int) $row['amount'];
		}
	}
	return $counts;
}

/** @return array<string,int>|WP_Error */
function wpagent_acpt_meta_link_counts( string $kind, array $slugs ) {
	$slugs = array_values( array_unique( array_filter( array_map( 'strval', $slugs ) ) ) );
	$counts = array_fill_keys( $slugs, 0 );
	if ( array() === $slugs ) {
		return $counts;
	}
	global $wpdb;
	$table = $wpdb->prefix . 'acpt_lite_belong';
	$belongs = 'post_type' === $kind ? 'customPostType' : 'taxonomy';
	$placeholders = implode( ',', array_fill( 0, count( $slugs ), '%s' ) );
	$args = array_merge( array( $belongs ), $slugs );
	$wpdb->last_error = '';
	$query = $wpdb->prepare( "SELECT find AS slug, COUNT(*) AS amount FROM {$table} WHERE belongs = %s AND find IN ({$placeholders}) GROUP BY find", ...$args );
	$rows = $wpdb->get_results( $query, ARRAY_A );
	if ( '' !== $wpdb->last_error || ! is_array( $rows ) ) {
		return new WP_Error( 'wpagent_acpt_dependencies', __( 'Verknüpfte ACPT-Lite-Metafelder konnten nicht zuverlässig geprüft werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	foreach ( $rows as $row ) {
		if ( ! is_array( $row ) || ! isset( $row['slug'], $row['amount'] ) ) {
			return new WP_Error( 'wpagent_acpt_dependencies', __( 'Verknüpfte ACPT-Lite-Metafelder konnten nicht zuverlässig geprüft werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
		}
		if ( isset( $counts[ $row['slug'] ] ) ) {
			$counts[ $row['slug'] ] = (int) $row['amount'];
		}
	}
	return $counts;
}

/** @return int|WP_Error */
function wpagent_acpt_meta_link_count( string $kind, string $slug ) {
	$counts = wpagent_acpt_meta_link_counts( $kind, array( $slug ) );
	return is_wp_error( $counts ) ? $counts : $counts[ $slug ];
}

/** @return string[] */
function wpagent_acpt_unmanaged_fields( string $kind, array $raw ): array {
	$known_settings = array( 'description', 'public', 'publicly_queryable', 'show_ui', 'show_in_menu', 'show_in_nav_menus', 'show_in_rest', 'hierarchical', 'rewrite', 'custom_rewrite', 'with_front', 'query_var' );
	$known_settings = array_merge( $known_settings, 'post_type' === $kind ? array( 'has_archive', 'exclude_from_search', 'can_export', 'delete_with_user', 'menu_position' ) : array( 'show_tagcloud', 'show_in_quick_edit', 'show_admin_column', 'sort' ) );
	$unmanaged = array();
	foreach ( array_keys( $raw['settings'] ) as $key ) {
		if ( ! in_array( $key, $known_settings, true ) ) {
			$unmanaged[] = 'settings.' . $key;
		}
	}
	return $unmanaged;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acpt_state( string $kind, object $model, ?int $content_count = null, ?int $meta_link_count = null ) {
	$raw = wpagent_acpt_raw_model( $kind, $model );
	if ( is_wp_error( $raw ) ) {
		return $raw;
	}
	if ( null === $content_count ) {
		$counts = wpagent_acpt_content_counts( $kind, array( $raw['slug'] ) );
		if ( is_wp_error( $counts ) ) {
			return $counts;
		}
		$content_count = $counts[ $raw['slug'] ];
	}
	if ( null === $meta_link_count ) {
		$meta_link_count = wpagent_acpt_meta_link_count( $kind, $raw['slug'] );
		if ( is_wp_error( $meta_link_count ) ) {
			return $meta_link_count;
		}
	}
	$settings = $raw['settings'];
	$rewrite = $settings['rewrite'] ?? false;
	$rewrite_slug = 'post_type' === $kind ? (string) ( $settings['custom_rewrite'] ?? '' ) : ( is_array( $rewrite ) ? (string) ( $rewrite['slug'] ?? '' ) : '' );
	$state = array(
		'slug' => $raw['slug'], 'singular_label' => $raw['singular'], 'plural_label' => $raw['plural'],
		'description' => (string) ( $settings['description'] ?? '' ), 'public' => (bool) ( $settings['public'] ?? true ),
		'hierarchical' => (bool) ( $settings['hierarchical'] ?? false ), 'show_ui' => (bool) ( $settings['show_ui'] ?? true ),
		'show_in_rest' => (bool) ( $settings['show_in_rest'] ?? true ), 'rewrite_slug' => $rewrite_slug,
		'content_count' => $content_count, 'meta_group_links' => $meta_link_count,
		'runtime_registered' => 'post_type' === $kind ? post_type_exists( $raw['slug'] ) : taxonomy_exists( $raw['slug'] ),
		'unmanaged_fields' => wpagent_acpt_unmanaged_fields( $kind, $raw ), 'model_hash' => wpagent_acpt_hash( $raw ),
	);
	if ( 'post_type' === $kind ) {
		$state['icon'] = $raw['icon'];
		$state['supports'] = array_values( array_unique( array_map( 'strval', $raw['supports'] ) ) );
		$state['taxonomies'] = array_values( array_unique( array_map( 'strval', $raw['associations'] ) ) );
		$state['has_archive'] = (bool) ( $settings['has_archive'] ?? false );
		sort( $state['supports'], SORT_STRING );
		sort( $state['taxonomies'], SORT_STRING );
	} else {
		$state['object_types'] = array_values( array_unique( array_map( 'strval', $raw['associations'] ) ) );
		$state['show_admin_column'] = (bool) ( $settings['show_admin_column'] ?? false );
		sort( $state['object_types'], SORT_STRING );
	}
	return $state;
}

/** @return string|WP_Error */
function wpagent_acpt_acquire_lock() {
	$option = 'wp_agent_acpt_lock_content_model';
	$token  = wpagent_companion_acquire_option_lock( $option, 300 );
	if ( is_string( $token ) ) {
		return $token;
	}
	return new WP_Error( 'wpagent_acpt_busy', __( 'Die ACPT-Lite-Definitionen werden gerade geändert. Versuche es gleich erneut.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

function wpagent_acpt_release_lock( string $token ): void {
	wpagent_companion_release_option_lock( 'wp_agent_acpt_lock_content_model', $token );
}

/** @return array<string,mixed> */
function wpagent_acpt_output_schema( string $kind ): array {
	$properties = array(
		'slug' => array( 'type' => 'string' ), 'singular_label' => array( 'type' => 'string' ), 'plural_label' => array( 'type' => 'string' ),
		'description' => array( 'type' => 'string' ), 'public' => array( 'type' => 'boolean' ), 'hierarchical' => array( 'type' => 'boolean' ),
		'show_ui' => array( 'type' => 'boolean' ), 'show_in_rest' => array( 'type' => 'boolean' ), 'rewrite_slug' => array( 'type' => 'string' ),
		'content_count' => array( 'type' => 'integer' ), 'meta_group_links' => array( 'type' => 'integer' ),
		'runtime_registered' => array( 'type' => 'boolean' ), 'unmanaged_fields' => array( 'type' => 'array', 'items' => array( 'type' => 'string' ) ),
		'model_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
	);
	if ( 'post_type' === $kind ) {
		$properties['icon'] = array( 'type' => 'string' );
		$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 );
}
