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

class ASA_Compliance_Scan {

	public function build_payload( $scan_profile = '' ) {
		$findings = $this->run();
		$summary  = array(
			'total_controls' => count( $findings ),
			'fail'           => 0,
			'warn'           => 0,
			'pass'           => 0,
			'by_provider'    => array(),
		);
		foreach ( $findings as $finding ) {
			$status   = ! empty( $finding['status'] ) ? (string) $finding['status'] : 'warn';
			$provider = ! empty( $finding['provider_key'] ) ? (string) $finding['provider_key'] : 'unknown';
			$summary[ $status ] = isset( $summary[ $status ] ) ? ( (int) $summary[ $status ] + 1 ) : 1;
			$summary['by_provider'][ $provider ] = isset( $summary['by_provider'][ $provider ] ) ? ( (int) $summary['by_provider'][ $provider ] + 1 ) : 1;
		}

		return array(
			'compliance_version' => '1.0.0',
			'generated_at_gmt'   => gmdate( 'Y-m-d H:i:s' ),
			'results'            => $this->build_profile_results( $summary, $scan_profile ),
			'findings'           => $findings,
			'summary'            => $summary,
			'privacy'            => array(
				'mode'        => 'metadata_only',
				'description' => 'Compliance baselines use WordPress inventory facts, database posture, route metadata, and observed response headers. They exclude customer content, usernames, emails, order contents, secrets, tokens, and database row values.',
			),
		);
	}

