<?php
/**
 * Orphaned post meta cleanup task.
 *
 * @package DatabaseCleanup
 */

namespace DatabaseCleanup\Includes\Tasks;

use DatabaseCleanup\Includes\Protected_Items;

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

class Task_Orphaned_Post_Meta extends Abstract_Cleanup_Task {

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

	public function get_label(): string {
		return __( 'Orphaned post meta', 'wp-powersuite' );
	}

	public function get_description(): string {
		return __( 'Delete postmeta rows whose parent post no longer exists (skips post_id = 0 and protected keys).', 'wp-powersuite' );
	}

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

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

		unset( $settings );

		// Exclude post_id = 0 — some plugins store global/settings rows that way.
		$count = (int) $wpdb->get_var(
			"SELECT COUNT(*) FROM {$wpdb->postmeta} pm
			LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
			WHERE p.ID IS NULL AND pm.post_id > 0"
		);

		return [
			'count'           => $count,
			'estimated_bytes' => $this->estimate_row_bytes( $count, 512 ),
			'details'         => [],
		];
	}

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

		unset( $settings, $cursor );

		$meta_ids = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT pm.meta_id FROM {$wpdb->postmeta} pm
				LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
				WHERE p.ID IS NULL AND pm.post_id > 0
				ORDER BY pm.meta_id ASC
				LIMIT %d",
				$batch_size
			)
		);

		$deleted = 0;
		$bytes   = 0;

		foreach ( (array) $meta_ids as $id ) {
			$id = (int) $id;
			if ( $id <= 0 ) {
				continue;
			}

			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$row = $wpdb->get_row( $wpdb->prepare( "SELECT meta_key, meta_value FROM {$wpdb->postmeta} WHERE meta_id = %d", $id ), ARRAY_A );
			if ( ! is_array( $row ) ) {
				continue;
			}

			$key = (string) ( $row['meta_key'] ?? '' );
			if ( Protected_Items::is_protected_meta_key( $key, 'post' ) ) {
				continue;
			}

			$bytes += strlen( $key ) + strlen( (string) ( $row['meta_value'] ?? '' ) ) + 32;

			if ( delete_metadata_by_mid( 'post', $id ) ) {
				++$deleted;
			}
		}

		$remaining = (int) $wpdb->get_var(
			"SELECT COUNT(*) FROM {$wpdb->postmeta} pm
			LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
			WHERE p.ID IS NULL AND pm.post_id > 0"
		);

		return [
			'deleted' => $deleted,
			'done'    => $remaining === 0 || $meta_ids === [],
			'cursor'  => null,
			'bytes'   => $bytes,
		];
	}
}
