<?php
/**
 * Post revisions cleanup task.
 *
 * @package DatabaseCleanup
 */

namespace DatabaseCleanup\Includes\Tasks;

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

class Task_Post_Revisions extends Abstract_Cleanup_Task {

	public function get_id(): string {
		return 'revisions';
	}

	public function get_label(): string {
		return __( 'Post revisions', 'wppowersuite-lite' );
	}

	public function get_description(): string {
		return __( 'Remove old post revisions while keeping the most recent copies per post.', 'wppowersuite-lite' );
	}

	public function is_safe(): bool {
		return true;
	}

	public function scan( array $settings ): array {
		global $wpdb;

		$keep  = max( 1, absint( $settings['revision_keep_count'] ?? 5 ) );
		$total = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'revision'" );

		$parents = (int) $wpdb->get_var(
			"SELECT COUNT(DISTINCT post_parent) FROM {$wpdb->posts} WHERE post_type = 'revision' AND post_parent > 0"
		);
		$kept_est  = min( $total, $parents * $keep );
		$removable = max( 0, $total - $kept_est );

		$avg_bytes = (int) $wpdb->get_var(
			"SELECT AVG(LENGTH(post_content) + LENGTH(post_title) + 256) FROM {$wpdb->posts} WHERE post_type = 'revision'"
		);

		return [
			'count'            => $removable,
			'estimated_bytes'  => $this->estimate_row_bytes( $removable, max( 512, $avg_bytes ) ),
			'details'          => [
				'total_revisions' => $total,
				'keep_per_post'   => $keep,
			],
		];
	}

	public function clean( array $settings, int $batch_size, mixed $cursor = null ): array {
		global $wpdb;

		$keep    = max( 1, absint( $settings['revision_keep_count'] ?? 5 ) );
		$offset  = is_array( $cursor ) ? absint( $cursor['offset'] ?? 0 ) : 0;
		$deleted = 0;
		$bytes   = 0;

		$parents = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT post_parent FROM {$wpdb->posts}
				WHERE post_type = 'revision' AND post_parent > 0
				GROUP BY post_parent
				HAVING COUNT(*) > %d
				ORDER BY post_parent ASC
				LIMIT %d OFFSET %d",
				$keep,
				max( 1, min( 50, (int) ceil( $batch_size / max( 1, $keep ) ) ) ),
				$offset
			)
		);

		if ( $parents === [] ) {
			return [
				'deleted' => 0,
				'done'    => true,
				'cursor'  => null,
				'bytes'   => 0,
			];
		}

		foreach ( $parents as $parent_id ) {
			$revision_ids = $wpdb->get_col(
				$wpdb->prepare(
					"SELECT ID FROM {$wpdb->posts} WHERE post_type = 'revision' AND post_parent = %d ORDER BY post_date DESC",
					(int) $parent_id
				)
			);

			$to_remove = array_slice( $revision_ids, $keep );

			foreach ( $to_remove as $revision_id ) {
				if ( $deleted >= $batch_size ) {
					break 2;
				}

				$post = get_post( (int) $revision_id );
				if ( ! $post ) {
					continue;
				}

				$bytes += strlen( (string) $post->post_content ) + strlen( (string) $post->post_title ) + 256;

				if ( wp_delete_post_revision( $post ) ) {
					++$deleted;
				}
			}
		}

		$next_offset = $offset + count( $parents );
		$has_more    = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*) FROM (
					SELECT post_parent FROM {$wpdb->posts}
					WHERE post_type = 'revision' AND post_parent > 0
					GROUP BY post_parent HAVING COUNT(*) > %d
				) t",
				$keep
			)
		) > $next_offset;

		return [
			'deleted' => $deleted,
			'done'    => ! $has_more,
			'cursor'  => $has_more ? [ 'offset' => $next_offset ] : null,
			'bytes'   => $bytes,
		];
	}
}
