<?php
/**
 * Runtime service composition.
 *
 * @package LocalGuardScanner
 */

namespace LocalGuardScanner;

use LocalGuardScanner\Admin\AdminController;
use LocalGuardScanner\Admin\FindingExplanationCatalog;
use LocalGuardScanner\Admin\FindingLocationPresenter;
use LocalGuardScanner\Admin\FindingPresenter;
use LocalGuardScanner\Admin\FindingRemediationCatalog;
use LocalGuardScanner\Admin\DatabaseReviewQueryBuilder;
use LocalGuardScanner\Application\Coverage\CoverageReporter;
use LocalGuardScanner\Application\Diagnostics\EnvironmentDiagnostics;
use LocalGuardScanner\Application\Findings\FindingLifecycle;
use LocalGuardScanner\Application\Findings\FindingPersister;
use LocalGuardScanner\Application\Findings\SuppressionService;
use LocalGuardScanner\Application\Notifications\NotificationPolicy;
use LocalGuardScanner\Application\Maintenance\RetentionCleaner;
use LocalGuardScanner\Application\Scan\DatabaseScanService;
use LocalGuardScanner\Application\Scan\ScanLock;
use LocalGuardScanner\Application\Scan\ScanOrchestrator;
use LocalGuardScanner\Detection\Database\DatabaseDetectorFactory;
use LocalGuardScanner\Detection\Database\DatabaseInventory;
use LocalGuardScanner\Detection\Database\SafeSerializedReader;
use LocalGuardScanner\Detection\DetectorFactory;
use LocalGuardScanner\Domain\Detection\FindingFingerprint;
use LocalGuardScanner\Domain\Detection\RiskClassifier;
use LocalGuardScanner\Infrastructure\Database\FindingRepository;
use LocalGuardScanner\Infrastructure\Database\InventoryRepository;
use LocalGuardScanner\Infrastructure\Database\Migrator;
use LocalGuardScanner\Infrastructure\Database\ScanRepository;
use LocalGuardScanner\Infrastructure\Database\Schema;
use LocalGuardScanner\Infrastructure\Database\SuppressionRepository;
use LocalGuardScanner\Infrastructure\WordPress\Capabilities;
use LocalGuardScanner\Infrastructure\WordPress\CronScanRunner;
use LocalGuardScanner\Infrastructure\WordPress\CronScheduler;
use LocalGuardScanner\Infrastructure\WordPress\MailNotifier;
use LocalGuardScanner\Infrastructure\WordPress\MaintenanceRunner;
use LocalGuardScanner\Infrastructure\WordPress\ManualScanController;
use LocalGuardScanner\Infrastructure\WordPress\Settings;
use LocalGuardScanner\Infrastructure\WordPress\WordPressLockStore;

/**
 * Owns the plugin's runtime object graph.
 */
final class Plugin {
	/** @var self|null */
	private static $instance;
	/** @var ScanOrchestrator */
	private $orchestrator;
	/** @var Settings */
	private $settings;

	/**
	 * Build and register runtime services once.
	 *
	 * @return void
	 */
	public static function initialize() {
		if ( null !== self::$instance ) {
			return;
		}
		global $wpdb;
		if ( Schema::VERSION !== (string) get_site_option( 'lgs_schema_version', '' ) ) {
			Migrator::migrate();
		}

		$settings          = new Settings();
		$scans             = new ScanRepository( $wpdb );
		$inventory         = new InventoryRepository( $wpdb );
		$findings          = new FindingRepository( $wpdb );
		$database_scanner  = new DatabaseScanService( ( new DatabaseDetectorFactory() )->create() );
		$finding_persister = new FindingPersister( $findings, new RiskClassifier(), new FindingFingerprint(), new FindingLifecycle(), new SuppressionService(), new SuppressionRepository( $wpdb ) );
		$orchestrator      = new ScanOrchestrator(
			$scans,
			$inventory,
			$findings,
			new ScanLock( new WordPressLockStore() ),
			new DetectorFactory( LOCALGUARD_SCANNER_DIR ),
			new DatabaseInventory( $wpdb, new SafeSerializedReader() ),
			$database_scanner,
			$finding_persister,
			new MailNotifier( new NotificationPolicy(), $settings ),
			$settings
		);
		self::$instance    = new self( $orchestrator, $settings );
		self::$instance->register();
	}

	/**
	 * @param ScanOrchestrator $orchestrator Orchestrator.
	 * @param Settings         $settings     Settings.
	 */
	private function __construct( ScanOrchestrator $orchestrator, Settings $settings ) {
		$this->orchestrator = $orchestrator;
		$this->settings     = $settings;
	}

	/** @return void */
	private function register() {
		global $wpdb;

		( new ManualScanController( $this->orchestrator ) )->register();
		( new CronScanRunner( $this->orchestrator, $this->settings ) )->register();
		$location_presenter = new FindingLocationPresenter( $wpdb, new DatabaseReviewQueryBuilder() );
		( new AdminController( $this->orchestrator, $this->settings, new CoverageReporter(), new EnvironmentDiagnostics( $wpdb ), new FindingPresenter( new FindingExplanationCatalog(), $location_presenter, new FindingRemediationCatalog() ) ) )->register();
		( new MaintenanceRunner( new RetentionCleaner( new ScanRepository( $wpdb ), new FindingRepository( $wpdb ), new InventoryRepository( $wpdb ) ) ) )->register();

		$settings = $this->settings->all();
		if ( ! empty( $settings['daily_enabled'] ) ) {
			CronScheduler::schedule_daily( isset( $settings['daily_hour'] ) ? (int) $settings['daily_hour'] : 2 );
		}
		CronScheduler::schedule_cleanup();
		add_action( 'admin_post_localguard_save_settings', array( $this, 'save_settings' ) );
	}

	/** @return void */
	public function save_settings() {
		if ( ! Capabilities::current_user_can_manage() ) {
			wp_die( esc_html__( 'You are not allowed to manage LocalGuard Scanner.', 'localguard-scanner' ), '', array( 'response' => 403 ) );
		}
		check_admin_referer( 'localguard_scanner_settings' );
		$input = isset( $_POST['localguard_scanner'] ) ? wp_unslash( $_POST['localguard_scanner'] ) : array();
		$clean = $this->settings->sanitize( $input );
		update_site_option( Settings::OPTION, $clean );
		CronScheduler::clear_all();
		if ( ! empty( $clean['daily_enabled'] ) ) {
			CronScheduler::schedule_daily( (int) $clean['daily_hour'] );
		}
		CronScheduler::schedule_cleanup();
		$url = is_multisite() ? network_admin_url( 'admin.php?page=localguard-scanner-settings&updated=1' ) : admin_url( 'admin.php?page=localguard-scanner-settings&updated=1' );
		wp_safe_redirect( $url );
		exit;
	}
}
