<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class ASA_REST {

	public function init() {
		add_action( 'rest_api_init', array( $this, 'register_routes' ) );
	}

	public function register_routes() {
		$routes = array(
			'/ping'                      => array( 'GET', 'ping' ),
			'/report/full'               => array( 'GET', 'full_report' ),
			'/report/static'             => array( 'POST', 'cached_static_report' ),
			'/report/vulnerability'      => array( 'POST', 'cached_vulnerability_report' ),
			'/report/compliance'         => array( 'POST', 'cached_compliance_report' ),
			'/scan/quick'                => array( 'POST', 'run_quick_scan' ),
			'/scan/malware'              => array( 'POST', 'run_malware_scan' ),
			'/scan/diff'                 => array( 'POST', 'run_diff_scan' ),
			'/scan/inventory'            => array( 'POST', 'run_inventory_scan' ),
			'/scan/vulnerability'        => array( 'POST', 'run_vulnerability_scan' ),
			'/scan/vulnerability/kickoff'=> array( 'POST', 'kickoff_vulnerability_scan' ),
			'/scan/static'               => array( 'POST', 'run_static_scan' ),
			'/scan/static/kickoff'       => array( 'POST', 'kickoff_static_scan' ),
			'/scan/manual/kickoff'       => array( 'POST', 'kickoff_manual_scan' ),
			'/scan/compliance'           => array( 'POST', 'run_compliance_scan' ),
			'/scan/permissions' => array( 'POST', 'run_permission_scan' ),
			'/scan/correlation' => array( 'POST', 'run_correlation_scan' ),
			'/telemetry'        => array( 'GET', 'telemetry_report' ),
			'/capabilities'     => array( 'GET', 'capabilities_report' ),
			'/logs/debug'       => array( 'POST', 'fetch_debug_log' ),
			'/logs/file'        => array( 'POST', 'fetch_log_file' ),
			'/activity-log'     => array( 'GET', 'fetch_wp_activity_log' ),
		);

		foreach ( $routes as $route => $config ) {
			register_rest_route(
				'aegisify-agent/v1',
				$route,
				array(
					'methods'             => $config[0],
					'callback'            => array( $this, $config[1] ),
					'permission_callback' => array( $this, 'permissions_check' ),
				)
			);
		}
	}

	public function permissions_check( WP_REST_Request $request ) {
		return ASA_Auth::validate_request( $request );
	}

	private function log_rest_event( $category, $status, $action, $message, $request = null, array $context = array(), $dedupe_window = 10 ) {
		if ( $request instanceof WP_REST_Request ) {
			$context = array_merge(
				array(
					'route'  => (string) $request->get_route(),
					'method' => strtoupper( (string) $request->get_method() ),
				),
				$context
			);
		}

		ASA_Logger::log( $category, $status, 'rest', $action, $message, $context, $dedupe_window );
	}

	public function ping( WP_REST_Request $request ) {
		$response = rest_ensure_response(
			array(
				'success'              => true,
				'message'              => 'Agent reachable over SSL with security key.',
				'site'                 => home_url(),
				'key_fingerprint'      => substr( hash( 'sha256', ASA_Auth::get_security_key() ), 0, 32 ),
				'telemetry_policy'     => ASA_Telemetry::get_capabilities(),
				'tooling_capabilities' => class_exists( 'ASA_Local_Inventory' ) ? ASA_Local_Inventory::get_tooling_capabilities() : array(),
				'capabilities_report'  => $this->build_capabilities_report(),
			)
		);
		$this->log_rest_event( 'connectivity', 'success', 'ping', __( 'The agent ping route responded successfully.', 'aegisify-audit-agent' ), $request, array( 'site' => home_url() ), 10 );
		return $response;
	}

	public function full_report( WP_REST_Request $request ) {
		$report    = ASA_Report_Builder::build();
		$response  = rest_ensure_response(
			array(
				'success'   => true,
				'report'    => $report,
				'telemetry' => ASA_Telemetry::build_payload(),
			)
		);
		$this->log_rest_event( 'network', 'success', 'full_report', __( 'The full report route returned a fresh agent report.', 'aegisify-audit-agent' ), $request, array( 'findings_count' => ! empty( $report['findings'] ) && is_array( $report['findings'] ) ? count( $report['findings'] ) : 0 ), 10 );
		return $response;
	}

	public function cached_static_report( WP_REST_Request $request ) {
		$requested_job_id      = absint( $request->get_param( 'job_id' ) );
		$requested_profile_key = sanitize_key( (string) $request->get_param( 'scan_profile' ) );
		$report = class_exists( 'ASA_Admin' ) && is_callable( array( 'ASA_Admin', 'get_cached_static_report_for_rest' ) )
			? ASA_Admin::get_cached_static_report_for_rest()
			: array();
		$report = is_array( $report ) ? $report : array();
		$scan_state = class_exists( 'ASA_Admin' ) && is_callable( array( 'ASA_Admin', 'get_static_scan_state_for_rest' ) ) ? ASA_Admin::get_static_scan_state_for_rest() : get_option( 'asa_static_scan_state', array() );

		$legacy_report = class_exists( 'ASA_Report_Builder' ) && is_callable( array( 'ASA_Report_Builder', 'get_cached_report' ) )
			? ASA_Report_Builder::get_cached_report()
			: array();
		$legacy_report = is_array( $legacy_report ) ? $legacy_report : array();

		$cached_job_id      = ! empty( $report['remote_job_id'] ) ? absint( $report['remote_job_id'] ) : 0;
		$state_job_id       = ! empty( $scan_state['remote_job_id'] ) ? absint( $scan_state['remote_job_id'] ) : 0;
		$cached_profile_key = ! empty( $report['scan_profile'] ) ? sanitize_key( (string) $report['scan_profile'] ) : '';
		if ( ( $requested_job_id > 0 && $cached_job_id > 0 && $requested_job_id !== $cached_job_id ) || ( '' !== $requested_profile_key && '' !== $cached_profile_key && $requested_profile_key !== $cached_profile_key ) ) {
			$report = array(
				'generated_at_gmt'      => '',
				'results'               => array(),
				'findings'              => array(),
				'summary'               => array(),
				'privacy'               => array(),
				'selected_plugins'      => array(),
				'selected_plugin_files' => array(),
				'remote_job_id'         => $state_job_id,
				'scan_profile'          => ! empty( $scan_state['scan_profile'] ) ? sanitize_key( (string) $scan_state['scan_profile'] ) : '',
				'request_domain'        => ! empty( $scan_state['request_domain'] ) ? sanitize_text_field( (string) $scan_state['request_domain'] ) : '',
			);
			$legacy_report = array();
		}

		$findings = ! empty( $report['findings'] ) && is_array( $report['findings'] ) ? $report['findings'] : array();
		$findings = array_values( array_filter( $findings, array( $this, 'is_static_report_finding' ) ) );
		if ( empty( $findings ) && ! empty( $legacy_report['findings'] ) && is_array( $legacy_report['findings'] ) ) {
			$findings = array_values( array_filter( $legacy_report['findings'], array( $this, 'is_static_report_finding' ) ) );
		}

		$selected_plugin_files  = ! empty( $report['selected_plugin_files'] ) && is_array( $report['selected_plugin_files'] ) ? array_values( array_unique( array_map( 'plugin_basename', array_map( 'strval', $report['selected_plugin_files'] ) ) ) ) : array();
		if ( empty( $selected_plugin_files ) && ! empty( $legacy_report['scan_filters']['plugins'] ) && is_array( $legacy_report['scan_filters']['plugins'] ) ) {
			$selected_plugin_files = array_values( array_unique( array_map( 'plugin_basename', array_map( 'strval', $legacy_report['scan_filters']['plugins'] ) ) ) );
		}
		$selected_plugin_labels = ! empty( $report['selected_plugins'] ) && is_array( $report['selected_plugins'] ) ? array_values( array_unique( array_map( 'sanitize_text_field', array_map( 'strval', $report['selected_plugins'] ) ) ) ) : $this->resolve_selected_plugin_labels( $selected_plugin_files, $findings );
		$summary                = ! empty( $report['summary'] ) && is_array( $report['summary'] ) ? $report['summary'] : array();
		$summary['total_findings'] = count( $findings );
		$summary['cached']         = ! empty( $report['generated_at_gmt'] );
		$summary['status']         = ! empty( $scan_state['status'] ) ? sanitize_key( (string) $scan_state['status'] ) : '';
		$summary['completed_at']   = ! empty( $scan_state['completed_at'] ) ? sanitize_text_field( (string) $scan_state['completed_at'] ) : '';
		$summary['message']        = ! empty( $scan_state['message'] ) ? sanitize_text_field( (string) $scan_state['message'] ) : '';

		$response = rest_ensure_response(
			array(
				'success'               => true,
				'report_source'         => ! empty( $report['generated_at_gmt'] ) ? 'cached_static_report' : 'cached_agent_report',
				'generated_at_gmt'      => ! empty( $report['generated_at_gmt'] ) ? sanitize_text_field( (string) $report['generated_at_gmt'] ) : ( ! empty( $legacy_report['generated_at_gmt'] ) ? sanitize_text_field( (string) $legacy_report['generated_at_gmt'] ) : '' ),
				'results'               => ! empty( $report['results'] ) && is_array( $report['results'] ) ? $report['results'] : array(),
				'findings'              => $findings,
				'selected_plugins'      => $selected_plugin_labels,
				'selected_plugin_files' => $selected_plugin_files,
				'summary'               => $summary,
				'privacy'               => ! empty( $report['privacy'] ) && is_array( $report['privacy'] ) ? $report['privacy'] : array(),
				'scan_state'            => is_array( $scan_state ) ? $scan_state : array(),
				'tooling_capabilities'  => ! empty( $summary['tooling_capabilities'] ) && is_array( $summary['tooling_capabilities'] ) ? $summary['tooling_capabilities'] : ( class_exists( 'ASA_Local_Inventory' ) ? ASA_Local_Inventory::get_tooling_capabilities() : array() ),
				'telemetry'             => ASA_Telemetry::build_payload(),
			)
		);
		$this->log_rest_event( 'network', 'success', 'cached_static_report', __( 'The cached static report route returned filtered report data.', 'aegisify-audit-agent' ), $request, array( 'findings_count' => count( $findings ) ), 10 );
		return $response;
	}

	public function cached_vulnerability_report( WP_REST_Request $request ) {
		$requested_job_id      = absint( $request->get_param( 'job_id' ) );
		$requested_profile_key = sanitize_key( (string) $request->get_param( 'scan_profile' ) );
		$report = class_exists( 'ASA_Admin' ) && is_callable( array( 'ASA_Admin', 'get_cached_vulnerability_report_for_rest' ) )
			? ASA_Admin::get_cached_vulnerability_report_for_rest()
			: array();
		$report = is_array( $report ) ? $report : array();
		$scan_state = class_exists( 'ASA_Admin' ) && is_callable( array( 'ASA_Admin', 'get_vulnerability_scan_state_for_rest' ) ) ? ASA_Admin::get_vulnerability_scan_state_for_rest() : get_option( 'asa_vulnerability_scan_state', array() );
		$cached_job_id      = ! empty( $report['remote_job_id'] ) ? absint( $report['remote_job_id'] ) : 0;
		$state_job_id       = ! empty( $scan_state['remote_job_id'] ) ? absint( $scan_state['remote_job_id'] ) : 0;
		$cached_profile_key = ! empty( $report['scan_profile'] ) ? sanitize_key( (string) $report['scan_profile'] ) : '';
		if ( ( $requested_job_id > 0 && $cached_job_id > 0 && $requested_job_id !== $cached_job_id ) || ( '' !== $requested_profile_key && '' !== $cached_profile_key && $requested_profile_key !== $cached_profile_key ) ) {
			$report = array(
				'generated_at_gmt'         => '',
				'summary'                  => array(),
				'provider_status'          => array(),
				'advisories'               => array(),
				'local_wordpress_findings' => array(),
				'results'                  => array(),
				'inventory'                => array(),
				'scan_details'             => array(),
				'remote_job_id'            => $state_job_id,
				'scan_profile'             => ! empty( $scan_state['scan_profile'] ) ? sanitize_key( (string) $scan_state['scan_profile'] ) : '',
				'request_domain'           => ! empty( $scan_state['request_domain'] ) ? sanitize_text_field( (string) $scan_state['request_domain'] ) : '',
			);
		}
		$inventory   = ! empty( $report['inventory'] ) && is_array( $report['inventory'] ) ? $report['inventory'] : array();
		$response = rest_ensure_response(
			array(
				'success'                    => true,
				'generated_at_gmt'           => ! empty( $report['generated_at_gmt'] ) ? sanitize_text_field( (string) $report['generated_at_gmt'] ) : '',
				'summary'                    => ! empty( $report['summary'] ) && is_array( $report['summary'] ) ? $report['summary'] : array(),
				'provider_status'            => ! empty( $report['provider_status'] ) && is_array( $report['provider_status'] ) ? $report['provider_status'] : array(),
				'advisories'                 => ! empty( $report['advisories'] ) && is_array( $report['advisories'] ) ? $report['advisories'] : array(),
				'local_wordpress_results'    => ! empty( $report['results'] ) && is_array( $report['results'] ) ? $report['results'] : array(),
				'local_wordpress_findings'   => ! empty( $report['local_wordpress_findings'] ) && is_array( $report['local_wordpress_findings'] ) ? $report['local_wordpress_findings'] : array(),
				'local_wordpress_advisories' => ! empty( $report['advisories'] ) && is_array( $report['advisories'] ) ? $report['advisories'] : array(),
				'local_wordpress_summary'    => ! empty( $report['scan_details'] ) && is_array( $report['scan_details'] ) ? $report['scan_details'] : array(),
				'results'                    => ! empty( $report['results'] ) && is_array( $report['results'] ) ? $report['results'] : array(),
				'inventory'                  => $inventory,
				'scan_details'               => ! empty( $report['scan_details'] ) && is_array( $report['scan_details'] ) ? $report['scan_details'] : array(),
				'scan_state'                 => is_array( $scan_state ) ? $scan_state : array(),
				'tooling_capabilities'       => ! empty( $inventory['tooling_capabilities'] ) && is_array( $inventory['tooling_capabilities'] ) ? $inventory['tooling_capabilities'] : ( class_exists( 'ASA_Local_Inventory' ) ? ASA_Local_Inventory::get_tooling_capabilities() : array() ),
				'telemetry'                  => ASA_Telemetry::build_payload(),
			)
		);
		$this->log_rest_event( 'network', 'success', 'cached_vulnerability_report', __( 'The cached vulnerability report route returned the latest cached vulnerability data.', 'aegisify-audit-agent' ), $request, array( 'advisory_count' => ! empty( $report['advisories'] ) && is_array( $report['advisories'] ) ? count( $report['advisories'] ) : 0 ), 10 );
		return $response;
	}

	public function run_quick_scan( WP_REST_Request $request ) {
		$results  = ( new ASA_Hardening_Scan() )->run();
		$response = rest_ensure_response( array( 'success' => true, 'results' => $results ) );
		$this->log_rest_event( 'scan', 'success', 'run_quick_scan', __( 'The quick scan route completed successfully.', 'aegisify-audit-agent' ), $request, array( 'result_count' => is_array( $results ) ? count( $results ) : 0 ), 10 );
		return $response;
	}

	public function cached_compliance_report( WP_REST_Request $request ) {
		$requested_job_id      = absint( $request->get_param( 'job_id' ) );
		$requested_profile_key = sanitize_key( (string) $request->get_param( 'scan_profile' ) );
		$report = class_exists( 'ASA_Admin' ) && is_callable( array( 'ASA_Admin', 'get_cached_compliance_report_for_rest' ) )
			? ASA_Admin::get_cached_compliance_report_for_rest()
			: array();
		$report     = is_array( $report ) ? $report : array();
		$scan_state = class_exists( 'ASA_Admin' ) && is_callable( array( 'ASA_Admin', 'get_scan_state_for_rest' ) ) ? ASA_Admin::get_scan_state_for_rest() : get_option( 'asa_scan_state', array() );
		$scan_state = is_array( $scan_state ) ? $scan_state : array();
		$cached_job_id      = ! empty( $report['remote_job_id'] ) ? absint( $report['remote_job_id'] ) : 0;
		$cached_profile_key = ! empty( $report['scan_profile'] ) ? sanitize_key( (string) $report['scan_profile'] ) : '';

		if ( ( $requested_job_id > 0 && $cached_job_id > 0 && $requested_job_id !== $cached_job_id ) || ( '' !== $requested_profile_key && '' !== $cached_profile_key && $requested_profile_key !== $cached_profile_key ) ) {
			$report = array(
				'compliance_version' => '',
				'generated_at_gmt'   => '',
				'results'            => array(),
				'findings'           => array(),
				'summary'            => array(),
				'privacy'            => array(),
				'remote_job_id'      => ! empty( $scan_state['remote_job_id'] ) ? absint( $scan_state['remote_job_id'] ) : 0,
				'scan_profile'       => ! empty( $scan_state['scan_profile'] ) ? sanitize_key( (string) $scan_state['scan_profile'] ) : '',
				'request_domain'     => ! empty( $scan_state['request_domain'] ) ? sanitize_text_field( (string) $scan_state['request_domain'] ) : '',
			);
		}

		$response = rest_ensure_response(
			array(
				'success'      => true,
				'report_source' => ! empty( $report['generated_at_gmt'] ) ? 'cached_compliance_report' : 'cached_scan_state',
				'payload'      => $report,
				'scan_state'   => $scan_state,
				'telemetry'    => ASA_Telemetry::build_payload(),
			)
		);
		$this->log_rest_event( 'network', 'success', 'cached_compliance_report', __( 'The cached compliance report route returned the latest cached compliance data.', 'aegisify-audit-agent' ), $request, array( 'finding_count' => ! empty( $report['findings'] ) && is_array( $report['findings'] ) ? count( $report['findings'] ) : 0 ), 10 );
		return $response;
	}

	public function run_malware_scan( WP_REST_Request $request ) {
		$results  = ( new ASA_Malware_Scan() )->run();
		$response = rest_ensure_response( array( 'success' => true, 'results' => $results ) );
		$this->log_rest_event( 'scan', 'success', 'run_malware_scan', __( 'The malware scan route completed successfully.', 'aegisify-audit-agent' ), $request, array( 'result_count' => is_array( $results ) ? count( $results ) : 0 ), 10 );
		return $response;
	}

	public function run_diff_scan( WP_REST_Request $request ) {
		$results  = ( new ASA_Diff_Scan() )->run();
		$response = rest_ensure_response(
			array(
				'success'   => true,
				'results'   => $results,
				'telemetry' => ASA_Telemetry::build_payload(),
			)
		);
		$this->log_rest_event( 'scan', 'success', 'run_diff_scan', __( 'The diff scan route completed successfully.', 'aegisify-audit-agent' ), $request, array( 'result_count' => is_array( $results ) ? count( $results ) : 0 ), 10 );
		return $response;
	}

	public function run_inventory_scan( WP_REST_Request $request ) {
		$inventory_scan = new ASA_Inventory_Scan();
		$payload        = $inventory_scan->build_payload( array( 'include_dependency_audits' => false ) );
		$results        = $inventory_scan->run( $payload );
		$response       = rest_ensure_response(
			array(
				'success'   => true,
				'results'   => $results,
				'inventory' => ! empty( $payload['inventory'] ) ? $payload['inventory'] : array(),
				'summary'   => ! empty( $payload['summary'] ) ? $payload['summary'] : array(),
				'privacy'   => ! empty( $payload['privacy'] ) ? $payload['privacy'] : array(),
				'tooling_capabilities' => ! empty( $payload['inventory']['tooling_capabilities'] ) ? $payload['inventory']['tooling_capabilities'] : ( class_exists( 'ASA_Local_Inventory' ) ? ASA_Local_Inventory::get_tooling_capabilities() : array() ),
				'telemetry' => ASA_Telemetry::build_payload(),
			)
		);
		$this->log_rest_event( 'scan', 'success', 'run_inventory_scan', __( 'The inventory scan route completed successfully.', 'aegisify-audit-agent' ), $request, array( 'result_count' => is_array( $results ) ? count( $results ) : 0 ), 10 );
		return $response;
	}

	public function kickoff_vulnerability_scan( WP_REST_Request $request ) {
		$state = class_exists( 'ASA_Admin' ) && is_callable( array( 'ASA_Admin', 'queue_remote_vulnerability_scan' ) )
			? ASA_Admin::queue_remote_vulnerability_scan(
				array(
					'scan_profile' => sanitize_key( (string) $request->get_param( 'scan_profile' ) ),
					'job_id'       => absint( $request->get_param( 'job_id' ) ),
					'domain'       => sanitize_text_field( (string) $request->get_param( 'domain' ) ),
				)
			)
			: array();
		$response = rest_ensure_response(
			array(
				'success'    => ! empty( $state['status'] ) && ! in_array( $state['status'], array( 'failed', 'error' ), true ),
				'scan_state' => is_array( $state ) ? $state : array(),
				'message'    => ! empty( $state['message'] ) ? sanitize_text_field( (string) $state['message'] ) : '',
			)
		);
		$this->log_rest_event( 'scan', 'success', 'kickoff_vulnerability_scan', __( 'The vulnerability kickoff route queued the local vulnerability scan.', 'aegisify-audit-agent' ), $request, array( 'status' => ! empty( $state['status'] ) ? $state['status'] : '' ), 10 );
		return $response;
	}

	public function run_vulnerability_scan( WP_REST_Request $request ) {
		$vulnerability_scan = new ASA_Vulnerability_Scan();
		$payload            = $vulnerability_scan->build_payload( sanitize_key( (string) $request->get_param( 'scan_profile' ) ) );
		$response           = rest_ensure_response(
			array(
				'success'              => true,
				'results'              => ! empty( $payload['results'] ) ? $payload['results'] : array(),
				'summary'              => ! empty( $payload['summary'] ) ? $payload['summary'] : array(),
				'provider_status'      => ! empty( $payload['provider_status'] ) ? $payload['provider_status'] : array(),
				'dependency_audits'    => ! empty( $payload['dependency_audits'] ) ? $payload['dependency_audits'] : array(),
				'local_wordpress_results'    => ! empty( $payload['local_wordpress_results'] ) ? $payload['local_wordpress_results'] : array(),
				'local_wordpress_findings'   => ! empty( $payload['local_wordpress_findings'] ) ? $payload['local_wordpress_findings'] : array(),
				'local_wordpress_advisories' => ! empty( $payload['local_wordpress_advisories'] ) ? $payload['local_wordpress_advisories'] : array(),
				'local_wordpress_summary'    => ! empty( $payload['local_wordpress_summary'] ) ? $payload['local_wordpress_summary'] : array(),
				'inventory'            => ! empty( $payload['inventory'] ) ? $payload['inventory'] : array(),
				'privacy'              => ! empty( $payload['privacy'] ) ? $payload['privacy'] : array(),
				'tooling_capabilities' => ! empty( $payload['tooling_capabilities'] ) ? $payload['tooling_capabilities'] : ( class_exists( 'ASA_Local_Inventory' ) ? ASA_Local_Inventory::get_tooling_capabilities() : array() ),
				'telemetry'            => ASA_Telemetry::build_payload(),
			)
		);
		$this->log_rest_event( 'scan', 'success', 'run_vulnerability_scan', __( 'The local vulnerability scan route completed successfully.', 'aegisify-audit-agent' ), $request, array( 'result_count' => ! empty( $payload['results'] ) && is_array( $payload['results'] ) ? count( $payload['results'] ) : 0 ), 10 );
		return $response;
	}

	public function kickoff_static_scan( WP_REST_Request $request ) {
		$state = class_exists( 'ASA_Admin' ) && is_callable( array( 'ASA_Admin', 'queue_remote_static_scan' ) )
			? ASA_Admin::queue_remote_static_scan(
				array(
					'scan_profile' => sanitize_key( (string) $request->get_param( 'scan_profile' ) ),
					'job_id'       => absint( $request->get_param( 'job_id' ) ),
					'domain'       => sanitize_text_field( (string) $request->get_param( 'domain' ) ),
					'selected_plugins' => is_array( $request->get_param( 'selected_plugins' ) ) ? array_map( 'sanitize_text_field', (array) $request->get_param( 'selected_plugins' ) ) : array(),
				)
			)
			: array();
		$response = rest_ensure_response(
			array(
				'success'    => ! empty( $state['status'] ) && ! in_array( $state['status'], array( 'failed', 'error' ), true ),
				'scan_state' => is_array( $state ) ? $state : array(),
				'message'    => ! empty( $state['message'] ) ? sanitize_text_field( (string) $state['message'] ) : '',
			)
		);
		$this->log_rest_event( 'scan', 'success', 'kickoff_static_scan', __( 'The static analysis kickoff route queued the local static analysis scan.', 'aegisify-audit-agent' ), $request, array( 'status' => ! empty( $state['status'] ) ? $state['status'] : '' ), 10 );
		return $response;
	}

	public function kickoff_manual_scan( WP_REST_Request $request ) {
		$state = class_exists( 'ASA_Admin' ) && is_callable( array( 'ASA_Admin', 'queue_remote_scan' ) )
			? ASA_Admin::queue_remote_scan(
				array(
					'scan_profile' => sanitize_key( (string) $request->get_param( 'scan_profile' ) ),
					'job_id'       => absint( $request->get_param( 'job_id' ) ),
					'domain'       => sanitize_text_field( (string) $request->get_param( 'domain' ) ),
				)
			)
			: array();
		$response = rest_ensure_response(
			array(
				'success'    => ! empty( $state['status'] ) && ! in_array( $state['status'], array( 'failed', 'error' ), true ),
				'scan_state' => is_array( $state ) ? $state : array(),
				'message'    => ! empty( $state['message'] ) ? sanitize_text_field( (string) $state['message'] ) : '',
			)
		);
		$this->log_rest_event( 'scan', 'success', 'kickoff_manual_scan', __( 'The manual Scan Now kickoff route queued the local Scan Now workflow.', 'aegisify-audit-agent' ), $request, array( 'status' => ! empty( $state['status'] ) ? $state['status'] : '' ), 10 );
		return $response;
	}

	public function run_static_scan( WP_REST_Request $request ) {
		$static_scan = new ASA_Static_Scan( is_array( $request->get_param( 'selected_plugins' ) ) ? array_map( 'sanitize_text_field', (array) $request->get_param( 'selected_plugins' ) ) : array() );
		$payload     = $static_scan->build_payload( sanitize_key( (string) $request->get_param( 'scan_profile' ) ) );
		$response    = rest_ensure_response(
			array(
				'success'   => true,
				'findings'  => ! empty( $payload['findings'] ) ? $payload['findings'] : array(),
				'summary'   => ! empty( $payload['summary'] ) ? $payload['summary'] : array(),
				'privacy'   => ! empty( $payload['privacy'] ) ? $payload['privacy'] : array(),
				'tooling_capabilities' => ! empty( $payload['summary']['tooling_capabilities'] ) ? $payload['summary']['tooling_capabilities'] : ( class_exists( 'ASA_Local_Inventory' ) ? ASA_Local_Inventory::get_tooling_capabilities() : array() ),
				'telemetry' => ASA_Telemetry::build_payload(),
			)
		);
		$this->log_rest_event( 'scan', 'success', 'run_static_scan', __( 'The static scan route completed successfully.', 'aegisify-audit-agent' ), $request, array( 'finding_count' => ! empty( $payload['findings'] ) && is_array( $payload['findings'] ) ? count( $payload['findings'] ) : 0 ), 10 );
		return $response;
	}

	public function run_compliance_scan( WP_REST_Request $request ) {
		$compliance_scan = new ASA_Compliance_Scan();
		$payload         = $compliance_scan->build_payload( sanitize_key( (string) $request->get_param( 'scan_profile' ) ) );
		$response        = rest_ensure_response(
			array(
				'success'   => true,
				'findings'  => ! empty( $payload['findings'] ) ? $payload['findings'] : array(),
				'summary'   => ! empty( $payload['summary'] ) ? $payload['summary'] : array(),
				'privacy'   => ! empty( $payload['privacy'] ) ? $payload['privacy'] : array(),
				'tooling_capabilities' => ! empty( $payload['inventory']['tooling_capabilities'] ) ? $payload['inventory']['tooling_capabilities'] : ( class_exists( 'ASA_Local_Inventory' ) ? ASA_Local_Inventory::get_tooling_capabilities() : array() ),
				'telemetry' => ASA_Telemetry::build_payload(),
			)
		);
		$this->log_rest_event( 'scan', 'success', 'run_compliance_scan', __( 'The compliance scan route completed successfully.', 'aegisify-audit-agent' ), $request, array( 'finding_count' => ! empty( $payload['findings'] ) && is_array( $payload['findings'] ) ? count( $payload['findings'] ) : 0 ), 10 );
		return $response;
	}

	public function run_wordfence_scan( WP_REST_Request $request ) {
		return $this->run_vulnerability_scan( $request );
	}

	public function run_permission_scan( WP_REST_Request $request ) {
		$results  = ( new ASA_Permission_Scan() )->run();
		$response = rest_ensure_response( array( 'success' => true, 'results' => $results ) );
		$this->log_rest_event( 'scan', 'success', 'run_permission_scan', __( 'The permission scan route completed successfully.', 'aegisify-audit-agent' ), $request, array( 'result_count' => is_array( $results ) ? count( $results ) : 0 ), 10 );
		return $response;
	}

	public function run_correlation_scan( WP_REST_Request $request ) {
		$results  = ( new ASA_Aegisify_Correlation() )->run();
		$response = rest_ensure_response( array( 'success' => true, 'results' => $results ) );
		$this->log_rest_event( 'scan', 'success', 'run_correlation_scan', __( 'The correlation scan route completed successfully.', 'aegisify-audit-agent' ), $request, array( 'result_count' => is_array( $results ) ? count( $results ) : 0 ), 10 );
		return $response;
	}

	public function fetch_debug_log( WP_REST_Request $request ) {
		if ( ! class_exists( 'ASA_Telemetry' ) || ! ASA_Telemetry::is_category_allowed( 'debug_log_fetch' ) ) {
			$this->log_rest_event( 'security', 'error', 'fetch_debug_log', __( 'The debug.log fetch route was blocked by telemetry access control.', 'aegisify-audit-agent' ), $request, array(), 10 );
			return new WP_REST_Response(
				array(
					'success' => false,
					'message' => __( 'debug.log fetch is blocked by the current Telemetry Access Control policy.', 'aegisify-audit-agent' ),
				),
				403
			);
		}

		$debug_log_path     = wp_normalize_path( WP_CONTENT_DIR . '/debug.log' );
		$delete_after_fetch = rest_sanitize_boolean( $request->get_param( 'delete_after_fetch' ) );

		if ( ! file_exists( $debug_log_path ) ) {
			$this->log_rest_event( 'network', 'warning', 'fetch_debug_log', __( 'The debug.log fetch route completed but no debug.log file was present.', 'aegisify-audit-agent' ), $request, array( 'path' => $debug_log_path ), 10 );
			return rest_ensure_response(
				array(
					'success'             => true,
					'exists'              => false,
					'path'                => $debug_log_path,
					'fetched_at'          => current_time( 'mysql' ),
					'bytes'               => 0,
					'lines'               => 0,
					'deleted_after_fetch' => false,
					'content'             => '',
					'message'             => __( 'No debug.log file was found in wp-content.', 'aegisify-audit-agent' ),
				)
			);
		}

		if ( ! is_readable( $debug_log_path ) || ! is_file( $debug_log_path ) ) {
			$this->log_rest_event( 'network', 'error', 'fetch_debug_log', __( 'The debug.log fetch route failed because the file is not readable.', 'aegisify-audit-agent' ), $request, array( 'path' => $debug_log_path ), 10 );
			return new WP_REST_Response(
				array(
					'success' => false,
					'message' => __( 'The debug.log file exists but is not readable by WordPress.', 'aegisify-audit-agent' ),
					'path'    => $debug_log_path,
				),
				500
			);
		}

		$content = file_get_contents( $debug_log_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Reading the local debug.log file for an authenticated agent request.
		if ( false === $content ) {
			$this->log_rest_event( 'network', 'error', 'fetch_debug_log', __( 'The debug.log fetch route failed because the file could not be read.', 'aegisify-audit-agent' ), $request, array( 'path' => $debug_log_path ), 10 );
			return new WP_REST_Response(
				array(
					'success' => false,
					'message' => __( 'The debug.log file could not be read.', 'aegisify-audit-agent' ),
					'path'    => $debug_log_path,
				),
				500
			);
		}

		$normalized_content = str_replace( array( "\r\n", "\r" ), "\n", (string) $content );
		$bytes              = strlen( (string) $content );
		$trimmed            = trim( $normalized_content );
		$lines              = '' === $trimmed ? 0 : substr_count( $trimmed, "\n" ) + 1;
		$deleted            = false;
		$message            = __( 'debug.log fetched successfully.', 'aegisify-audit-agent' );

		if ( $delete_after_fetch ) {
			$deleted = @unlink( $debug_log_path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged,WordPress.WP.AlternativeFunctions.unlink_unlink -- Deleting the fetched debug.log file is an explicit user-requested agent capability.
			if ( ! $deleted ) {
				$message = __( 'debug.log fetched, but delete-after-fetch did not succeed.', 'aegisify-audit-agent' );
			}
		}

		$response = rest_ensure_response(
			array(
				'success'             => true,
				'exists'              => true,
				'path'                => $debug_log_path,
				'fetched_at'          => current_time( 'mysql' ),
				'bytes'               => $bytes,
				'lines'               => $lines,
				'deleted_after_fetch' => $deleted,
				'content'             => (string) $content,
				'message'             => $message,
			)
		);
		$this->log_rest_event( 'network', $deleted || ! $delete_after_fetch ? 'success' : 'warning', 'fetch_debug_log', __( 'The debug.log fetch route completed successfully.', 'aegisify-audit-agent' ), $request, array( 'path' => $debug_log_path, 'bytes' => $bytes, 'lines' => $lines, 'deleted_after_fetch' => $deleted ), 10 );
		return $response;
	}



	public function fetch_wp_activity_log( WP_REST_Request $request ) {
		if ( ! class_exists( 'ASA_Telemetry' ) || ! ASA_Telemetry::is_category_allowed( 'wp_activity_log_fetch' ) ) {
			$this->log_rest_event( 'security', 'error', 'fetch_wp_activity_log', __( 'The WordPress activity log fetch route was blocked by telemetry access control.', 'aegisify-audit-agent' ), $request, array(), 10 );
			return new WP_REST_Response(
				array(
					'success' => false,
					'message' => __( 'WordPress activity log fetch is blocked by the current Telemetry Access Control policy.', 'aegisify-audit-agent' ),
				),
				403
			);
		}

		if ( ! class_exists( 'ASA_WP_Activity_Log' ) ) {
			return new WP_REST_Response(
				array(
					'success' => false,
					'message' => __( 'The WordPress activity log collector is not available on this Agent.', 'aegisify-audit-agent' ),
				),
				500
			);
		}

		$limit_param = $request->get_param( 'limit' );
		$limit      = null !== $limit_param && '' !== (string) $limit_param ? max( 1, min( 500, absint( $limit_param ) ) ) : 200;
		$since_id   = max( 0, absint( $request->get_param( 'since_id' ) ) );
		$payload    = ASA_WP_Activity_Log::build_rest_payload( $limit, $since_id );
		$response = rest_ensure_response(
			array(
				'success' => true,
				'message' => __( 'WordPress activity log fetched successfully.', 'aegisify-audit-agent' ),
				'payload' => $payload,
			)
		);
		return $response;
	}

	public function fetch_log_file( WP_REST_Request $request ) {
		if ( ! class_exists( 'ASA_Telemetry' ) || ! ASA_Telemetry::is_category_allowed( 'file_log_fetch' ) ) {
			$this->log_rest_event( 'security', 'error', 'fetch_log_file', __( 'The custom log fetch route was blocked by telemetry access control.', 'aegisify-audit-agent' ), $request, array(), 10 );
			return new WP_REST_Response(
				array(
					'success' => false,
					'message' => __( 'Custom log fetch is blocked by the current Telemetry Access Control policy.', 'aegisify-audit-agent' ),
				),
				403
			);
		}

		$requested_path   = $this->sanitize_requested_log_path( $request->get_param( 'path' ) );
		$requested_offset = $this->sanitize_requested_log_offset( $request->get_param( 'offset' ) );
		if ( '' === $requested_path ) {
			return new WP_REST_Response(
				array(
					'success' => false,
					'message' => __( 'A valid log path is required.', 'aegisify-audit-agent' ),
				),
				400
			);
		}

		$resolved = $this->resolve_requested_log_path( $requested_path );
		if ( false === $resolved ) {
			$this->log_rest_event( 'security', 'warning', 'fetch_log_file', __( 'The custom log fetch route rejected a path outside the allowed content scope.', 'aegisify-audit-agent' ), $request, array( 'requested_path' => $requested_path ), 10 );
			return new WP_REST_Response(
				array(
					'success' => false,
					'message' => __( 'The requested log path is outside the allowed wp-content scope.', 'aegisify-audit-agent' ),
				),
				400
			);
		}

		if ( ! $this->is_allowed_log_file( $resolved ) ) {
			$this->log_rest_event( 'security', 'warning', 'fetch_log_file', __( 'The custom log fetch route rejected a file that does not look like a log.', 'aegisify-audit-agent' ), $request, array( 'resolved_path' => $resolved ), 10 );
			return new WP_REST_Response(
				array(
					'success' => false,
					'message' => __( 'The requested file does not match the allowed log file patterns.', 'aegisify-audit-agent' ),
				),
				400
			);
		}

		if ( ! file_exists( $resolved ) ) {
			$this->log_rest_event( 'network', 'warning', 'fetch_log_file', __( 'The custom log fetch route completed but no file was present at the requested path.', 'aegisify-audit-agent' ), $request, array( 'path' => $resolved ), 10 );
			return rest_ensure_response(
				array(
					'success'          => true,
					'exists'           => false,
					'path'             => $resolved,
					'fetched_at'       => current_time( 'mysql' ),
					'bytes'            => 0,
					'total_bytes'      => 0,
					'chunk_bytes'      => 0,
					'offset_requested' => $requested_offset,
					'offset_used'      => 0,
					'read_mode'        => 'full',
					'lines'            => 0,
					'content'          => '',
					'message'          => __( 'No log file was found at the requested path.', 'aegisify-audit-agent' ),
				)
			);
		}

		if ( ! is_file( $resolved ) || ! is_readable( $resolved ) ) {
			$this->log_rest_event( 'network', 'error', 'fetch_log_file', __( 'The custom log fetch route failed because the file is not readable.', 'aegisify-audit-agent' ), $request, array( 'path' => $resolved ), 10 );
			return new WP_REST_Response(
				array(
					'success' => false,
					'message' => __( 'The requested log file exists but is not readable by WordPress.', 'aegisify-audit-agent' ),
					'path'    => $resolved,
				),
				500
			);
		}

		clearstatcache( true, $resolved );
		$total_bytes = @filesize( $resolved ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_filesize -- Reading authenticated log-file metadata for delta fetch decisions.
		$total_bytes = false !== $total_bytes ? max( 0, (int) $total_bytes ) : 0;

		$offset_used = 0;
		$read_mode   = 'full';
		$content     = false;

		if ( $requested_offset > 0 && $total_bytes > 0 && $requested_offset < $total_bytes ) {
			$offset_used = $requested_offset;
			$read_mode   = 'delta';
			$content     = $this->read_log_file_segment( $resolved, $requested_offset );
		} elseif ( $requested_offset > 0 && $requested_offset === $total_bytes ) {
			$offset_used = $requested_offset;
			$read_mode   = 'delta';
			$content     = '';
		} elseif ( $requested_offset > 0 && $requested_offset > $total_bytes ) {
			$offset_used = 0;
			$read_mode   = 'reset_full';
			$content     = $this->read_log_file_segment( $resolved, 0 );
		} else {
			$content = $this->read_log_file_segment( $resolved, 0 );
		}

		if ( false === $content ) {
			$this->log_rest_event( 'network', 'error', 'fetch_log_file', __( 'The custom log fetch route failed because the file could not be read.', 'aegisify-audit-agent' ), $request, array( 'path' => $resolved, 'requested_offset' => $requested_offset ), 10 );
			return new WP_REST_Response(
				array(
					'success' => false,
					'message' => __( 'The requested log file could not be read.', 'aegisify-audit-agent' ),
					'path'    => $resolved,
				),
				500
			);
		}

		$normalized_content = str_replace( array( "
", "
" ), "
", (string) $content );
		$chunk_bytes        = strlen( (string) $content );
		$trimmed            = trim( $normalized_content );
		$lines              = '' === $trimmed ? 0 : substr_count( $trimmed, "
" ) + 1;

		if ( 'delta' === $read_mode && 0 === $chunk_bytes ) {
			$message = __( 'No new log content was found since the last fetch.', 'aegisify-audit-agent' );
		} elseif ( 'delta' === $read_mode ) {
			$message = __( 'Log delta fetched successfully.', 'aegisify-audit-agent' );
		} elseif ( 'reset_full' === $read_mode ) {
			$message = __( 'Log file fetched successfully after the stored offset was reset.', 'aegisify-audit-agent' );
		} else {
			$message = __( 'Log file fetched successfully.', 'aegisify-audit-agent' );
		}

		$response = rest_ensure_response(
			array(
				'success'          => true,
				'exists'           => true,
				'path'             => $resolved,
				'fetched_at'       => current_time( 'mysql' ),
				'bytes'            => $total_bytes,
				'total_bytes'      => $total_bytes,
				'chunk_bytes'      => $chunk_bytes,
				'offset_requested' => $requested_offset,
				'offset_used'      => $offset_used,
				'read_mode'        => $read_mode,
				'lines'            => $lines,
				'content'          => '',
				'content_b64'      => base64_encode( (string) $content ),
				'content_encoding' => 'base64',
				'message'          => $message,
			)
		);
		$this->log_rest_event( 'network', 'success', 'fetch_log_file', __( 'The custom log fetch route completed successfully.', 'aegisify-audit-agent' ), $request, array( 'path' => $resolved, 'requested_offset' => $requested_offset, 'offset_used' => $offset_used, 'read_mode' => $read_mode, 'chunk_bytes' => $chunk_bytes, 'total_bytes' => $total_bytes, 'lines' => $lines ), 10 );
		return $response;
	}

	private function sanitize_requested_log_offset( $offset ) {
		return max( 0, absint( $offset ) );
	}

	private function read_log_file_segment( $path, $offset = 0 ) {
		$offset = max( 0, absint( $offset ) );
		if ( 0 === $offset ) {
			return file_get_contents( $path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Reading the authenticated log file for App Logging ingestion.
		}

		$handle = @fopen( $path, 'rb' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- Reading the authenticated log file for App Logging ingestion.
		if ( false === $handle ) {
			return false;
		}

		if ( 0 !== fseek( $handle, $offset ) ) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fseek -- Seeking within the authenticated log file for delta reads.
			// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Closing a read-only log file handle after a failed seek.
			fclose( $handle );
			return false;
		}

		$content = stream_get_contents( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_stream_get_contents -- Reading the authenticated log file tail segment for App Logging ingestion.
		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Closing a read-only log file handle after reading the requested segment.
		fclose( $handle );

		return false === $content ? false : (string) $content;
	}

	private function sanitize_requested_log_path( $path ) {
		$path = wp_unslash( (string) $path );
		$path = preg_replace( '/\0+/', '', $path );
		$path = str_replace( '\\', '/', $path );
		$path = preg_replace( '#/+#', '/', $path );
		$path = trim( $path );
		if ( '' === $path ) {
			return '';
		}
		if ( '/' !== substr( $path, 0, 1 ) && ! preg_match( '/^[A-Za-z]:\//', $path ) ) {
			$path = '/' . ltrim( $path, '/' );
		}
		return sanitize_text_field( $path );
	}

	private function normalize_path_segments( $path ) {
		$path   = str_replace( '\\', '/', (string) $path );
		$prefix = '';
		if ( preg_match( '/^[A-Za-z]:/', $path, $matches ) ) {
			$prefix = strtoupper( (string) $matches[0] );
			$path   = substr( $path, 2 );
		}

		$leading = '/' === substr( $path, 0, 1 ) ? '/' : '';
		$parts   = explode( '/', $path );
		$stack   = array();

		foreach ( $parts as $part ) {
			$part = trim( (string) $part );
			if ( '' === $part || '.' === $part ) {
				continue;
			}
			if ( '..' === $part ) {
				array_pop( $stack );
				continue;
			}
			$stack[] = $part;
		}

		return wp_normalize_path( $prefix . $leading . implode( '/', $stack ) );
	}

	private function resolve_requested_log_path( $requested_path ) {
		$requested_path = $this->sanitize_requested_log_path( $requested_path );
		if ( '' === $requested_path ) {
			return false;
		}

		$root_path    = $this->normalize_path_segments( untrailingslashit( ABSPATH ) );
		$content_path = $this->normalize_path_segments( untrailingslashit( WP_CONTENT_DIR ) );

		if ( 0 === strpos( $requested_path, $root_path ) ) {
			$resolved = $this->normalize_path_segments( $requested_path );
		} else {
			$resolved = $this->normalize_path_segments( $root_path . '/' . ltrim( $requested_path, '/' ) );
		}

		if ( $resolved !== $content_path && 0 !== strpos( $resolved, $content_path . '/' ) ) {
			return false;
		}

		return $resolved;
	}

	private function is_allowed_log_file( $path ) {
		$path        = wp_normalize_path( (string) $path );
		$basename    = strtolower( wp_basename( $path ) );
		$content_log = wp_normalize_path( WP_CONTENT_DIR . '/aegisify-logs/' );

		if ( 0 === strpos( $path, $content_log ) ) {
			return true;
		}

		$extension = strtolower( pathinfo( $basename, PATHINFO_EXTENSION ) );
		if ( in_array( $extension, array( 'log', 'txt', 'text', 'jsonl', 'out' ), true ) ) {
			return true;
		}

		return false !== strpos( $basename, 'log' );
	}

	public function telemetry_report( WP_REST_Request $request ) {
		$payload  = ASA_Telemetry::build_payload();
		$response = rest_ensure_response( array( 'success' => true, 'telemetry' => $payload ) );
		$this->log_rest_event( 'network', 'success', 'telemetry_report', __( 'The telemetry route returned the current telemetry payload.', 'aegisify-audit-agent' ), $request, array(), 10 );
		return $response;
	}

	public function capabilities_report( WP_REST_Request $request ) {
		$capabilities = $this->build_capabilities_report();
		$response     = rest_ensure_response(
			array(
				'success'      => true,
				'capabilities' => $capabilities,
			)
		);
		$this->log_rest_event( 'network', 'success', 'capabilities_report', __( 'The capabilities route returned the current agent capability report.', 'aegisify-audit-agent' ), $request, array(), 10 );
		return $response;
	}

	private function is_static_report_finding( $finding ) {
		if ( ! is_array( $finding ) ) {
			return false;
		}

		$file_path   = ! empty( $finding['file_path'] ) ? sanitize_text_field( (string) $finding['file_path'] ) : '';
		$plugin_name = ! empty( $finding['plugin_name'] ) ? sanitize_text_field( (string) $finding['plugin_name'] ) : '';
		$plugin_slug = ! empty( $finding['plugin_slug'] ) ? sanitize_key( (string) $finding['plugin_slug'] ) : '';
		$engine      = ! empty( $finding['engine'] ) ? sanitize_key( (string) $finding['engine'] ) : '';
		$rule_code   = ! empty( $finding['code'] ) ? sanitize_text_field( (string) $finding['code'] ) : '';
		$category    = ! empty( $finding['category'] ) ? sanitize_key( (string) $finding['category'] ) : '';

		if ( '' !== $file_path || '' !== $plugin_name || '' !== $plugin_slug ) {
			return true;
		}

		if ( '' !== $engine && in_array( $engine, array( 'phpcs_wpcs', 'wordpress_custom_rules', 'aegisify_php_rules', 'aegisify_js_rules', 'aegisify_python_rules' ), true ) ) {
			return true;
		}

		if ( '' !== $rule_code && in_array( $category, array( 'code', 'plugin_repo' ), true ) ) {
			return true;
		}

		return false;
	}

	private function resolve_selected_plugin_labels( array $selected_plugin_files, array $findings ) {
		$labels = array();
		if ( ! empty( $selected_plugin_files ) ) {
			if ( ! function_exists( 'get_plugins' ) ) {
				require_once ABSPATH . 'wp-admin/includes/plugin.php';
			}
			$plugins = function_exists( 'get_plugins' ) ? (array) get_plugins() : array();
			foreach ( $selected_plugin_files as $plugin_file ) {
				$plugin_file = plugin_basename( (string) $plugin_file );
				if ( isset( $plugins[ $plugin_file ]['Name'] ) && '' !== (string) $plugins[ $plugin_file ]['Name'] ) {
					$labels[] = sanitize_text_field( (string) $plugins[ $plugin_file ]['Name'] );
				} else {
					$labels[] = sanitize_text_field( $plugin_file );
				}
			}
		}

		if ( empty( $labels ) ) {
			foreach ( $findings as $finding ) {
				if ( empty( $finding['plugin_name'] ) ) {
					continue;
				}
				$labels[] = sanitize_text_field( (string) $finding['plugin_name'] );
			}
		}

		$labels = array_values( array_unique( array_filter( array_map( 'strval', $labels ) ) ) );
		sort( $labels, SORT_NATURAL | SORT_FLAG_CASE );
		return $labels;
	}

	private function build_capabilities_report() {
		$tooling = class_exists( 'ASA_Local_Inventory' ) ? ASA_Local_Inventory::get_tooling_capabilities() : array();
		return array(
			'agent' => array(
				'name'    => 'Aegisify Audit Agent',
				'version' => defined( 'ASA_VERSION' ) ? (string) ASA_VERSION : '1.9.4',
			),
			'execution_model' => array(
				'supported' => array(
					'Bundled PHPCS + WPCS is the supported local code-analysis engine for Deep Code Analysis.',
					'Aegisify custom PHP and JavaScript sniffs run on top of the bundled PHPCS/WPCS package.',
					'Aegisify native Python rulesets run locally when Python source files are discovered, while pip-audit remains optional and manifest-driven.',
				),
			),
			'routes' => array(
				'ping' => array(
					'path'      => '/aegisify-agent/v1/ping',
					'method'    => 'GET',
					'supported' => true,
				),
				'report_full' => array(
					'path'      => '/aegisify-agent/v1/report/full',
					'method'    => 'GET',
					'supported' => true,
				),
				'report_static' => array(
					'path'      => '/aegisify-agent/v1/report/static',
					'method'    => 'POST',
					'supported' => true,
				),
				'report_vulnerability' => array(
					'path'      => '/aegisify-agent/v1/report/vulnerability',
					'method'    => 'POST',
					'supported' => true,
				),
				'report_compliance' => array(
					'path'      => '/aegisify-agent/v1/report/compliance',
					'method'    => 'POST',
					'supported' => true,
				),
				'scan_quick' => array(
					'path'      => '/aegisify-agent/v1/scan/quick',
					'method'    => 'POST',
					'supported' => true,
				),
				'scan_inventory' => array(
					'path'      => '/aegisify-agent/v1/scan/inventory',
					'method'    => 'POST',
					'supported' => true,
				),
				'scan_vulnerability_kickoff' => array(
					'path'      => '/aegisify-agent/v1/scan/vulnerability/kickoff',
					'method'    => 'POST',
					'supported' => true,
				),
				'scan_static' => array(
					'path'      => '/aegisify-agent/v1/scan/static',
					'method'    => 'POST',
					'supported' => true,
				),
				'scan_static_kickoff' => array(
					'path'      => '/aegisify-agent/v1/scan/static/kickoff',
					'method'    => 'POST',
					'supported' => true,
				),
				'scan_manual_kickoff' => array(
					'path'      => '/aegisify-agent/v1/scan/manual/kickoff',
					'method'    => 'POST',
					'supported' => true,
				),
				'scan_compliance' => array(
					'path'      => '/aegisify-agent/v1/scan/compliance',
					'method'    => 'POST',
					'supported' => true,
				),
				'scan_permissions' => array(
					'path'      => '/aegisify-agent/v1/scan/permissions',
					'method'    => 'POST',
					'supported' => true,
				),
				'scan_correlation' => array(
					'path'      => '/aegisify-agent/v1/scan/correlation',
					'method'    => 'POST',
					'supported' => true,
				),
				'telemetry' => array(
					'path'      => '/aegisify-agent/v1/telemetry',
					'method'    => 'GET',
					'supported' => true,
				),
				'capabilities' => array(
					'path'      => '/aegisify-agent/v1/capabilities',
					'method'    => 'GET',
					'supported' => true,
				),
				'debug_log_fetch' => array(
					'path'      => '/aegisify-agent/v1/logs/debug',
					'method'    => 'POST',
					'supported' => true,
				),
				'file_log_fetch' => array(
					'path'      => '/aegisify-agent/v1/logs/file',
					'method'    => 'POST',
					'supported' => true,
				),
				'wp_activity_log' => array(
					'path'      => '/aegisify-agent/v1/activity-log',
					'method'    => 'GET',
					'supported' => true,
				),
			),
			'tooling_capabilities' => $tooling,
		);
	}
}
