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

class ASA_Hardening_Scan {

	public function run() {
		global $wp_version;

		$results            = array();
		$version_exposed    = has_action( 'wp_head', 'wp_generator' );
		$xmlrpc_enabled     = apply_filters( 'xmlrpc_enabled', true );
		$debug_enabled      = defined( 'WP_DEBUG' ) && WP_DEBUG;
		$file_edit_off      = defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT;
		$readme_exists      = file_exists( ABSPATH . 'readme.html' );
		$file_mods_off      = defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS;
		$ssl_admin          = defined( 'FORCE_SSL_ADMIN' ) && FORCE_SSL_ADMIN;
		$site_ssl           = is_ssl();
		$wp_debug_location  = self::locate_wp_config_constant( 'WP_DEBUG' );
		$file_edit_location = self::locate_wp_config_constant( 'DISALLOW_FILE_EDIT' );
		$file_mods_location = self::locate_wp_config_constant( 'DISALLOW_FILE_MODS' );
		$ssl_admin_location = self::locate_wp_config_constant( 'FORCE_SSL_ADMIN' );
		$readme_relative    = self::relative_path( ABSPATH . 'readme.html' );

		$results[] = array( 'severity' => $version_exposed ? 'low' : 'passed', 'category' => 'exposure', 'title' => 'WordPress version exposure', 'details' => $version_exposed ? 'wp_generator appears enabled.' : 'wp_generator not exposed in wp_head.' );
		$results[] = array( 'severity' => $xmlrpc_enabled ? 'low' : 'passed', 'category' => 'xml-rpc', 'title' => 'XML-RPC status', 'details' => $xmlrpc_enabled ? 'XML-RPC appears enabled.' : 'XML-RPC appears disabled.' );
		$results[] = array(
			'severity'  => $debug_enabled ? 'high' : 'passed',
			'category'  => 'configuration',
			'title'     => 'WP_DEBUG',
			'details'   => $debug_enabled ? 'WP_DEBUG is enabled.' : 'WP_DEBUG is disabled.',
			'file_name' => ! empty( $wp_debug_location['file_path'] ) ? $wp_debug_location['file_path'] : '',
			'file_path' => ! empty( $wp_debug_location['file_path'] ) ? $wp_debug_location['file_path'] : '',
			'line'      => ! empty( $wp_debug_location['line'] ) ? absint( $wp_debug_location['line'] ) : 0,
		);
		$results[] = array(
			'severity'  => $file_edit_off ? 'passed' : 'medium',
			'category'  => 'hardening',
			'title'     => 'File editor',
			'details'   => $file_edit_off ? 'DISALLOW_FILE_EDIT is enabled.' : 'Built-in theme/plugin editor may be enabled.',
			'file_name' => ! empty( $file_edit_location['file_path'] ) ? $file_edit_location['file_path'] : '',
			'file_path' => ! empty( $file_edit_location['file_path'] ) ? $file_edit_location['file_path'] : '',
			'line'      => ! empty( $file_edit_location['line'] ) ? absint( $file_edit_location['line'] ) : 0,
		);
		$results[] = array(
			'severity'  => $file_mods_off ? 'passed' : 'low',
			'category'  => 'hardening',
			'title'     => 'File modifications',
			'details'   => $file_mods_off ? 'DISALLOW_FILE_MODS is enabled.' : 'File modifications are allowed.',
			'file_name' => ! empty( $file_mods_location['file_path'] ) ? $file_mods_location['file_path'] : '',
			'file_path' => ! empty( $file_mods_location['file_path'] ) ? $file_mods_location['file_path'] : '',
			'line'      => ! empty( $file_mods_location['line'] ) ? absint( $file_mods_location['line'] ) : 0,
		);
		$results[] = array(
			'severity'  => $ssl_admin ? 'passed' : ( $site_ssl ? 'low' : 'medium' ),
			'category'  => 'tls',
			'title'     => 'Admin SSL enforcement',
			'details'   => $ssl_admin ? 'FORCE_SSL_ADMIN is enabled.' : ( $site_ssl ? 'FORCE_SSL_ADMIN is not enabled, but the current admin request is already using SSL.' : 'FORCE_SSL_ADMIN is not enabled.' ),
			'file_name' => ! empty( $ssl_admin_location['file_path'] ) ? $ssl_admin_location['file_path'] : '',
			'file_path' => ! empty( $ssl_admin_location['file_path'] ) ? $ssl_admin_location['file_path'] : '',
			'line'      => ! empty( $ssl_admin_location['line'] ) ? absint( $ssl_admin_location['line'] ) : 0,
		);
		$results[] = array(
			'severity'  => $readme_exists ? 'low' : 'passed',
			'category'  => 'exposure',
			'title'     => 'readme.html exposure',
			'details'   => $readme_exists ? 'readme.html exists in the web root.' : 'readme.html not found.',
			'file_name' => $readme_exists ? $readme_relative : '',
			'file_path' => $readme_exists ? $readme_relative : '',
			'line'      => $readme_exists ? 1 : 0,
		);
		$results[] = array( 'severity' => 'info', 'category' => 'version', 'title' => 'Detected WordPress version', 'details' => is_string( $wp_version ) ? $wp_version : 'Unknown' );

		return $results;
	}

	private static function locate_wp_config_constant( $constant_name ) {
		$constant_name = strtoupper( sanitize_key( (string) $constant_name ) );
		$candidates    = array(
			wp_normalize_path( ABSPATH . 'wp-config.php' ),
			wp_normalize_path( dirname( ABSPATH ) . '/wp-config.php' ),
		);
		$pattern = '/define\s*\(\s*["\']' . preg_quote( $constant_name, '/' ) . '["\']/i';

		foreach ( $candidates as $candidate ) {
			if ( ! is_file( $candidate ) || ! is_readable( $candidate ) ) {
				continue;
			}
			$contents = file_get_contents( $candidate ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Read-only local config lookup to map findings to exact file and line numbers.
			if ( false === $contents || '' === $contents ) {
				continue;
			}
			if ( preg_match( $pattern, $contents, $matches, PREG_OFFSET_CAPTURE ) ) {
				$offset = isset( $matches[0][1] ) ? absint( $matches[0][1] ) : 0;
				return array(
					'file_path' => self::relative_path( $candidate ),
					'line'      => 1 + substr_count( substr( $contents, 0, $offset ), "\n" ),
				);
			}
		}

		return array( 'file_path' => '', 'line' => 0 );
	}

	private static function relative_path( $path ) {
		$path = wp_normalize_path( (string) $path );
		if ( 0 === strpos( $path, wp_normalize_path( ABSPATH ) ) ) {
			return ltrim( substr( $path, strlen( wp_normalize_path( ABSPATH ) ) ), '/' );
		}
		return ltrim( $path, '/' );
	}
}
