<?php

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

/**
 * Resumable scanner for stores that are too large for one web request.
 */
final class ClaimPrism_Background_Scanner {
	const OPTION = 'claimprism_scan_job';
	const HOOK   = 'claimprism_background_batch';
	const LOCK   = 'claimprism_background_lock';
	const BATCH_SIZE = 50;

	/** @var ClaimPrism_Scanner */
	private $scanner;

	public function __construct( ClaimPrism_Scanner $scanner ) {
		$this->scanner = $scanner;
		add_action( self::HOOK, array( $this, 'run_batch' ) );
	}

	public function start_scan() {
		$existing = $this->get_state();
		if ( 'running' === $existing['status'] ) {
			if ( ! wp_next_scheduled( self::HOOK ) ) {
				wp_schedule_single_event( time() + 10, self::HOOK );
			}
			return $existing;
		}

		wp_clear_scheduled_hook( self::HOOK );
		$state = array(
			'status'     => 'running',
			'total'      => $this->count_scannable_content(),
			'processed'  => 0,
			'findings'   => 0,
			'page'       => 1,
			'started_at' => current_time( 'mysql' ),
			'updated_at' => current_time( 'mysql' ),
		);
		update_option( self::OPTION, $state, false );

		if ( ! wp_next_scheduled( self::HOOK ) ) {
			wp_schedule_single_event( time() + 10, self::HOOK );
		}

		return $state;
	}

	public function run_batch() {
		$state = $this->get_state();
		if ( 'running' !== $state['status'] ) {
			return $state;
		}

		if ( get_transient( self::LOCK ) ) {
			if ( ! wp_next_scheduled( self::HOOK ) ) {
				wp_schedule_single_event( time() + MINUTE_IN_SECONDS + 5, self::HOOK );
			}
			return $state;
		}
		set_transient( self::LOCK, 1, MINUTE_IN_SECONDS );

		$query = new WP_Query(
			array(
				'post_type'              => ClaimPrism_Scanner::supported_post_types(),
				'post_status'            => array( 'publish', 'draft', 'private' ),
				'posts_per_page'         => self::BATCH_SIZE,
				'paged'                  => max( 1, (int) $state['page'] ),
				'orderby'                => 'ID',
				'order'                  => 'ASC',
				'no_found_rows'          => true,
				'update_post_meta_cache' => true,
				'update_post_term_cache' => false,
			)
		);

		$batch_findings = 0;
		foreach ( $query->posts as $post ) {
			$batch_findings += count( $this->scanner->scan_post( $post ) );
		}

		$state['processed']  = min( (int) $state['total'], (int) $state['processed'] + count( $query->posts ) );
		$state['findings']   = (int) $state['findings'] + $batch_findings;
		$state['page']       = (int) $state['page'] + 1;
		$state['updated_at'] = current_time( 'mysql' );

		$finished = empty( $query->posts ) || count( $query->posts ) < self::BATCH_SIZE || $state['processed'] >= $state['total'];
		if ( $finished ) {
			$state['status']       = 'completed';
			$state['completed_at'] = current_time( 'mysql' );
			update_option( 'claimprism_last_scan', current_time( 'mysql' ), false );
			wp_clear_scheduled_hook( self::HOOK );
		} elseif ( ! wp_next_scheduled( self::HOOK ) ) {
			wp_schedule_single_event( time() + 10, self::HOOK );
		}

		update_option( self::OPTION, $state, false );
		delete_transient( self::LOCK );

		do_action( 'claimprism_batch_completed', $state, count( $query->posts ), $batch_findings );

		return $state;
	}

	public function get_state() {
		$default = array(
			'status'     => 'idle',
			'total'      => 0,
			'processed'  => 0,
			'findings'   => 0,
			'page'       => 1,
			'started_at' => '',
			'updated_at' => '',
		);
		$state = get_option( self::OPTION, array() );

		return wp_parse_args( is_array( $state ) ? $state : array(), $default );
	}

	private function count_scannable_content() {
		$query = new WP_Query(
			array(
				'post_type'      => ClaimPrism_Scanner::supported_post_types(),
				'post_status'    => array( 'publish', 'draft', 'private' ),
				'posts_per_page' => 1,
				'fields'         => 'ids',
			)
		);

		return (int) $query->found_posts;
	}
}
