<?php
/** Shared state, validation and locking for WooCommerce product shipping classes. */

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

define( 'WPAGENT_WC_SHIPPING_CLASS_TAXONOMY', 'product_shipping_class' );
define( 'WPAGENT_WC_SHIPPING_CLASS_LOCK_OPTION', 'wp_agent_wc_shipping_class_lock' );
define( 'WPAGENT_WC_SHIPPING_CLASS_LOCK_TTL', 60 );

function wpagent_perm_wc_manage_shipping_classes( $input = null ): bool {
	unset( $input );
	return current_user_can( 'manage_product_terms' );
}

/** @return true|WP_Error */
function wpagent_wc_require_shipping_class_permission() {
	if ( ! current_user_can( 'manage_product_terms' ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Du darfst WooCommerce-Produkt-Versandklassen nicht verwalten.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_wc_require_shipping_class_runtime() {
	$woocommerce = wpagent_require_woocommerce();
	if ( is_wp_error( $woocommerce ) ) {
		return $woocommerce;
	}
	if ( ! taxonomy_exists( WPAGENT_WC_SHIPPING_CLASS_TAXONOMY ) ) {
		return new WP_Error( 'wpagent_wc_shipping_classes_unavailable', __( 'Die WooCommerce-Versandklassen-Taxonomie ist nicht verfügbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return true;
}

/** @return WP_Term|null */
function wpagent_wc_shipping_class_record( int $shipping_class_id ) {
	$term = get_term( $shipping_class_id, WPAGENT_WC_SHIPPING_CLASS_TAXONOMY );
	return $term instanceof WP_Term ? $term : null;
}

/** @return true|WP_Error */
function wpagent_wc_shipping_class_require_unique_fields( array $next, int $excluding_id = 0 ) {
	foreach ( array( 'name', 'slug' ) as $field ) {
		$term = get_term_by( $field, (string) $next[ $field ], WPAGENT_WC_SHIPPING_CLASS_TAXONOMY );
		if ( $term instanceof WP_Term && (int) $term->term_id !== $excluding_id ) {
			return new WP_Error(
				'wpagent_wc_shipping_class_duplicate',
				__( 'Name oder Slug der Versandklasse wird bereits verwendet.', 'wp-agent-companion' ),
				array( 'status' => 409, 'field' => $field, 'shipping_class_id' => (int) $term->term_id )
			);
		}
	}
	return true;
}

/**
 * Counts every object-term relationship, including products, variations, drafts and custom post types.
 *
 * @param WP_Term[] $terms
 * @return array<int,int>|WP_Error Maps term-taxonomy IDs to assigned-object counts.
 */
function wpagent_wc_shipping_class_relation_counts( array $terms ) {
	$term_taxonomy_ids = array_values(
		array_unique(
			array_filter(
				array_map(
					static fn( $term ): int => $term instanceof WP_Term ? absint( $term->term_taxonomy_id ) : 0,
					$terms
				)
			)
		)
	);
	if ( array() === $term_taxonomy_ids ) {
		return array();
	}

	global $wpdb;
	$placeholders = implode( ', ', array_fill( 0, count( $term_taxonomy_ids ), '%d' ) );
	// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $placeholders contains only fixed %d placeholders.
	$sql = "SELECT term_taxonomy_id, COUNT(*) AS assigned_object_count FROM {$wpdb->term_relationships} WHERE term_taxonomy_id IN ({$placeholders}) GROUP BY term_taxonomy_id";
	$wpdb->last_error = '';
	$rows             = $wpdb->get_results( $wpdb->prepare( $sql, $term_taxonomy_ids ), ARRAY_A );
	if ( '' !== $wpdb->last_error ) {
		return new WP_Error( 'wpagent_wc_shipping_class_relation_check_failed', __( 'Die Belegung der Versandklasse konnte nicht sicher geprüft werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}

	$counts = array_fill_keys( $term_taxonomy_ids, 0 );
	foreach ( (array) $rows as $row ) {
		$counts[ absint( $row['term_taxonomy_id'] ?? 0 ) ] = max( 0, absint( $row['assigned_object_count'] ?? 0 ) );
	}
	return $counts;
}

/** @return array<string,mixed> */
function wpagent_wc_shipping_class_payload( WP_Term $term, int $assigned_object_count ): array {
	return wpagent_wc_shipping_class_payload_from_values(
		(int) $term->term_id,
		array(
			'name'        => (string) $term->name,
			'slug'        => (string) $term->slug,
			'description' => (string) $term->description,
		),
		$assigned_object_count
	);
}

/** @return array<string,mixed> */
function wpagent_wc_shipping_class_payload_from_values( int $shipping_class_id, array $fields, int $assigned_object_count ): array {
	$payload = array(
		'shipping_class_id'     => $shipping_class_id,
		'name'                  => (string) $fields['name'],
		'slug'                  => (string) $fields['slug'],
		'description'           => (string) $fields['description'],
		'assigned_object_count' => max( 0, $assigned_object_count ),
		'in_use'                => $assigned_object_count > 0,
	);
	$payload['state_hash'] = hash( 'sha256', (string) wp_json_encode( $payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) );
	return $payload;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_wc_shipping_class_payload_for_term( WP_Term $term ) {
	$counts = wpagent_wc_shipping_class_relation_counts( array( $term ) );
	if ( is_wp_error( $counts ) ) {
		return $counts;
	}
	return wpagent_wc_shipping_class_payload( $term, (int) ( $counts[ (int) $term->term_taxonomy_id ] ?? 0 ) );
}

/** @return array<string,mixed> */
function wpagent_wc_shipping_class_schema(): array {
	return array(
		'type'                 => 'object',
		'properties'           => array(
			'shipping_class_id' => array( 'type' => 'integer', 'minimum' => 1 ),
			'name'              => array( 'type' => 'string' ),
			'slug'              => array( 'type' => 'string' ),
			'description'       => array( 'type' => 'string' ),
			'assigned_object_count' => array( 'type' => 'integer', 'minimum' => 0 ),
			'in_use'               => array( 'type' => 'boolean' ),
			'state_hash'        => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
		),
		'required'             => array( 'shipping_class_id', 'name', 'slug', 'description', 'assigned_object_count', 'in_use', 'state_hash' ),
		'additionalProperties' => false,
	);
}

/** @return array<string,mixed>|WP_Error */
function wpagent_wc_validate_shipping_class_fields( array $input, bool $creating, ?array $previous = null ) {
	$allowed = array( 'name', 'slug', 'description' );
	$fields  = array_intersect_key( $input, array_flip( $allowed ) );
	if ( ! $creating && array() === $fields ) {
		return new WP_Error( 'wpagent_wc_shipping_class_no_fields', __( 'Kein zu aktualisierendes Versandklassenfeld übergeben.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$next = $creating
		? array( 'name' => '', 'slug' => '', 'description' => '' )
		: array( 'name' => (string) $previous['name'], 'slug' => (string) $previous['slug'], 'description' => (string) $previous['description'] );
	if ( array_key_exists( 'name', $fields ) ) {
		$next['name'] = trim( sanitize_text_field( (string) $fields['name'] ) );
	}
	if ( '' === $next['name'] ) {
		return new WP_Error( 'wpagent_wc_shipping_class_name_required', __( 'Eine Versandklasse benötigt einen Namen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( strlen( $next['name'] ) > 200 ) {
		return new WP_Error( 'wpagent_wc_shipping_class_name_too_long', __( 'Der Versandklassenname darf höchstens 200 Byte lang sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( array_key_exists( 'slug', $fields ) ) {
		$next['slug'] = sanitize_title( (string) $fields['slug'] );
	}
	if ( $creating && '' === $next['slug'] ) {
		$next['slug'] = sanitize_title( $next['name'] );
	}
	if ( '' === $next['slug'] || strlen( $next['slug'] ) > 200 ) {
		return new WP_Error( 'wpagent_wc_shipping_class_slug_invalid', __( 'Der Versandklassen-Slug ist leer oder zu lang.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( array_key_exists( 'description', $fields ) ) {
		$next['description'] = sanitize_textarea_field( (string) $fields['description'] );
	}
	if ( strlen( $next['description'] ) > 5000 ) {
		return new WP_Error( 'wpagent_wc_shipping_class_description_too_long', __( 'Die Versandklassenbeschreibung darf höchstens 5.000 Byte lang sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return $next;
}

/** @return string|WP_Error */
function wpagent_wc_shipping_class_acquire_lock() {
	$token = wpagent_companion_acquire_option_lock( WPAGENT_WC_SHIPPING_CLASS_LOCK_OPTION, WPAGENT_WC_SHIPPING_CLASS_LOCK_TTL );
	if ( is_string( $token ) ) {
		return $token;
	}
	return new WP_Error( 'wpagent_wc_shipping_class_locked', __( 'Die WooCommerce-Versandklassen werden gerade durch einen anderen Vorgang geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

function wpagent_wc_shipping_class_release_lock( string $token ): void {
	wpagent_companion_release_option_lock( WPAGENT_WC_SHIPPING_CLASS_LOCK_OPTION, $token );
}

function wpagent_wc_shipping_class_refresh( int $shipping_class_id ): void {
	clean_term_cache( $shipping_class_id, WPAGENT_WC_SHIPPING_CLASS_TAXONOMY );
}
