<?php
/**
 * Action Scheduler cleanup tasks.
 *
 * @package DatabaseCleanup
 */

namespace DatabaseCleanup\Includes\Tasks;

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

abstract class Abstract_Action_Scheduler_Task extends Abstract_Cleanup_Task {

	protected function table_exists(): bool {
		global $wpdb;

		$table = $wpdb->prefix . 'actionscheduler_actions';

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) === $table;
	}

	/**
	 * @return array{table:string,status:string,log:bool}
	 */
	abstract protected function target(): array;

	public function scan( array $settings ): array {
		unset( $settings );

		if ( ! $this->table_exists() ) {
			return [ 'count' => 0, 'estimated_bytes' => 0, 'details' => [] ];
		}

		global $wpdb;
		$target = $this->target();
		$table  = $target['log'] ? $wpdb->prefix . 'actionscheduler_logs' : $wpdb->prefix . 'actionscheduler_actions';
		$where  = $target['log']
			? $wpdb->prepare( 'action_id IN (SELECT action_id FROM `' . $wpdb->prefix . 'actionscheduler_actions` WHERE status = %s)', $target['status'] )
			: $wpdb->prepare( 'status = %s', $target['status'] );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$table}` WHERE {$where}" );

		return [
			'count'           => $count,
			'estimated_bytes' => $this->estimate_row_bytes( $count, $target['log'] ? 512 : 768 ),
			'details'         => [],
		];
	}

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

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

		global $wpdb;
		$target = $this->target();
		$table  = $target['log'] ? $wpdb->prefix . 'actionscheduler_logs' : $wpdb->prefix . 'actionscheduler_actions';
		$id_col = $target['log'] ? 'log_id' : 'action_id';

		if ( $target['log'] ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$ids = $wpdb->get_col(
				$wpdb->prepare(
					"SELECT l.log_id FROM `{$wpdb->prefix}actionscheduler_logs` l
					INNER JOIN `{$wpdb->prefix}actionscheduler_actions` a ON a.action_id = l.action_id
					WHERE a.status = %s
					ORDER BY l.log_id ASC
					LIMIT %d",
					$target['status'],
					$batch_size
				)
			);
		} else {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$ids = $wpdb->get_col(
				$wpdb->prepare(
					"SELECT action_id FROM `{$table}` WHERE status = %s ORDER BY action_id ASC LIMIT %d",
					$target['status'],
					$batch_size
				)
			);
		}

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

		$placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) );
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$deleted = (int) $wpdb->query( $wpdb->prepare( "DELETE FROM `{$table}` WHERE {$id_col} IN ($placeholders)", ...array_map( 'intval', $ids ) ) );

		$remaining = $this->scan( [] )['count'] ?? 0;

		return [
			'deleted' => $deleted,
			'done'    => (int) $remaining === 0,
			'cursor'  => null,
			'bytes'   => $this->estimate_row_bytes( $deleted, $target['log'] ? 512 : 768 ),
		];
	}
}