	public function run() {
		$payload   = ASA_Local_Inventory::generate();
		$inventory = ! empty( $payload['inventory'] ) && is_array( $payload['inventory'] ) ? $payload['inventory'] : array();
		$probe     = self::probe_http_surface();
		$findings  = array();

		$runtime      = ! empty( $inventory['runtime'] ) ? (array) $inventory['runtime'] : array();
		$db_posture   = ! empty( $inventory['db_posture'] ) ? (array) $inventory['db_posture'] : array();
		$db_privs     = ! empty( $inventory['db_privileges'] ) ? (array) $inventory['db_privileges'] : array();
		$rest_routes  = ! empty( $inventory['rest_routes'] ) ? (array) $inventory['rest_routes'] : array();
		$ajax_actions = ! empty( $inventory['admin_ajax_actions'] ) ? (array) $inventory['admin_ajax_actions'] : array();
		$headers      = ! empty( $probe['headers'] ) ? (array) $probe['headers'] : array();
		$cookies      = ! empty( $probe['cookies'] ) ? (array) $probe['cookies'] : array();
		$xmlrpc       = ! empty( $probe['xmlrpc'] ) ? (array) $probe['xmlrpc'] : array();

		$public_route_count = 0;
		$public_mutating_routes = 0;
		foreach ( $rest_routes as $route ) {
			if ( empty( $route['public'] ) ) {
				continue;
			}
			$public_route_count++;
			$methods = ! empty( $route['methods'] ) ? array_map( 'strtoupper', (array) $route['methods'] ) : array();
			if ( array_intersect( array( 'POST', 'PUT', 'PATCH', 'DELETE' ), $methods ) ) {
				$public_mutating_routes++;
			}
		}
		$public_ajax_count = count(
			array_filter(
				$ajax_actions,
				static function( $action ) {
					return ! empty( $action['public'] );
				}
			)
		);

		$findings[] = $this->control_result( 'stig_appsec', 'app.cookie.secure', $this->evaluate_cookie_flag( $cookies, 'secure' ), 'Observed anonymous response cookie flags', 'All session cookies should be marked Secure over HTTPS.', array( 'probe.cookies', 'runtime.is_ssl' ), true );
		$findings[] = $this->control_result( 'stig_appsec', 'app.cookie.httponly', $this->evaluate_cookie_flag( $cookies, 'httponly' ), 'Observed anonymous response cookie flags', 'All session cookies should be marked HttpOnly.', array( 'probe.cookies' ), true );
		$findings[] = $this->control_result( 'stig_appsec', 'app.cookie.samesite', $this->evaluate_cookie_flag( $cookies, 'samesite' ), 'Observed anonymous response cookie flags', 'All session cookies should declare an explicit SameSite policy.', array( 'probe.cookies' ), true );
		$findings[] = $this->control_result( 'stig_appsec', 'app.header.hsts', $this->evaluate_header_presence( $headers, 'strict-transport-security', ! empty( $runtime['is_ssl'] ) ? 'fail' : 'warn' ), 'Observed response headers for home page probe', 'Strict-Transport-Security should be present for HTTPS sites.', array( 'probe.headers', 'runtime.is_ssl' ), true );
		$findings[] = $this->control_result( 'stig_appsec', 'app.header.csp', $this->evaluate_header_presence( $headers, 'content-security-policy', 'warn' ), 'Observed response headers for home page probe', 'Content-Security-Policy should be present on browser-facing responses.', array( 'probe.headers' ), true );
		$findings[] = $this->control_result( 'stig_appsec', 'app.header.frame_protection', $this->evaluate_frame_protection( $headers ), 'Observed response headers for home page probe', 'Frame protections should use frame-ancestors or X-Frame-Options.', array( 'probe.headers' ), true );
		$findings[] = $this->control_result( 'stig_appsec', 'app.header.referrer_policy', $this->evaluate_header_presence( $headers, 'referrer-policy', 'warn' ), 'Observed response headers for home page probe', 'Referrer-Policy should minimize sensitive URL leakage.', array( 'probe.headers' ), true );
		$findings[] = $this->control_result( 'stig_appsec', 'app.header.x_content_type_options', $this->evaluate_header_value_contains( $headers, 'x-content-type-options', 'nosniff', 'fail' ), 'Observed response headers for home page probe', 'X-Content-Type-Options should be set to nosniff.', array( 'probe.headers' ), true );
		$findings[] = $this->control_result( 'stig_appsec', 'app.xmlrpc.restricted', $this->evaluate_xmlrpc( $runtime, $xmlrpc ), 'Observed XML-RPC probe status', 'xmlrpc.php should be disabled, blocked, or otherwise tightly controlled.', array( 'runtime.xmlrpc_enabled', 'probe.xmlrpc' ), true );
		$findings[] = $this->control_result( 'stig_appsec', 'app.rest.auth.boundaries', $this->evaluate_rest_boundaries( $public_route_count, $public_mutating_routes ), sprintf( 'Observed %1$d public REST routes, including %2$d public mutating routes.', (int) $public_route_count, (int) $public_mutating_routes ), 'Public REST exposure should align with least-privilege authorization boundaries.', array( 'inventory.rest_routes' ), true );
		$findings[] = $this->control_result( 'stig_appsec', 'app.ajax.auth.boundaries', $this->evaluate_ajax_boundaries( $public_ajax_count ), sprintf( 'Observed %d public wp_ajax_nopriv actions.', (int) $public_ajax_count ), 'Public admin-ajax actions should be minimal and strongly constrained.', array( 'inventory.admin_ajax_actions' ), true );
		$findings[] = $this->control_result( 'stig_appsec', 'app.file.editors.disabled', ! empty( $runtime['file_editors_disabled'] ) ? array( 'status' => 'pass', 'observed' => 'Browser-based file editors are disabled.' ) : array( 'status' => 'fail', 'observed' => 'Browser-based file editors remain enabled.' ), 'Observed runtime constants for DISALLOW_FILE_EDIT and DISALLOW_FILE_MODS.', 'Built-in file editors should be disabled.', array( 'runtime.file_editors_disabled', 'runtime.file_mods_allowed' ), false );

		$sql_mode = ! empty( $db_posture['sql_mode'] ) ? strtoupper( (string) $db_posture['sql_mode'] ) : '';
		$strict_sql = ( false !== strpos( $sql_mode, 'STRICT_TRANS_TABLES' ) || false !== strpos( $sql_mode, 'STRICT_ALL_TABLES' ) );
		$findings[] = $this->control_result( 'stig_database', 'db.sql_mode.strict', $strict_sql ? array( 'status' => 'pass', 'observed' => $sql_mode ) : array( 'status' => 'fail', 'observed' => '' === $sql_mode ? 'SQL mode unavailable.' : $sql_mode ), 'Observed database sql_mode posture.', 'SQL mode should include strict controls.', array( 'inventory.db_posture.sql_mode' ), false );
		$findings[] = $this->control_result( 'stig_database', 'db.privilege.no_all', empty( $db_privs['has_all_privileges'] ) ? array( 'status' => 'pass', 'observed' => 'ALL PRIVILEGES not present.' ) : array( 'status' => 'fail', 'observed' => 'Application database user has ALL PRIVILEGES.' ), 'Observed current database grants.', 'Application account must not have ALL PRIVILEGES.', array( 'inventory.db_privileges.has_all_privileges' ), false );
		$findings[] = $this->control_result( 'stig_database', 'db.privilege.no_file', empty( $db_privs['has_file_priv'] ) ? array( 'status' => 'pass', 'observed' => 'FILE privilege not present.' ) : array( 'status' => 'fail', 'observed' => 'Application database user has FILE privilege.' ), 'Observed current database grants.', 'Application account must not have FILE privilege.', array( 'inventory.db_privileges.has_file_priv' ), false );
		$findings[] = $this->control_result( 'stig_database', 'db.privilege.no_super', empty( $db_privs['has_super_priv'] ) ? array( 'status' => 'pass', 'observed' => 'SUPER privilege not present.' ) : array( 'status' => 'fail', 'observed' => 'Application database user has SUPER privilege.' ), 'Observed current database grants.', 'Application account must not have SUPER privilege.', array( 'inventory.db_privileges.has_super_priv' ), false );
		$findings[] = $this->control_result( 'stig_database', 'db.privilege.no_grant_option', empty( $db_privs['has_grant_option'] ) ? array( 'status' => 'pass', 'observed' => 'GRANT OPTION not present.' ) : array( 'status' => 'fail', 'observed' => 'Application database user can grant additional privileges.' ), 'Observed current database grants.', 'Application account must not have GRANT OPTION.', array( 'inventory.db_privileges.has_grant_option' ), false );
		$findings[] = $this->control_result( 'stig_database', 'db.privilege.no_create_user', empty( $db_privs['has_create_user_priv'] ) ? array( 'status' => 'pass', 'observed' => 'CREATE USER privilege not present.' ) : array( 'status' => 'fail', 'observed' => 'Application database user can create database users.' ), 'Observed current database grants.', 'Application account must not have CREATE USER.', array( 'inventory.db_privileges.has_create_user_priv' ), false );
		$findings[] = $this->control_result( 'stig_database', 'db.privilege.no_process', empty( $db_privs['has_process_priv'] ) ? array( 'status' => 'pass', 'observed' => 'PROCESS privilege not present.' ) : array( 'status' => 'warn', 'observed' => 'Application database user has PROCESS privilege.' ), 'Observed current database grants.', 'PROCESS privilege should be removed unless operationally justified.', array( 'inventory.db_privileges.has_process_priv' ), false );

		$local_infile = ! empty( $db_posture['local_infile'] ) ? strtolower( (string) $db_posture['local_infile'] ) : '';
		$findings[] = $this->control_result( 'stig_database', 'db.local_infile.disabled', in_array( $local_infile, array( '0', 'off', 'disabled', 'false', 'no' ), true ) ? array( 'status' => 'pass', 'observed' => (string) $db_posture['local_infile'] ) : array( 'status' => 'fail', 'observed' => '' === $local_infile ? 'local_infile unavailable.' : (string) $db_posture['local_infile'] ), 'Observed database local_infile posture.', 'local_infile should be disabled unless explicitly required.', array( 'inventory.db_posture.local_infile' ), false );
		$secure_file_priv = ! empty( $db_posture['secure_file_priv'] ) ? strtolower( (string) $db_posture['secure_file_priv'] ) : '';
		$secure_status = 'fail';
		if ( in_array( $secure_file_priv, array( 'restricted', 'null', 'disabled' ), true ) ) {
			$secure_status = 'pass';
		} elseif ( '' === $secure_file_priv ) {
			$secure_status = 'warn';
		}
		$findings[] = $this->control_result( 'stig_database', 'db.secure_file_priv.restricted', array( 'status' => $secure_status, 'observed' => '' === $secure_file_priv ? 'secure_file_priv unavailable.' : (string) $db_posture['secure_file_priv'] ), 'Observed database secure_file_priv posture.', 'secure_file_priv should be restricted or disabled.', array( 'inventory.db_posture.secure_file_priv' ), false );

		$have_ssl = ! empty( $db_posture['have_ssl'] ) ? strtolower( (string) $db_posture['have_ssl'] ) : '';
		$require_secure_transport = ! empty( $db_posture['require_secure_transport'] ) ? strtolower( (string) $db_posture['require_secure_transport'] ) : '';
		$ssl_status = ( in_array( $have_ssl, array( 'yes', 'on', 'supported' ), true ) || in_array( $require_secure_transport, array( '1', 'on', 'yes' ), true ) ) ? 'pass' : 'warn';
		$findings[] = $this->control_result( 'stig_database', 'db.ssl.available', array( 'status' => $ssl_status, 'observed' => sprintf( 'have_ssl=%1$s; require_secure_transport=%2$s', (string) $have_ssl, (string) $require_secure_transport ) ), 'Observed database SSL posture.', 'Database SSL/TLS support should be available or enforced.', array( 'inventory.db_posture.have_ssl', 'inventory.db_posture.require_secure_transport' ), false );

		$db_engine  = ! empty( $runtime['db_engine'] ) ? (string) $runtime['db_engine'] : '';
		$db_version = ! empty( $runtime['db_version'] ) ? (string) $runtime['db_version'] : '';
		$findings[] = $this->control_result( 'stig_database', 'db.engine.identified', ( '' !== $db_engine && '' !== $db_version ) ? array( 'status' => 'pass', 'observed' => $db_engine . ' ' . $db_version ) : array( 'status' => 'fail', 'observed' => 'Database engine or version unavailable.' ), 'Observed runtime database inventory.', 'Database engine and version should be identified for patch governance.', array( 'runtime.db_engine', 'runtime.db_version' ), false );

		return array_values( array_filter( $findings ) );
	}

