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

class ASA_Report_Builder {

	const REPORT_OPTION = 'asa_cached_report';

	public static function build( array $scan_plugins = array(), array $scan_severities = array() ) {
		ASA_Logger::log(
			'scan',
			'info',
			'report_builder',
			'build_report',
			__( 'The agent report builder started assembling a full report.', 'aegisify-audit-agent' ),
			array(
				'selected_plugins'    => count( $scan_plugins ),
				'selected_severities' => array_values( array_unique( array_map( 'sanitize_key', array_map( 'strval', $scan_severities ) ) ) ),
			),
			10
		);

		$hardening_findings = ( new ASA_Hardening_Scan() )->run();
		ASA_Logger::log( 'scan', 'success', 'report_builder', 'hardening_scan', __( 'The hardening scan stage completed.', 'aegisify-audit-agent' ), array( 'finding_count' => is_array( $hardening_findings ) ? count( $hardening_findings ) : 0 ), 10 );
		$inventory_findings = ( new ASA_Inventory_Scan() )->run();
		ASA_Logger::log( 'scan', 'success', 'report_builder', 'inventory_scan', __( 'The inventory scan stage completed.', 'aegisify-audit-agent' ), array( 'finding_count' => is_array( $inventory_findings ) ? count( $inventory_findings ) : 0 ), 10 );
		$permission_findings = ( new ASA_Permission_Scan() )->run();
		ASA_Logger::log( 'scan', 'success', 'report_builder', 'permission_scan', __( 'The permission scan stage completed.', 'aegisify-audit-agent' ), array( 'finding_count' => is_array( $permission_findings ) ? count( $permission_findings ) : 0 ), 10 );
		$malware_findings = ( new ASA_Malware_Scan() )->run();
		ASA_Logger::log( 'scan', 'success', 'report_builder', 'malware_scan', __( 'The malware scan stage completed.', 'aegisify-audit-agent' ), array( 'finding_count' => is_array( $malware_findings ) ? count( $malware_findings ) : 0 ), 10 );
		$diff_findings = ( new ASA_Diff_Scan() )->run();
		ASA_Logger::log( 'scan', 'success', 'report_builder', 'diff_scan', __( 'The diff scan stage completed.', 'aegisify-audit-agent' ), array( 'finding_count' => is_array( $diff_findings ) ? count( $diff_findings ) : 0 ), 10 );
		$correlation_findings = ( new ASA_Aegisify_Correlation() )->run();
		ASA_Logger::log( 'scan', 'success', 'report_builder', 'correlation_scan', __( 'The correlation scan stage completed.', 'aegisify-audit-agent' ), array( 'finding_count' => is_array( $correlation_findings ) ? count( $correlation_findings ) : 0 ), 10 );

		$findings = array_merge(
			$hardening_findings,
			$inventory_findings,
			$permission_findings,
			$malware_findings,
			$diff_findings,
			$correlation_findings
		);

		$static_payload  = ( new ASA_Static_Scan( $scan_plugins ) )->build_payload( 'deep_code_analysis' );
		$static_findings = self::normalize_static_findings( ! empty( $static_payload['findings'] ) && is_array( $static_payload['findings'] ) ? $static_payload['findings'] : array() );
		ASA_Logger::log( 'scan', 'success', 'report_builder', 'static_scan', __( 'The deep code analysis stage completed.', 'aegisify-audit-agent' ), array( 'finding_count' => count( $static_findings ) ), 10 );
		if ( ! empty( $static_findings ) ) {
			$findings = array_merge( $findings, $static_findings );
		}

		$summary = array( 'critical' => 0, 'high' => 0, 'medium' => 0, 'low' => 0, 'passed_info' => 0 );
		foreach ( $findings as $finding ) {
			$sev = isset( $finding['severity'] ) ? sanitize_key( (string) $finding['severity'] ) : 'info';
			if ( isset( $summary[ $sev ] ) ) {
				$summary[ $sev ]++;
			} else {
				$summary['passed_info']++;
			}
		}
		ASA_Logger::log(
			'scan',
			'success',
			'report_builder',
			'build_report',
			__( 'The agent report builder completed the full report successfully.', 'aegisify-audit-agent' ),
			array( 'finding_count' => count( $findings ) ),
			10
		);

		return self::normalize_report(
			array(
				'generated_at_gmt' => gmdate( 'Y-m-d H:i:s' ),
				'scan_filters'    => array(
					'plugins'    => array_values( array_unique( array_map( 'plugin_basename', array_map( 'strval', $scan_plugins ) ) ) ),
					'severities' => array_values( array_unique( array_map( 'sanitize_key', array_map( 'strval', $scan_severities ) ) ) ),
				),
				'site'             => self::site_info(),
				'inventory'        => self::inventory_info(),
				'site_health'      => self::site_health_info(),
				'events'           => self::events_info(),
				'news'             => self::news_info(),
				'summary'          => $summary,
				'findings'         => $findings,
				'telemetry_policy' => ASA_Telemetry::get_capabilities(),
				'telemetry'        => ASA_Telemetry::build_payload(),
			)
		);
	}

