<?php
/** Input validation for persistent MB Relationships definitions. */

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

/** @return bool */
function wpagent_metabox_relationship_boolean( $value ): bool {
	return is_bool( $value ) || in_array( $value, array( 'true', 'false', 0, 1, '0', '1' ), true );
}

function wpagent_metabox_relationship_signature_boolean( $value, bool &$valid ): bool {
	$valid = wpagent_metabox_relationship_boolean( $value );
	return in_array( $value, array( true, 1, '1', 'true' ), true );
}

/** @return array<string,mixed> */
function wpagent_metabox_relationship_side_signature( $side ): array {
	if ( is_string( $side ) ) {
		return array( 'valid' => '' !== $side, 'object_type' => 'post', 'target' => array( $side ), 'has_one_relationship' => false );
	}
	$valid = is_array( $side );
	$side  = $valid ? $side : array();
	$raw_object_type = $side['object_type'] ?? 'post';
	if ( ! is_string( $raw_object_type ) || ! in_array( $raw_object_type, array( 'post', 'term', 'user' ), true ) ) {
		$valid = false;
	}
	$object_type = is_string( $raw_object_type ) ? $raw_object_type : '';
	$field = $side['field'] ?? array();
	if ( ! is_array( $field ) ) {
		$valid = false;
		$field = array();
	}
	$target = array();
	if ( 'post' === $object_type ) {
		$target = $field['post_type'] ?? $side['post_type'] ?? array();
	} elseif ( 'term' === $object_type ) {
		$target = $field['taxonomy'] ?? $side['taxonomy'] ?? array();
	}
	$target = is_array( $target ) ? $target : array( $target );
	$normalized_target = array();
	foreach ( $target as $item ) {
		if ( ! is_string( $item ) || '' === $item ) {
			$valid = false;
			continue;
		}
		$normalized_target[] = $item;
	}
	$normalized_target = array_values( array_unique( $normalized_target ) );
	sort( $normalized_target, SORT_STRING );
	if ( 'user' !== $object_type && array() === $normalized_target ) {
		$valid = false;
	}
	$boolean_valid = true;
	$has_one = wpagent_metabox_relationship_signature_boolean( $side['has_one_relationship'] ?? false, $boolean_valid );
	return array(
		'valid' => $valid && $boolean_valid,
		'object_type' => $object_type,
		'target' => $normalized_target,
		'has_one_relationship' => $has_one,
	);
}

/** @return array<string,mixed> */
function wpagent_metabox_relationship_runtime_signature( array $settings ): array {
	$id = $settings['id'] ?? '';
	$boolean_valid = true;
	$reciprocal = wpagent_metabox_relationship_signature_boolean( $settings['reciprocal'] ?? false, $boolean_valid );
	return array(
		'valid' => is_string( $id ) && '' !== $id && $boolean_valid,
		'id' => is_string( $id ) ? $id : '',
		'reciprocal' => $reciprocal,
		'from' => wpagent_metabox_relationship_side_signature( $settings['from'] ?? array() ),
		'to' => wpagent_metabox_relationship_side_signature( $settings['to'] ?? array() ),
	);
}

function wpagent_metabox_relationship_string_length( string $value ): int {
	return function_exists( 'mb_strlen' ) ? mb_strlen( $value ) : strlen( $value );
}

/** @return string|WP_Error */
function wpagent_metabox_relationship_text( $value, string $field, int $max_length, bool $allow_empty = true ) {
	if ( ! is_string( $value ) ) {
		return new WP_Error( 'wpagent_metabox_relationship_type_invalid', sprintf( /* translators: %s: machine-readable field name. */ __( '%s muss Text sein.', 'wp-agent-companion' ), $field ), array( 'status' => 400 ) );
	}
	$value = sanitize_text_field( $value );
	if ( ( ! $allow_empty && '' === $value ) || wpagent_metabox_relationship_string_length( $value ) > $max_length ) {
		return new WP_Error( 'wpagent_metabox_relationship_type_invalid', sprintf( /* translators: %s: machine-readable field name. */ __( '%s ist leer oder zu lang.', 'wp-agent-companion' ), $field ), array( 'status' => 400 ) );
	}
	return $value;
}