	private function control_result( $provider_key, $control_key, array $evaluation, $observed_prefix, $expected, array $fact_keys, $internet_facing ) {
		$status   = ! empty( $evaluation['status'] ) ? sanitize_key( (string) $evaluation['status'] ) : 'warn';
		$observed = ! empty( $evaluation['observed'] ) ? sanitize_text_field( (string) $evaluation['observed'] ) : sanitize_text_field( (string) $observed_prefix );
		return array(
			'provider_key'     => sanitize_key( (string) $provider_key ),
			'control_key'      => sanitize_key( (string) $control_key ),
			'status'           => in_array( $status, array( 'pass', 'warn', 'fail' ), true ) ? $status : 'warn',
			'title'            => ucwords( str_replace( array( '.', '_' ), ' ', (string) $control_key ) ),
			'description'      => sanitize_text_field( (string) $observed_prefix ),
			'observed'         => $observed,
			'expected'         => sanitize_text_field( (string) $expected ),
			'fact_keys'        => array_values( array_map( 'sanitize_text_field', $fact_keys ) ),
			'confidence'       => 'warn' === $status ? 80 : 92,
			'internet_facing'  => (bool) $internet_facing,
			'dedupe_key'       => md5( wp_json_encode( array( $provider_key, $control_key, $status, $observed ) ) ),
		);
	}

