<?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,
		];
	}

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

		$meta_key = TaxonomyHelper::getOrderMetaKey();
		$args     = [
			'taxonomy'   => $taxonomy,
			'hide_empty' => false,
			'fields'     => 'ids',
		];

		if ( TaxonomyHelper::isHierarchical( $taxonomy ) && $hierarchical_mode === 'within_parent' ) {
			$args['parent'] = max( 0, $parent_id );
		}

		$term_ids = get_terms( $args );
		if ( is_wp_error( $term_ids ) || ! is_array( $term_ids ) ) {
			return [
				'success' => false,
				'message' => __( 'Could not load terms for reset.', 'wp-powersuite' ),
				'updated' => 0,
			];
		}

		$updated = self::batchDeleteTermOrder( $term_ids, $meta_key, $taxonomy );

		clean_term_cache( $term_ids, $taxonomy );

		return [
			'success' => true,
			'message' => __( 'Order reset successfully.', 'wp-powersuite' ),
			'updated' => $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 -- dynamic VALUES with prepared params.
		$prepared = $wpdb->prepare( $sql, ...$params );
		$result   = $wpdb->query( $prepared );

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

	/**
	 * @param list<int|string> $term_ids
	 */
	private static function batchDeleteTermOrder( array $term_ids, string $meta_key, string $taxonomy ): int {
		global $wpdb;

		$ids = array_values(
			array_filter(
				array_map( 'intval', $term_ids ),
				static fn( int $id ): bool => $id > 0
			)
		);

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

		$allowed = array_filter(
			$ids,
			static fn( int $term_id ): bool => TaxonomyHelper::userCanEditTerm( $term_id, $taxonomy )
		);

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

		$placeholders = implode( ', ', array_fill( 0, count( $allowed ), '%d' ) );
		$sql          = "DELETE FROM {$wpdb->termmeta} WHERE meta_key = %s AND term_id IN ({$placeholders})";
		$params       = array_merge( [ $meta_key ], $allowed );

		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- placeholders built above.
		$prepared = $wpdb->prepare( $sql, ...$params );
		$result   = $wpdb->query( $prepared );

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