<?php

declare(strict_types=1);

namespace SecurWP\Modules\Scanner\Queue\Handler;

use SecurWP\Modules\Scanner\Queue\ScanOrchestrator;
use SecurWP\Modules\Scanner\Queue\ScanQueue;
use SecurWP\Modules\Scanner\Queue\ScanWorker;
use SecurWP\Modules\Scanner\ScanController;

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

/**
 * Executes one bounded file-worker slice.
 */
class ScanWorkerHandler {

	private const EXECUTION_LIMIT = 20;

	/**
	 * @param array<string,mixed> $task
	 * @param string              $scan_id
	 * @return TaskResult
	 */
	public function handle( array $task, string $scan_id ): TaskResult {
		$queue = ScanQueue::get_job( $scan_id );
		if ( ! is_array( $queue ) || ! in_array( (string) ( $queue['status'] ?? '' ), [ 'pending', 'running' ], true ) ) {
			return TaskResult::completed( [ 'status' => 'idle' ] );
		}

		if ( 'pending' === ( $queue['status'] ?? '' ) ) {
			ScanQueue::start();
		}

		if ( (int) ( $queue['processed'] ?? 0 ) === 0 && ! get_transient( 'securwp_scan_preflight_' . $scan_id ) ) {
			$preflight = ScanWorker::run_preflight_check( $scan_id );
			if ( is_wp_error( $preflight ) ) {
				$error_code = (string) $preflight->get_error_code();
				$message = (string) $preflight->get_error_message();
				ScanQueue::fail(
					'Cloud service is temporarily unavailable. Please try again later.',
					[
						'error_code'        => sanitize_key( $error_code !== '' ? $error_code : 'preflight_failed' ),
						'stage'             => 'preflight',
						'retryable'         => false,
						'status_code'       => 503,
						'technical_message' => sanitize_text_field( $message ),
					]
				);

				return TaskResult::completed(
					[
						'status'  => 'failed',
						'message' => $message,
					]
				);
			}

			set_transient( 'securwp_scan_preflight_' . $scan_id, true, HOUR_IN_SECONDS );
		}

		$controller = $this->ensure_controller();
		if ( ! $controller instanceof ScanController ) {
			ScanQueue::fail(
				'Scanner controller is unavailable in worker context. Please restart the scan.',
				[
					'error_code'        => 'controller_unavailable',
					'stage'             => 'controller_bootstrap',
					'retryable'         => true,
					'status_code'       => 500,
					'technical_message' => 'ScanController bootstrap failed for task queue worker',
				]
			);

			return TaskResult::completed(
				[
					'status'  => 'failed',
					'message' => 'Controller unavailable',
				]
			);
		}

		try {
			$orchestrator = new ScanOrchestrator( $scan_id, $controller );
			$orchestrator->set_execution_limit( self::EXECUTION_LIMIT );
			$orchestrator->set_inline_system_checks_enabled( false );

			$stats = $orchestrator->run_task_queue_scan_worker_slice();
			$follow_ups = [];
			$upload_payload = [
				'max_files'    => 50,
				'max_size_mb'  => 8,
			];
			$next_file_worker_available_at = $orchestrator->get_next_file_worker_available_at();

			if ( $orchestrator->has_file_worker_work() ) {
				$follow_ups[] = [
					'task_type'    => 'scan_worker',
					'payload'      => [ 'budget_seconds' => self::EXECUTION_LIMIT ],
					'available_at' => $next_file_worker_available_at,
					'priority'     => 100,
				];
			}

			if ( $orchestrator->has_ready_upload_batch( $upload_payload ) ) {
				$follow_ups[] = [
					'task_type'    => 'upload_batch',
					'payload'      => $upload_payload,
					'available_at' => time(),
					'priority'     => 70,
				];
			}

			if ( $orchestrator->is_ready_for_finalize() ) {
				$follow_ups[] = [
					'task_type'    => 'finalize',
					'payload'      => [ 'scan_id' => $scan_id ],
					'available_at' => time(),
					'priority'     => 10,
					'dedupe_key'   => 'finalize',
				];
			}

			$queue_after = ScanQueue::get_job( $scan_id );

			return TaskResult::completed(
				[
					'status' => is_array( $queue_after ) ? (string) ( $queue_after['status'] ?? 'running' ) : 'running',
					'stats'  => $stats,
				],
				$follow_ups
			);
		} catch ( \Throwable $e ) {
			return TaskResult::failed(
				sanitize_text_field( $e->getMessage() ),
				[
					'status' => 'exception',
				]
			);
		}
	}

	/**
	 * Build a controller on demand for background task execution.
	 *
	 * @return ScanController|null
	 */
	private function ensure_controller(): ?ScanController {
		if ( ! class_exists( ScanController::class ) ) {
			require_once dirname( __DIR__, 2 ) . '/ScanController.php';
		}

		if ( ! class_exists( ScanController::class ) || ! method_exists( ScanController::class, 'build_default_controller' ) ) {
			return null;
		}

		try {
			return ScanController::build_default_controller();
		} catch ( \Throwable $e ) {
			return null;
		}
	}
}