	public static function empty_report() {
		return array(
			'generated_at_gmt' => '',
			'scan_filters'    => array(
				'plugins'    => array(),
				'severities' => array(),
			),
			'site'             => array(
				'home_url'         => home_url(),
				'wp_version'       => '',
				'php_version'      => PHP_VERSION,
				'server_software'  => '',
				'is_ssl'           => is_ssl(),
				'timezone'         => wp_timezone_string(),
				'uploads_writable' => false,
			),
			'inventory'        => array(
				'active_plugins'    => 0,
				'installed_plugins' => 0,
				'installed_themes'  => 0,
				'plugin_updates'    => 0,
			),
			'site_health'      => array( 'count' => 0 ),
			'events'           => array( 'cron_event_count' => 0 ),
			'news'             => array( 'count' => 0 ),
			'summary'          => array(
				'critical'    => 0,
				'high'        => 0,
				'medium'      => 0,
				'low'         => 0,
				'passed_info' => 0,
			),
			'findings'         => array(),
			'telemetry_policy' => array(),
			'telemetry'        => array(),
		);
	}

	public static function normalize_report( $report ) {
		$defaults = self::empty_report();
		$report   = is_array( $report ) ? $report : array();

		$normalized                     = wp_parse_args( $report, $defaults );
		$normalized['scan_filters']     = wp_parse_args( is_array( $normalized['scan_filters'] ) ? $normalized['scan_filters'] : array(), $defaults['scan_filters'] );
		$normalized['scan_filters']['plugins']    = is_array( $normalized['scan_filters']['plugins'] ) ? array_values( array_unique( array_map( 'plugin_basename', array_map( 'strval', $normalized['scan_filters']['plugins'] ) ) ) ) : array();
		$normalized['scan_filters']['severities'] = is_array( $normalized['scan_filters']['severities'] ) ? array_values( array_unique( array_map( 'sanitize_key', array_map( 'strval', $normalized['scan_filters']['severities'] ) ) ) ) : array();
		$normalized['site']             = wp_parse_args( is_array( $normalized['site'] ) ? $normalized['site'] : array(), $defaults['site'] );
		$normalized['inventory']        = wp_parse_args( is_array( $normalized['inventory'] ) ? $normalized['inventory'] : array(), $defaults['inventory'] );
		$normalized['site_health']      = wp_parse_args( is_array( $normalized['site_health'] ) ? $normalized['site_health'] : array(), $defaults['site_health'] );
		$normalized['events']           = wp_parse_args( is_array( $normalized['events'] ) ? $normalized['events'] : array(), $defaults['events'] );
		$normalized['news']             = wp_parse_args( is_array( $normalized['news'] ) ? $normalized['news'] : array(), $defaults['news'] );
		$normalized['summary']          = wp_parse_args( is_array( $normalized['summary'] ) ? $normalized['summary'] : array(), $defaults['summary'] );
		$normalized['findings']         = is_array( $normalized['findings'] ) ? array_values( $normalized['findings'] ) : array();
		$normalized['telemetry_policy'] = is_array( $normalized['telemetry_policy'] ) ? $normalized['telemetry_policy'] : array();
		$normalized['telemetry']        = is_array( $normalized['telemetry'] ) ? $normalized['telemetry'] : array();

		return $normalized;
	}

	public static function get_cached_report() {
		return self::normalize_report( get_option( self::REPORT_OPTION, array() ) );
	}

	public static function cache_report( array $report ) {
		update_option( self::REPORT_OPTION, self::normalize_report( $report ), false );
		ASA_Logger::log( 'scan', 'success', 'report_builder', 'cache_report', __( 'The built agent report was cached successfully.', 'aegisify-audit-agent' ), array( 'finding_count' => ! empty( $report['findings'] ) && is_array( $report['findings'] ) ? count( $report['findings'] ) : 0 ), 10 );
	}

