<?php

namespace CustomTaxonomyOrder\Includes;

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

/**
 * Validates and persists term order in term meta.
 */
class OrderSaver {

	/**
	 * @param list<int> $ordered_ids
	 * @return array{success: bool, message: string, updated: int}
	 */
	public static function saveOrder(
		string $taxonomy,
		array $ordered_ids,
		string $reorder_capability,
		int $parent_id = 0,
		string $hierarchical_mode = 'within_parent',
		int $order_offset = 0,
		bool $is_chunk = false
	): array {
		$taxonomy = sanitize_key( $taxonomy );
		if ( ! TaxonomyHelper::userCanReorderTaxonomy( $taxonomy, $reorder_capability ) ) {
			return [
				'success' => false,
				'message' => __( 'You do not have permission to reorder this taxonomy.', 'wp-powersuite' ),
				'updated' => 0,
			];
		}

		$ordered_ids = array_values( array_unique( array_filter( array_map( 'intval', $ordered_ids ) ) ) );
		if ( $ordered_ids === [] ) {
			return [
				'success' => false,
				'message' => __( 'No valid terms were submitted.', 'wp-powersuite' ),
				'updated' => 0,
			];
		}

		$validated = self::validateTermIds( $ordered_ids, $taxonomy, $parent_id, $hierarchical_mode );
		if ( $validated['ids'] === [] ) {
			return [
				'success' => false,
				'message' => $validated['message'] ?? __( 'No valid terms could be updated.', 'wp-powersuite' ),
				'updated' => 0,
			];
		}

		if ( ! $is_chunk && count( $validated['ids'] ) !== count( $ordered_ids ) ) {
			return [
				'success' => false,
				'message' => __( 'One or more terms failed validation. No changes were saved.', 'wp-powersuite' ),
				'updated' => 0,
			];
		}

		$updated = self::batchUpdateTermOrder( $validated['ids'], $order_offset );
		if ( $updated < 1 ) {
			return [
				'success' => false,
				'message' => __( 'Failed to save order. Please try again.', 'wp-powersuite' ),
				'updated' => 0,
			];
		}

		clean_term_cache( $validated['ids'], $taxonomy );

		return [
			'success' => true,
			'message' => __( 'Order saved successfully.', 'wp-powersuite' ),
			'updated' => (int) $updated,
		];
	}

	/**
	 * @param list<int> $ordered_ids
	 * @return array{ids: list<int>, message?: string}
	 */
	private static function validateTermIds(
		array $ordered_ids,
		string $taxonomy,
		int $parent_id,
		string $hierarchical_mode
	): array {
		$valid     = [];
		$parent_id = max( 0, $parent_id );

		foreach ( $ordered_ids as $term_id ) {
			$term = get_term( $term_id, $taxonomy );
			if ( ! $term instanceof \WP_Term || is_wp_error( $term ) ) {
				continue;
			}
			if ( ! TaxonomyHelper::userCanEditTerm( $term_id, $taxonomy ) ) {
				continue;
			}
			if ( TaxonomyHelper::isHierarchical( $taxonomy ) && $hierarchical_mode === 'within_parent' ) {
				if ( (int) $term->parent !== $parent_id ) {
					continue;
				}
			}

			$valid[] = $term_id;
		}

		return [ 'ids' => $valid ];
	}

	/**
	 * @param list<int> $ordered_ids
	 */
	private static function batchUpdateTermOrder( array $ordered_ids, int $order_offset = 0 ): int {
		global $wpdb;

		if ( $ordered_ids === [] ) {
			return 0;
		}

		$meta_key = TaxonomyHelper::getOrderMetaKey();
		$values   = [];
		$params   = [];

		foreach ( $ordered_ids as $position => $term_id ) {
			$values[] = '(%d, %s, %s)';
			$params[] = $term_id;
			$params[] = $meta_key;
			$params[] = (string) ( $order_offset + $position );
		}

		$sql = "INSERT INTO {$wpdb->termmeta} (term_id, meta_key, meta_value) VALUES "
			. implode( ', ', $values )
			. ' ON DUPLICATE KEY UPDATE meta_value = VALUES(meta_value)';

		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- VALUES list uses %d/%s placeholders.
		$result = $wpdb->query( $wpdb->prepare( $sql, ...$params ) );

		return $result === false ? 0 : count( $ordered_ids );
	}
}
