<?php
/**
 * Base class for post-based better block editor migrations within The7.
 */

namespace The7\Mods\Theme_Update\Base\Block_Editor;

use The7\Mods\Theme_Update\Base\Migration_Base;

defined( 'ABSPATH' ) || exit;

abstract class Post_Migration_Base extends Migration_Base {

    /**
     * Number of posts to process per batch.
     */
    protected const BATCH_SIZE = 100;

    /**
     * Migrate a batch of posts, starting from the given offset.
     *
     * @param int $offset
     * @return array|false Next migration callback or false if complete.
     */
	public static function migrate(int $offset = 0) {
        $post_ids = static::get_post_ids_to_migrate( $offset, static::BATCH_SIZE );

        //migration complete.
        if ( empty( $post_ids ) ) {
            return false;
        }

        foreach ( $post_ids as $post_id ) {
            $post = get_post( $post_id );
            if ( $post ) {
                static::migrate_post( $post );
            }
            clean_post_cache( $post_id );
        }

        $next_offset = $offset + count( $post_ids );
        $total       = static::get_total_posts_to_migrate();

        // If we processed less than BATCH_SIZE we reached the end
        if ( count( $post_ids ) < static::BATCH_SIZE || $next_offset >= $total ) {
            return false;
        }
        // Requeue next batch by returning the next callback to the updater.
        return [
            'callback' => [ static::class, 'migrate' ],
            'args'     => [ $next_offset ],
        ];
	}

    /**
     * Total post count. (Cached for performance, as this doesn't change during migration))
     * @return int
     */
    protected static function get_total_posts_to_migrate(): int {
        global $wpdb;
        return (int) $wpdb->get_var(
            "SELECT COUNT(ID)
             FROM {$wpdb->posts}
             WHERE post_type != 'attachment'
               AND post_content != ''"
        );
    }

	public static function migrate_post( $post ): void {
		$updated_post = static::process_post( clone $post );

		static::update_post( $post, $updated_post );
	}


    /**
     * Fetch a slice of IDs, to avoid loading the full set into memory.
     *
     * @param int $offset
     * @param int $limit
     * @return int[]
     */
    protected static function get_post_ids_to_migrate( int $offset = 0, int $limit = 0 ): array {
        global $wpdb;

        $limit_clause = $limit > 0
            ? $wpdb->prepare( 'LIMIT %d OFFSET %d', $limit, $offset )
            : '';

        return array_map(
            'intval',
            $wpdb->get_col(
                "SELECT ID FROM {$wpdb->posts} WHERE post_type != 'attachment' AND post_content != '' ORDER BY ID ASC {$limit_clause}"
            )
        );
    }

	abstract protected static function process_post( $post );

	protected static function update_post( $original_post, $migrated_post ): void {
		$changes = [];

		foreach ( get_object_vars( $original_post ) as $key => $value ) {
			if ( $migrated_post->$key !== $value ) {
				$changes[ $key ] = $migrated_post->$key;
			}
		}

		if ( empty( $changes ) ) {
			return;
		}

		// Prevent revision creation.
		add_filter( 'wp_revisions_to_keep', '__return_zero', 999 );

		wp_update_post(
			array_merge(
				$changes,
				[
					'ID'           => $original_post->ID,
					'post_content' => wp_slash( $changes['post_content'] ?? $migrated_post->post_content ),
				]
			)
		);

		remove_filter( 'wp_revisions_to_keep', '__return_zero', 999 );
	}
}
