<?php

declare(strict_types=1);

namespace SiteFort\Modules\Scanner\API;

use SiteFort\Includes\RequestVerifier;
use SiteFort\Includes\RestRateLimiter;
use SiteFort\Modules\Cloud\Service\CloudSettings;
use SiteFort\Modules\Scanner\Queue\Handler\TaskResult;
use SiteFort\Modules\Scanner\Queue\ScanQueue;
use SiteFort\Modules\Scanner\Queue\TaskScheduler;
use SiteFort\Modules\Scanner\Repository\CloudJobRepository;
use SiteFort\Modules\Scanner\Repository\TaskQueueRepository;

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

if ( ! class_exists( RestRateLimiter::class ) ) {
	require_once SITEFORT_PLUGIN_DIR . 'includes/RestRateLimiter.php';
}

class ScannerWakeupReceiver {

	private const NAMESPACE              = 'sitefort/v1';
	private const RATE_LIMIT_PER_MINUTE  = 12;
	private const MAX_BODY_BYTES         = 4096;
	private const ALLOWED_EVENTS         = [ 'cloud_job_ready' ];
	private const TERMINAL_CLOUD_STATUSES = [ 'completed', 'failed' ];
	private const ACTIVE_LOCAL_STATUSES  = [ 'submitted', 'processing', 'queued' ];

	public static function register(): void {
		RestRateLimiter::register_hooks();
		add_action( 'rest_api_init', [ __CLASS__, 'register_routes' ] );
	}

	public static function register_routes(): void {
		register_rest_route(
			self::NAMESPACE,
			'/scanner/wakeup',
			[
				'methods'             => 'POST',
				'callback'            => [ __CLASS__, 'handle_wakeup' ],
				'permission_callback' => [ __CLASS__, 'verify_signature' ],
			]
		);
	}

	public static function verify_signature( $request ) {
		$settings    = CloudSettings::get_all();
		$site_token  = sanitize_text_field( (string) ( $settings['site_token'] ?? '' ) );
		$site_secret = sanitize_text_field( (string) ( $settings['site_secret'] ?? '' ) );

		$verified = RequestVerifier::verify_rest_request( $site_token, $site_secret, $request, self::MAX_BODY_BYTES );
		if ( true !== $verified ) {
			return $verified;
		}

		$rate_error = RestRateLimiter::check( 'scanner_wakeup', self::RATE_LIMIT_PER_MINUTE );
		if ( null !== $rate_error ) {
			return $rate_error;
		}

		return true;
	}

	public static function handle_wakeup( $request ): \WP_REST_Response {
		ignore_user_abort( true );

		$raw_body = (string) $request->get_body();
		if ( strlen( $raw_body ) > self::MAX_BODY_BYTES ) {
			return new \WP_REST_Response(
				[ 'ok' => false, 'error' => 'payload_too_large' ],
				413
			);
		}

		$payload = $request->get_json_params();
		if ( ! is_array( $payload ) ) {
			return new \WP_REST_Response( [ 'ok' => false, 'error' => 'invalid_json' ], 400 );
		}

		$event = sanitize_key( (string) ( $payload['event'] ?? '' ) );
		if ( ! in_array( $event, self::ALLOWED_EVENTS, true ) ) {
			return new \WP_REST_Response( [ 'ok' => false, 'error' => 'invalid_event' ], 400 );
		}

		$cloud_status = sanitize_key( (string) ( $payload['status'] ?? '' ) );
		if ( ! in_array( $cloud_status, self::TERMINAL_CLOUD_STATUSES, true ) ) {
			return new \WP_REST_Response( [ 'ok' => false, 'error' => 'invalid_status' ], 400 );
		}

		$job_id = sanitize_text_field( (string) ( $payload['job_id'] ?? '' ) );
		if ( ! self::is_valid_job_id( $job_id ) ) {
			return new \WP_REST_Response( [ 'ok' => false, 'error' => 'invalid_job_id' ], 400 );
		}

		$job_repository = new CloudJobRepository();
		$job            = $job_repository->get_by_job_id( $job_id );
		if ( ! is_array( $job ) ) {
			return new \WP_REST_Response( [ 'ok' => true, 'status' => 'job_not_local' ], 202 );
		}

		$scan_id = sanitize_text_field( (string) ( $job['scan_id'] ?? '' ) );
		if ( '' === $scan_id ) {
			return new \WP_REST_Response( [ 'ok' => true, 'status' => 'missing_scan_id' ], 202 );
		}

		$local_status = sanitize_key( (string) ( $job['status'] ?? '' ) );
		if ( ! in_array( $local_status, self::ACTIVE_LOCAL_STATUSES, true ) ) {
			return new \WP_REST_Response(
				[
					'ok'     => true,
					'status' => 'job_already_resolved',
				],
				200
			);
		}

		$queue = ScanQueue::get_job( $scan_id );
		if ( ! is_array( $queue ) || ! in_array( (string) ( $queue['status'] ?? '' ), [ 'pending', 'running' ], true ) ) {
			return new \WP_REST_Response( [ 'ok' => true, 'status' => 'scan_not_active' ], 202 );
		}

		$task_repository = new TaskQueueRepository();
		$enqueued        = $task_repository->enqueue(
			$scan_id,
			'poll_cloud',
			[ 'poll_count' => 0 ],
			time(),
			60,
			TaskResult::build_scan_poll_dedupe_key()
		);

		if ( false === $enqueued ) {
			return new \WP_REST_Response( [ 'ok' => false, 'error' => 'enqueue_failed' ], 500 );
		}

		if ( self::can_finish_request() ) {
			self::send_wakeup_response(
				[
					'ok'     => true,
					'status' => 'wakeup_queued',
				],
				200
			);
			TaskScheduler::dispatch_worker( $scan_id, true );
			exit;
		}

		TaskScheduler::dispatch_worker( $scan_id, true );

		return new \WP_REST_Response(
			[
				'ok'     => true,
				'status' => 'wakeup_queued',
			],
			200
		);
	}

	private static function is_valid_job_id( string $job_id ): bool {
		if ( '' === $job_id || strlen( $job_id ) > 64 ) {
			return false;
		}

		if ( function_exists( 'wp_is_uuid' ) && wp_is_uuid( $job_id ) ) {
			return true;
		}

		return 1 === preg_match( '/^[A-Za-z0-9_-]{16,64}$/', $job_id );
	}

	private static function can_finish_request(): bool {
		return ! headers_sent() && ( function_exists( 'fastcgi_finish_request' ) || function_exists( 'litespeed_finish_request' ) );
	}

	private static function send_wakeup_response( array $payload, int $status_code ): void {
		status_header( $status_code );
		nocache_headers();
		header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
		echo wp_json_encode( $payload );

		if ( function_exists( 'litespeed_finish_request' ) ) {
			@litespeed_finish_request();
			return;
		}

		@fastcgi_finish_request();
	}
}