	private function evaluate_cookie_flag( array $cookies, $flag ) {
		$flag = strtolower( (string) $flag );
		if ( empty( $cookies ) ) {
			return array(
				'status'   => 'warn',
				'observed' => 'No Set-Cookie headers were observed during the anonymous probe.',
			);
		}
		$missing = 0;
		foreach ( $cookies as $cookie ) {
			if ( empty( $cookie[ $flag ] ) ) {
				$missing++;
			}
		}
		if ( $missing > 0 ) {
			return array(
				'status'   => 'fail',
				'observed' => sprintf( '%1$d of %2$d observed cookies were missing %3$s.', (int) $missing, count( $cookies ), strtoupper( $flag ) ),
			);
		}
		return array(
			'status'   => 'pass',
			'observed' => sprintf( 'All %1$d observed cookies included %2$s.', count( $cookies ), strtoupper( $flag ) ),
		);
	}

	private function evaluate_header_presence( array $headers, $header_name, $missing_status ) {
		$header_name = strtolower( (string) $header_name );
		if ( ! empty( $headers[ $header_name ] ) ) {
			return array(
				'status'   => 'pass',
				'observed' => $header_name . ': ' . sanitize_text_field( (string) $headers[ $header_name ] ),
			);
		}
		return array(
			'status'   => sanitize_key( (string) $missing_status ),
			'observed' => $header_name . ' header not observed.',
		);
	}