/** @return mixed|WP_Error */
function wpagent_metabox_relationship_query_value( $value, int $depth = 0, int &$items = 0 ) {
	++$items;
	if ( $items > 100 ) {
		return new WP_Error( 'wpagent_metabox_relationship_query_invalid', __( 'Die Query-Einstellungen sind zu tief oder enthalten zu viele Werte.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( is_array( $value ) ) {
		if ( $depth > 0 ) {
			return new WP_Error( 'wpagent_metabox_relationship_query_invalid', __( 'Die Query-Einstellungen dürfen nur ein flaches Objekt enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$clean = array();
		foreach ( $value as $key => $item ) {
			if ( is_string( $key ) && ! preg_match( '/^[A-Za-z0-9_.-]{1,80}$/', $key ) ) {
				return new WP_Error( 'wpagent_metabox_relationship_query_invalid', __( 'Ein Query-Schlüssel ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
			}
			$parsed = wpagent_metabox_relationship_query_value( $item, $depth + 1, $items );
			if ( is_wp_error( $parsed ) ) {
				return $parsed;
			}
			$clean[ $key ] = $parsed;
		}
		return $clean;
	}
	if ( is_string( $value ) ) {
		if ( strlen( $value ) > 500 ) {
			return new WP_Error( 'wpagent_metabox_relationship_query_invalid', __( 'Ein Query-Textwert ist zu lang.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		return sanitize_text_field( $value );
	}
	if ( is_int( $value ) || is_float( $value ) || is_bool( $value ) || null === $value ) {
		return $value;
	}
	return new WP_Error( 'wpagent_metabox_relationship_query_invalid', __( 'Ein Query-Wert besitzt einen nicht unterstützten Typ.', 'wp-agent-companion' ), array( 'status' => 400 ) );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_metabox_validate_relationship_side( array $side ) {
	$allowed = array( 'object_type', 'post_type', 'taxonomy', 'has_one_relationship', 'empty_message', 'admin_filter', 'admin_column', 'meta_box', 'field' );
	if ( array_diff( array_keys( $side ), $allowed ) ) {
		return new WP_Error( 'wpagent_metabox_relationship_unknown_setting', __( 'Eine Beziehungsseite enthält nicht freigegebene Einstellungen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$object_type = is_string( $side['object_type'] ?? null ) ? $side['object_type'] : 'post';
	if ( ! in_array( $object_type, array( 'post', 'term', 'user' ), true ) ) {
		return new WP_Error( 'wpagent_metabox_relationship_object_invalid', __( 'Der Beziehungstyp muss post, term oder user sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$side['object_type'] = $object_type;
	if ( 'post' === $object_type ) {
		$post_type = $side['post_type'] ?? null;
		if ( ! is_string( $post_type ) || ! preg_match( '/^[a-z0-9_-]{1,20}$/', $post_type ) || ! post_type_exists( $post_type ) ) {
			return new WP_Error( 'wpagent_metabox_relationship_object_invalid', __( 'Der Inhaltstyp einer Beziehungsseite existiert nicht.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$side['post_type'] = $post_type;
	}
	if ( 'term' === $object_type ) {
		$taxonomy = $side['taxonomy'] ?? null;
		if ( ! is_string( $taxonomy ) || ! preg_match( '/^[a-z0-9_-]{1,32}$/', $taxonomy ) || ! taxonomy_exists( $taxonomy ) ) {
			return new WP_Error( 'wpagent_metabox_relationship_object_invalid', __( 'Die Taxonomie einer Beziehungsseite existiert nicht.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$side['taxonomy'] = $taxonomy;
	}
	foreach ( array( 'has_one_relationship', 'admin_filter' ) as $key ) {
		if ( array_key_exists( $key, $side ) && ! wpagent_metabox_relationship_boolean( $side[ $key ] ) ) {
			return new WP_Error( 'wpagent_metabox_relationship_type_invalid', __( 'Eine boolesche Beziehungseinstellung ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
	}
	if ( array_key_exists( 'empty_message', $side ) ) {
		$empty_message = wpagent_metabox_relationship_text( $side['empty_message'], 'empty_message', 500 );
		if ( is_wp_error( $empty_message ) ) {
			return $empty_message;
		}
		$side['empty_message'] = $empty_message;
	}
	$admin_column = $side['admin_column'] ?? array();
	if ( ! is_array( $admin_column ) || array_diff( array_keys( $admin_column ), array( 'enable', 'position', 'title', 'link' ) ) ) {
		return new WP_Error( 'wpagent_metabox_relationship_unknown_setting', __( 'Die Admin-Spalteneinstellungen sind ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( isset( $admin_column['enable'] ) && ! wpagent_metabox_relationship_boolean( $admin_column['enable'] ) ) {
		return new WP_Error( 'wpagent_metabox_relationship_type_invalid', __( 'Die Admin-Spaltenfreigabe ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( isset( $admin_column['link'] ) && ! in_array( $admin_column['link'], array( 'view', 'edit', 'false', false ), true ) ) {
		return new WP_Error( 'wpagent_metabox_relationship_type_invalid', __( 'Der Linktyp der Admin-Spalte ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( isset( $admin_column['title'] ) ) {
		$title = wpagent_metabox_relationship_text( $admin_column['title'], 'admin_column.title', 200 );
		if ( is_wp_error( $title ) ) {
			return $title;
		}
		$admin_column['title'] = $title;
	}
	if ( isset( $admin_column['position'] ) ) {
		$items = 0;
		$position = wpagent_metabox_relationship_query_value( $admin_column['position'], 0, $items );
		if ( is_wp_error( $position ) ) {
			return $position;
		}
		$admin_column['position'] = $position;
	}
	$side['admin_column'] = $admin_column;
	$meta_box = $side['meta_box'] ?? array();
	if ( ! is_array( $meta_box ) || array_diff( array_keys( $meta_box ), array( 'title', 'context', 'priority', 'style', 'closed', 'class' ) ) ) {
		return new WP_Error( 'wpagent_metabox_relationship_unknown_setting', __( 'Die Meta-Box-Einstellungen einer Beziehungsseite sind ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$enums = array( 'context' => array( 'normal', 'side' ), 'priority' => array( 'low', 'high' ), 'style' => array( 'default', 'seamless' ) );
	foreach ( $enums as $key => $values ) {
		if ( isset( $meta_box[ $key ] ) && ! in_array( $meta_box[ $key ], $values, true ) ) {
			return new WP_Error( 'wpagent_metabox_relationship_type_invalid', __( 'Eine Meta-Box-Auswahleinstellung ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
	}
	if ( isset( $meta_box['closed'] ) && ! wpagent_metabox_relationship_boolean( $meta_box['closed'] ) ) {
		return new WP_Error( 'wpagent_metabox_relationship_type_invalid', __( 'Der eingeklappte Meta-Box-Zustand ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	foreach ( array( 'title', 'class' ) as $key ) {
		if ( isset( $meta_box[ $key ] ) ) {
			$value = wpagent_metabox_relationship_text( $meta_box[ $key ], 'meta_box.' . $key, 'title' === $key ? 200 : 500 );
			if ( is_wp_error( $value ) ) {
				return $value;
			}
			$meta_box[ $key ] = $value;
		}
	}
	$side['meta_box'] = $meta_box;
	$field = $side['field'] ?? array();
	if ( ! is_array( $field ) || array_diff( array_keys( $field ), array( 'name', 'label_description', 'desc', 'placeholder', 'add_new', 'query_args', 'max_clone', 'add_button', 'before', 'after', 'class' ) ) ) {
		return new WP_Error( 'wpagent_metabox_relationship_unknown_setting', __( 'Die Feldausgabe einer Beziehungsseite enthält nicht freigegebene Einstellungen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( isset( $field['add_new'] ) && ! wpagent_metabox_relationship_boolean( $field['add_new'] ) ) {
		return new WP_Error( 'wpagent_metabox_relationship_type_invalid', __( 'Die Feldfreigabe zum Hinzufügen ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( isset( $field['max_clone'] ) && ( ! is_int( $field['max_clone'] ) && ( ! is_string( $field['max_clone'] ) || ! preg_match( '/^(?:0|[1-9][0-9]{0,3})$/', $field['max_clone'] ) ) || (int) $field['max_clone'] < 0 || (int) $field['max_clone'] > 1000 ) ) {
		return new WP_Error( 'wpagent_metabox_relationship_type_invalid', __( 'Die maximale Verbindungszahl muss zwischen 0 und 1.000 liegen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$text_limits = array( 'name' => 200, 'label_description' => 500, 'desc' => 500, 'placeholder' => 200, 'add_button' => 200, 'class' => 500 );
	foreach ( $text_limits as $key => $limit ) {
		if ( isset( $field[ $key ] ) ) {
			$value = wpagent_metabox_relationship_text( $field[ $key ], 'field.' . $key, $limit );
			if ( is_wp_error( $value ) ) {
				return $value;
			}
			$field[ $key ] = $value;
		}
	}
	foreach ( array( 'before', 'after' ) as $key ) {
		if ( isset( $field[ $key ] ) ) {
			if ( ! is_string( $field[ $key ] ) || strlen( $field[ $key ] ) > 10000 ) {
				return new WP_Error( 'wpagent_metabox_relationship_type_invalid', __( 'Ein HTML-Feld der Beziehungsseite ist ungültig oder zu lang.', 'wp-agent-companion' ), array( 'status' => 400 ) );
			}
			$field[ $key ] = wp_kses_post( (string) $field[ $key ] );
		}
	}
	if ( isset( $field['query_args'] ) ) {
		$items = 0;
		$query_args = wpagent_metabox_relationship_query_value( $field['query_args'], 0, $items );
		if ( is_wp_error( $query_args ) ) {
			return $query_args;
		}
		$field['query_args'] = $query_args;
	}
	$side['field'] = $field;
	return $side;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_metabox_validate_relationship_settings( array $settings ) {
	if ( ! class_exists( 'MBB\\Extensions\\Relationships\\Parsers\\Relationship' ) || ! class_exists( 'MB_Relationships_API' ) ) {
		return new WP_Error( 'wpagent_metabox_relationships_missing', __( 'MB Relationships und seine Builder-Integration sind nicht aktiv.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$json = wp_json_encode( $settings );
	if ( false === $json || strlen( $json ) > 65536 ) {
		return new WP_Error( 'wpagent_metabox_relationship_too_large', __( 'Die Beziehungsdefinition ist ungültig oder zu groß.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( array_diff( array_keys( $settings ), array( 'id', 'from', 'to', 'reciprocal', 'delete_data' ) ) ) {
		return new WP_Error( 'wpagent_metabox_relationship_unknown_setting', __( 'Die Beziehungsdefinition enthält nicht freigegebene Einstellungen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$id = is_string( $settings['id'] ?? null ) ? $settings['id'] : '';
	if ( ! preg_match( '/^[a-z][a-z0-9_-]{0,43}$/', $id ) || sanitize_title( $id ) !== $id ) {
		return new WP_Error( 'wpagent_metabox_relationship_id_invalid', __( 'Die Beziehungs-ID darf höchstens 44 Zeichen lang sein, muss mit einem Kleinbuchstaben beginnen und darf nur Kleinbuchstaben, Zahlen, Unterstriche und Bindestriche enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	foreach ( array( 'reciprocal', 'delete_data' ) as $key ) {
		if ( isset( $settings[ $key ] ) && ! wpagent_metabox_relationship_boolean( $settings[ $key ] ) ) {
			return new WP_Error( 'wpagent_metabox_relationship_type_invalid', __( 'Eine boolesche Definitionseinstellung ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
	}
	foreach ( array( 'from', 'to' ) as $side_name ) {
		if ( ! is_array( $settings[ $side_name ] ?? null ) ) {
			return new WP_Error( 'wpagent_metabox_relationship_side_invalid', __( 'Die Beziehung benötigt gültige from- und to-Seiten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$side = wpagent_metabox_validate_relationship_side( $settings[ $side_name ] );
		if ( is_wp_error( $side ) ) {
			return $side;
		}
		$settings[ $side_name ] = $side;
	}
	try {
		$settings_parser = new MBB\Extensions\Relationships\Parsers\Relationship( $settings );
		$settings_parser->parse_boolean_values()->parse_numeric_values();
		$normalized = $settings_parser->get_settings();
		$relationship_parser = new MBB\Extensions\Relationships\Parsers\Relationship( $normalized );
		$relationship_parser->parse();
		$relationship = $relationship_parser->get_settings();
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_metabox_relationship_invalid', __( 'Meta Box konnte die Beziehungsdefinition nicht normalisieren.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( ! is_array( $normalized ) || ! is_array( $relationship ) || $id !== ( $relationship['id'] ?? '' ) ) {
		return new WP_Error( 'wpagent_metabox_relationship_invalid', __( 'Die normalisierte Beziehungsdefinition ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return array( 'settings' => $normalized, 'relationship' => $relationship );
}