	private static function normalize_static_findings( array $findings ) {
		$normalized = array();
		foreach ( $findings as $finding ) {
			if ( ! is_array( $finding ) ) {
				continue;
			}
			$details = '';
			if ( ! empty( $finding['details'] ) ) {
				$details = sanitize_text_field( (string) $finding['details'] );
			} elseif ( ! empty( $finding['description'] ) ) {
				$details = sanitize_text_field( (string) $finding['description'] );
			}

			$normalized[] = array(
				'severity'    => ! empty( $finding['severity'] ) ? sanitize_key( (string) $finding['severity'] ) : 'medium',
				'category'    => ! empty( $finding['category'] ) ? sanitize_key( (string) $finding['category'] ) : 'code',
				'title'       => ! empty( $finding['title'] ) ? sanitize_text_field( (string) $finding['title'] ) : __( 'Static analysis finding', 'aegisify-audit-agent' ),
				'details'     => $details,
				'plugin_slug' => ! empty( $finding['plugin_slug'] ) ? sanitize_key( (string) $finding['plugin_slug'] ) : '',
				'plugin_name' => ! empty( $finding['plugin_name'] ) ? sanitize_text_field( (string) $finding['plugin_name'] ) : '',
				'file_name'   => ! empty( $finding['file_path'] ) ? sanitize_text_field( (string) $finding['file_path'] ) : ( ! empty( $finding['file_name'] ) ? sanitize_text_field( (string) $finding['file_name'] ) : '' ),
				'file_path'   => ! empty( $finding['file_path'] ) ? sanitize_text_field( (string) $finding['file_path'] ) : '',
				'line'        => ! empty( $finding['line'] ) ? absint( $finding['line'] ) : 0,
				'end_line'    => ! empty( $finding['end_line'] ) ? absint( $finding['end_line'] ) : 0,
				'type'        => ! empty( $finding['type'] ) ? sanitize_key( strtolower( (string) $finding['type'] ) ) : 'warning',
				'code'        => ! empty( $finding['code'] ) ? sanitize_text_field( (string) $finding['code'] ) : ( ! empty( $finding['rule_key'] ) ? sanitize_text_field( (string) $finding['rule_key'] ) : '' ),
				'column'      => isset( $finding['column'] ) ? absint( $finding['column'] ) : 0,
				'engine'      => ! empty( $finding['engine'] ) ? sanitize_key( (string) $finding['engine'] ) : '',
				'language'    => ! empty( $finding['language'] ) ? sanitize_key( (string) $finding['language'] ) : '',
				'rule_key'    => ! empty( $finding['rule_key'] ) ? sanitize_text_field( (string) $finding['rule_key'] ) : '',
			);
		}
		return $normalized;
	}

	private static function site_info() {
		global $wp_version;
		$uploads = wp_get_upload_dir();
		return array(
			'home_url'         => home_url(),
			'wp_version'       => is_string( $wp_version ) ? $wp_version : '',
			'php_version'      => PHP_VERSION,
			'server_software'  => isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : 'Unknown',
			'is_ssl'           => is_ssl(),
			'timezone'         => wp_timezone_string(),
			'uploads_writable' => ! empty( $uploads['basedir'] ) && wp_is_writable( $uploads['basedir'] ),
		);
	}

	private static function inventory_info() {
		if ( ! function_exists( 'get_plugins' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}
		$plugins = get_plugins();
		$themes  = wp_get_themes();
		$updates = get_site_transient( 'update_plugins' );
		return array(
			'active_plugins'    => count( (array) get_option( 'active_plugins', array() ) ),
			'installed_plugins' => count( $plugins ),
			'installed_themes'  => count( $themes ),
			'plugin_updates'    => ! empty( $updates->response ) ? count( (array) $updates->response ) : 0,
		);
	}

	private static function site_health_info() {
		$count = 0;
		if ( class_exists( 'WP_Site_Health' ) ) {
			$tests = WP_Site_Health::get_tests();
			foreach ( $tests as $group ) {
				if ( is_array( $group ) ) {
					$count += count( $group );
				}
			}
		}
		return array( 'count' => $count );
	}

	private static function events_info() {
		$cron  = _get_cron_array();
		$count = 0;
		if ( is_array( $cron ) ) {
			foreach ( $cron as $timestamp => $hooks ) {
				foreach ( $hooks as $hook => $instances ) {
					$count += count( $instances );
				}
			}
		}
		return array( 'cron_event_count' => $count );
	}

	private static function news_info() {
		$items = 0;
		$feeds = get_transient( 'dash_v2_news' );
		if ( is_array( $feeds ) ) {
			$items = count( $feeds );
		}
		return array( 'count' => $items );
	}
}