	private function evaluate_header_value_contains( array $headers, $header_name, $expected_substring, $missing_status ) {
		$header_name = strtolower( (string) $header_name );
		$value       = ! empty( $headers[ $header_name ] ) ? strtolower( (string) $headers[ $header_name ] ) : '';
		if ( '' !== $value && false !== strpos( $value, strtolower( (string) $expected_substring ) ) ) {
			return array( 'status' => 'pass', 'observed' => $header_name . ': ' . sanitize_text_field( (string) $headers[ $header_name ] ) );
		}
		return array( 'status' => sanitize_key( (string) $missing_status ), 'observed' => $header_name . ' missing expected value ' . $expected_substring . '.' );
	}

	private function evaluate_frame_protection( array $headers ) {
		if ( ! empty( $headers['content-security-policy'] ) && false !== stripos( (string) $headers['content-security-policy'], 'frame-ancestors' ) ) {
			return array( 'status' => 'pass', 'observed' => 'Content-Security-Policy contains frame-ancestors.' );
		}
		if ( ! empty( $headers['x-frame-options'] ) ) {
			return array( 'status' => 'pass', 'observed' => 'X-Frame-Options: ' . sanitize_text_field( (string) $headers['x-frame-options'] ) );
		}
		return array( 'status' => 'fail', 'observed' => 'No frame-ancestors or X-Frame-Options protection observed.' );
	}

	private function evaluate_xmlrpc( array $runtime, array $xmlrpc ) {
		$xmlrpc_enabled = ! empty( $runtime['xmlrpc_enabled'] );
		$code           = ! empty( $xmlrpc['code'] ) ? absint( $xmlrpc['code'] ) : 0;
		if ( ! $xmlrpc_enabled || in_array( $code, array( 403, 404 ), true ) ) {
			return array( 'status' => 'pass', 'observed' => sprintf( 'xmlrpc_enabled=%1$s; response_code=%2$d', $xmlrpc_enabled ? 'yes' : 'no', (int) $code ) );
		}
		if ( in_array( $code, array( 200, 405 ), true ) ) {
			return array( 'status' => 'fail', 'observed' => sprintf( 'xmlrpc_enabled=%1$s; response_code=%2$d', $xmlrpc_enabled ? 'yes' : 'no', (int) $code ) );
		}
		return array( 'status' => 'warn', 'observed' => sprintf( 'xmlrpc_enabled=%1$s; response_code=%2$d', $xmlrpc_enabled ? 'yes' : 'no', (int) $code ) );
	}

