<?php

namespace CustomPostOrder\Includes;

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

/**
 * Validates and persists menu_order values.
 */
class OrderSaver {

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

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

		$validated = self::validatePostIds( $ordered_ids, $post_type, $parent_id, $hierarchical_mode );
		if ( $validated['ids'] === [] ) {
			return [
				'success' => false,
				'message' => $validated['message'] ?? __( 'No valid items could be updated.', 'wppowersuite-lite' ),
				'updated' => 0,
			];
		}

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

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

		clean_post_cache( $validated['ids'] );

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

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

		foreach ( $ordered_ids as $post_id ) {
			$post = get_post( $post_id );
			if ( ! $post instanceof \WP_Post ) {
				continue;
			}
			if ( $post->post_type !== $post_type || $post->post_status === 'trash' ) {
				continue;
			}
			if ( ! PostTypeHelper::userCanEditPost( $post_id ) ) {
				continue;
			}
			if ( PostTypeHelper::isHierarchical( $post_type ) && $hierarchical_mode === 'within_parent' ) {
				if ( (int) $post->post_parent !== $parent_id ) {
					continue;
				}
			}

			$valid[] = $post_id;
		}

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

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

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

		$case_parts = [];
		$id_params  = [];

		foreach ( $ordered_ids as $position => $post_id ) {
			$case_parts[] = $wpdb->prepare( 'WHEN %d THEN %d', $post_id, $order_offset + $position );
			$id_params[]  = $post_id;
		}

		$in_placeholders = implode( ', ', array_fill( 0, count( $id_params ), '%d' ) );
		$sql             = "UPDATE {$wpdb->posts} SET menu_order = CASE ID "
			. implode( ' ', $case_parts )
			. " END WHERE ID IN ({$in_placeholders})";

		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- dynamic CASE with prepared fragments.
		$result = $wpdb->query( $wpdb->prepare( $sql, ...$id_params ) );

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