	private function evaluate_rest_boundaries( $public_route_count, $public_mutating_routes ) {
		if ( $public_mutating_routes > 0 ) {
			return array( 'status' => 'fail', 'observed' => sprintf( '%1$d public REST routes include %2$d public mutating routes.', (int) $public_route_count, (int) $public_mutating_routes ) );
		}
		if ( $public_route_count > 25 ) {
			return array( 'status' => 'warn', 'observed' => sprintf( '%d public REST routes were observed.', (int) $public_route_count ) );
		}
		return array( 'status' => 'pass', 'observed' => sprintf( '%d public REST routes were observed without public mutating methods.', (int) $public_route_count ) );
	}

	private function evaluate_ajax_boundaries( $public_ajax_count ) {
		if ( $public_ajax_count > 10 ) {
			return array( 'status' => 'fail', 'observed' => sprintf( '%d public wp_ajax_nopriv actions were observed.', (int) $public_ajax_count ) );
		}
		if ( $public_ajax_count > 0 ) {
			return array( 'status' => 'warn', 'observed' => sprintf( '%d public wp_ajax_nopriv actions were observed.', (int) $public_ajax_count ) );
		}
		return array( 'status' => 'pass', 'observed' => 'No public wp_ajax_nopriv actions were observed.' );
	}

	private static function probe_http_surface() {
		$target = home_url( '/' );
		ASA_Logger::log( 'network', 'info', 'compliance_scan', 'probe_http_surface', __( 'The compliance scan started probing the local HTTP surface.', 'aegisify-audit-agent' ), array( 'target' => $target ), 10 );
		$response = wp_remote_get(
			$target,
			array(
				'timeout'     => 12,
				'redirection' => 3,
				'sslverify'   => true,
			)
		);
		$headers = array();
		$cookies = array();
		if ( is_wp_error( $response ) ) {
			ASA_Logger::log( 'network', 'error', 'compliance_scan', 'probe_http_surface', __( 'The compliance scan could not fetch the local home page during the HTTP surface probe.', 'aegisify-audit-agent' ), array( 'target' => $target, 'message' => $response->get_error_message() ), 10 );
		} else {
			$headers = self::normalize_headers( wp_remote_retrieve_headers( $response ) );
			$cookies = self::parse_set_cookie_headers( $headers );
			ASA_Logger::log( 'network', 'success', 'compliance_scan', 'probe_http_surface', __( 'The compliance scan fetched the local home page successfully during the HTTP surface probe.', 'aegisify-audit-agent' ), array( 'target' => $target, 'http_code' => (int) wp_remote_retrieve_response_code( $response ), 'header_count' => count( $headers ), 'cookie_count' => count( $cookies ) ), 10 );
		}
		$xmlrpc_target   = home_url( '/xmlrpc.php' );
		$xmlrpc_response = wp_remote_get(
			$xmlrpc_target,
			array(
				'timeout'     => 8,
				'redirection' => 1,
				'sslverify'   => true,
			)
		);
		if ( is_wp_error( $xmlrpc_response ) ) {
			ASA_Logger::log( 'network', 'warning', 'compliance_scan', 'probe_xmlrpc_surface', __( 'The compliance scan could not fetch the local xmlrpc.php endpoint during the HTTP surface probe.', 'aegisify-audit-agent' ), array( 'target' => $xmlrpc_target, 'message' => $xmlrpc_response->get_error_message() ), 10 );
		} else {
			ASA_Logger::log( 'network', 'success', 'compliance_scan', 'probe_xmlrpc_surface', __( 'The compliance scan fetched the local xmlrpc.php endpoint during the HTTP surface probe.', 'aegisify-audit-agent' ), array( 'target' => $xmlrpc_target, 'http_code' => (int) wp_remote_retrieve_response_code( $xmlrpc_response ) ), 10 );
		}
		return array(
			'headers' => $headers,
			'cookies' => $cookies,
			'xmlrpc'  => array(
				'code'  => is_wp_error( $xmlrpc_response ) ? 0 : (int) wp_remote_retrieve_response_code( $xmlrpc_response ),
				'error' => is_wp_error( $xmlrpc_response ) ? sanitize_text_field( $xmlrpc_response->get_error_message() ) : '',
			),
		);
	}

	private static function normalize_headers( $headers ) {
		$normalized = array();
		if ( is_object( $headers ) && method_exists( $headers, 'getAll' ) ) {
			$headers = $headers->getAll();
		}
		foreach ( (array) $headers as $key => $value ) {
			$key = strtolower( sanitize_text_field( (string) $key ) );
			if ( is_array( $value ) ) {
				$normalized[ $key ] = implode( ', ', array_map( 'sanitize_text_field', $value ) );
			} else {
				$normalized[ $key ] = sanitize_text_field( (string) $value );
			}
		}
		return $normalized;
	}

	private static function parse_set_cookie_headers( array $headers ) {
		$set_cookie = array();
		if ( ! empty( $headers['set-cookie'] ) ) {
			$raw = (string) $headers['set-cookie'];
			$set_cookie = preg_split( '/,(?=[^;,]+=)/', $raw );
		}
		$results = array();
		foreach ( (array) $set_cookie as $cookie_line ) {
			$cookie_line = trim( (string) $cookie_line );
			if ( '' === $cookie_line ) {
				continue;
			}
			$parts = array_map( 'trim', explode( ';', $cookie_line ) );
			$name_value = array_shift( $parts );
			$name = sanitize_text_field( (string) strtok( $name_value, '=' ) );
			$flags = array(
				'name'      => $name,
				'secure'    => false,
				'httponly'  => false,
				'samesite'  => '',
			);
			foreach ( $parts as $part ) {
				$lower = strtolower( (string) $part );
				if ( 'secure' === $lower ) {
					$flags['secure'] = true;
				} elseif ( 'httponly' === $lower ) {
					$flags['httponly'] = true;
				} elseif ( 0 === strpos( $lower, 'samesite=' ) ) {
					$flags['samesite'] = sanitize_text_field( trim( substr( (string) $part, 9 ) ) );
				}
			}
			$results[] = $flags;
		}
		return $results;
	}


	private function build_profile_results( array $summary, $scan_profile = '' ) {
		$scan_profile = sanitize_key( (string) $scan_profile );
		if ( 'compliance' !== $scan_profile ) {
			return array();
		}
		$fail = ! empty( $summary['fail'] ) ? (int) $summary['fail'] : 0;
		$warn = ! empty( $summary['warn'] ) ? (int) $summary['warn'] : 0;
		$pass = ! empty( $summary['pass'] ) ? (int) $summary['pass'] : 0;
		$severity = $fail > 0 ? 'medium' : ( $warn > 0 ? 'low' : 'info' );
		/* translators: 1: Failed control count, 2: Warning control count, 3: Passed control count. */
		$details = sprintf( __( 'STIG/SRG baseline checks completed locally on the agent. Fail: %1$d, Warn: %2$d, Pass: %3$d.', 'aegisify-audit-agent' ), $fail, $warn, $pass );

		return array(
			array(
				'severity'       => $severity,
				'category'       => 'compliance',
				'title'          => __( 'Compliance baseline summary', 'aegisify-audit-agent' ),
				'details'        => $details,
				'recommendation' => $fail > 0 ? __( 'Review the failed STIG/SRG findings and apply the mapped fix text for each control.', 'aegisify-audit-agent' ) : __( 'Review warnings and keep the compliance baseline aligned with your desired posture.', 'aegisify-audit-agent' ),
				'evidence_json'  => wp_json_encode( array(
					'scan_profile' => $scan_profile,
					'summary'      => $summary,
				) ),
			)
		);
	}
}
