<?php
/**
 * Installer — Activation, Deactivation, and DB Schema
 *
 * Uses dbDelta() for safe, incremental schema upgrades.
 * All tables use the wp_ prefix and charset/collation from WP globals.
 *
 * @package GuardianShield
 */

namespace GuardianShield;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Installer {

	// ── Activation ───────────────────────────────
	public static function activate( bool $network_wide = false ): void {
		if ( $network_wide && is_multisite() ) {
			// Activate for every site in the network.
			foreach ( get_sites( [ 'fields' => 'ids', 'number' => 0 ] ) as $blog_id ) {
				switch_to_blog( $blog_id );
				self::single_activate();
				restore_current_blog();
			}
		} else {
			self::single_activate();
		}
	}

	// ── Single Site Activation ───────────────────
	public static function single_activate(): void {
		self::create_tables();
		self::set_default_options();
		self::schedule_crons();
		self::create_directories();
		self::store_activation_hash();

		// Flush rewrite rules after potentially hiding wp-login.
		flush_rewrite_rules();

		// Store activation timestamp for security score baseline.
		update_option( 'gs_activated_at', time(), false );
		update_option( 'gs_db_version', GS_VERSION, false );
	}

	// ── Deactivation ─────────────────────────────
	public static function deactivate(): void {
		// Remove scheduled crons but DO NOT delete data — user might reactivate.
		wp_clear_scheduled_hook( 'gs_scheduled_malware_scan' );
		wp_clear_scheduled_hook( 'gs_scheduled_integrity_check' );
		wp_clear_scheduled_hook( 'gs_cleanup' );

		flush_rewrite_rules();
	}

	// ── Uninstall (called from uninstall.php) ────
	public static function uninstall(): void {
		global $wpdb;

		// Remove all plugin options.
		$option_keys = $wpdb->get_col(
			"SELECT option_name FROM {$wpdb->options}
			 WHERE option_name LIKE 'gs_%'"
		);
		foreach ( $option_keys as $key ) {
			delete_option( $key );
		}

		// Drop custom tables.
		$tables = [
			$wpdb->prefix . 'gs_attack_logs',
			$wpdb->prefix . 'gs_login_attempts',
			$wpdb->prefix . 'gs_file_hashes',
			$wpdb->prefix . 'gs_firewall_events',
			$wpdb->prefix . 'gs_ip_rules',
			$wpdb->prefix . 'gs_scan_results',
		];
		foreach ( $tables as $table ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery
			$wpdb->query( "DROP TABLE IF EXISTS `{$table}`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		}

		// Remove transients.
		$wpdb->query(
			"DELETE FROM {$wpdb->options}
			 WHERE option_name LIKE '_transient_gs_%'
			    OR option_name LIKE '_transient_timeout_gs_%'"
		);

		// Remove log & quarantine directories.
		self::remove_directory( GS_LOG_DIR );
		self::remove_directory( GS_QUARANTINE );
	}

	// ── DB Schema ────────────────────────────────
	/**
	 * Creates / upgrades plugin tables via dbDelta().
	 * dbDelta is safe to call on every activation — it only makes changes
	 * when the schema differs from what's on disk.
	 */
	public static function create_tables(): void {
		global $wpdb;
		require_once ABSPATH . 'wp-admin/includes/upgrade.php';

		$charset_collate = $wpdb->get_charset_collate();
		$prefix          = $wpdb->prefix;

		// ── 1. Attack Logs ──────────────────────────
		// General log for all detected threats.
		$sql_attack = "CREATE TABLE {$prefix}gs_attack_logs (
			id           BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
			ip_address   VARCHAR(45)         NOT NULL DEFAULT '',
			attack_type  VARCHAR(100)        NOT NULL DEFAULT '',
			severity     TINYINT(1)          NOT NULL DEFAULT 1 COMMENT '1=low,2=medium,3=high,4=critical',
			description  TEXT                NOT NULL,
			request_uri  TEXT                NOT NULL,
			user_agent   TEXT                NOT NULL,
			request_data LONGTEXT                     DEFAULT NULL,
			country_code CHAR(2)                      DEFAULT NULL,
			threat_score TINYINT(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'AI behavior score 0-100',
			blocked      TINYINT(1)          NOT NULL DEFAULT 0,
			created_at   DATETIME            NOT NULL DEFAULT CURRENT_TIMESTAMP,
			PRIMARY KEY  (id),
			KEY ip_address  (ip_address),
			KEY attack_type (attack_type),
			KEY severity    (severity),
			KEY created_at  (created_at),
			KEY blocked     (blocked)
		) {$charset_collate};";

		// ── 2. Login Attempts ───────────────────────
		$sql_login = "CREATE TABLE {$prefix}gs_login_attempts (
			id           BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
			ip_address   VARCHAR(45)         NOT NULL DEFAULT '',
			username     VARCHAR(200)        NOT NULL DEFAULT '',
			result       ENUM('success','failure','blocked') NOT NULL DEFAULT 'failure',
			user_agent   TEXT                         DEFAULT NULL,
			country_code CHAR(2)                      DEFAULT NULL,
			two_fa_used  TINYINT(1)          NOT NULL DEFAULT 0,
			created_at   DATETIME            NOT NULL DEFAULT CURRENT_TIMESTAMP,
			PRIMARY KEY  (id),
			KEY ip_address (ip_address),
			KEY username   (username(100)),
			KEY result     (result),
			KEY created_at (created_at)
		) {$charset_collate};";

		// ── 3. File Hashes (integrity monitoring) ──
		$sql_hashes = "CREATE TABLE {$prefix}gs_file_hashes (
			id           BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
			file_path    VARCHAR(1000)       NOT NULL DEFAULT '',
			file_hash    CHAR(64)            NOT NULL DEFAULT '' COMMENT 'SHA-256',
			file_size    BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
			file_type    VARCHAR(50)         NOT NULL DEFAULT 'core' COMMENT 'core|plugin|theme|upload',
			status       ENUM('clean','modified','new','deleted') NOT NULL DEFAULT 'clean',
			last_checked DATETIME            NOT NULL DEFAULT CURRENT_TIMESTAMP,
			alerted      TINYINT(1)          NOT NULL DEFAULT 0,
			PRIMARY KEY  (id),
			UNIQUE KEY file_path (file_path(500)),
			KEY status       (status),
			KEY file_type    (file_type),
			KEY last_checked (last_checked)
		) {$charset_collate};";

		// ── 4. Firewall Events ──────────────────────
		$sql_fw = "CREATE TABLE {$prefix}gs_firewall_events (
			id           BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
			ip_address   VARCHAR(45)         NOT NULL DEFAULT '',
			rule_matched VARCHAR(200)        NOT NULL DEFAULT '',
			action       ENUM('block','allow','log') NOT NULL DEFAULT 'block',
			request_uri  TEXT                NOT NULL,
			payload      TEXT                         DEFAULT NULL,
			created_at   DATETIME            NOT NULL DEFAULT CURRENT_TIMESTAMP,
			PRIMARY KEY  (id),
			KEY ip_address   (ip_address),
			KEY rule_matched (rule_matched),
			KEY created_at   (created_at)
		) {$charset_collate};";

		// ── 5. IP Rules (blacklist/whitelist) ───────
		$sql_ip = "CREATE TABLE {$prefix}gs_ip_rules (
			id           BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
			ip_address   VARCHAR(45)         NOT NULL DEFAULT '' COMMENT 'Supports CIDR notation',
			rule_type    ENUM('blacklist','whitelist','temp_block') NOT NULL DEFAULT 'blacklist',
			reason       VARCHAR(500)                 DEFAULT NULL,
			expires_at   DATETIME                     DEFAULT NULL COMMENT 'NULL = permanent',
			created_by   BIGINT(20) UNSIGNED          DEFAULT NULL COMMENT 'WP user ID; NULL = auto',
			created_at   DATETIME            NOT NULL DEFAULT CURRENT_TIMESTAMP,
			PRIMARY KEY  (id),
			UNIQUE KEY ip_rule (ip_address, rule_type),
			KEY rule_type  (rule_type),
			KEY expires_at (expires_at)
		) {$charset_collate};";

		// ── 6. Scan Results ─────────────────────────
		$sql_scan = "CREATE TABLE {$prefix}gs_scan_results (
			id           BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
			scan_type    ENUM('malware','integrity','full') NOT NULL DEFAULT 'malware',
			file_path    VARCHAR(1000)       NOT NULL DEFAULT '',
			threat_type  VARCHAR(200)        NOT NULL DEFAULT '',
			threat_detail TEXT                        DEFAULT NULL,
			severity     TINYINT(1)          NOT NULL DEFAULT 2,
			status       ENUM('detected','quarantined','ignored','clean') NOT NULL DEFAULT 'detected',
			scan_at      DATETIME            NOT NULL DEFAULT CURRENT_TIMESTAMP,
			PRIMARY KEY  (id),
			KEY scan_type  (scan_type),
			KEY severity   (severity),
			KEY status     (status),
			KEY scan_at    (scan_at)
		) {$charset_collate};";

		dbDelta( $sql_attack );
		dbDelta( $sql_login );
		dbDelta( $sql_hashes );
		dbDelta( $sql_fw );
		dbDelta( $sql_ip );
		dbDelta( $sql_scan );
	}

	// ── Default Options ──────────────────────────
	private static function set_default_options(): void {
		$defaults = [
			// Login security
			'gs_max_login_attempts'    => 5,
			'gs_lockout_duration'      => 30,    // minutes
			'gs_lockout_notify'        => true,
			'gs_hide_login_url'        => false,
			'gs_custom_login_slug'     => 'secure-login',
			'gs_captcha_enabled'       => false,

			// Firewall
			'gs_firewall_enabled'      => true,
			'gs_block_sqli'            => true,
			'gs_block_xss'             => true,
			'gs_block_bad_bots'        => true,
			'gs_block_xmlrpc'          => false,
			'gs_rate_limit_enabled'    => true,
			'gs_rate_limit_requests'   => 200,   // per minute
			'gs_rate_limit_window'     => 60,    // seconds

			// Scanner
			'gs_scan_enabled'          => true,
			'gs_scan_frequency'        => 'daily',
			'gs_scan_uploads'          => true,
			'gs_quarantine_enabled'    => true,

			// File monitor
			'gs_file_monitor_enabled'  => true,
			'gs_monitor_core'          => true,
			'gs_monitor_plugins'       => true,
			'gs_monitor_themes'        => true,

			// Security headers
			'gs_headers_enabled'       => true,
			'gs_hsts_enabled'          => true,
			'gs_hsts_max_age'          => 31536000,
			'gs_csp_enabled'           => true,

			// Alerts
			'gs_email_alerts'          => true,
			'gs_alert_email'           => get_option( 'admin_email' ),
			'gs_alert_on_bruteforce'   => true,
			'gs_alert_on_malware'      => true,
			'gs_alert_on_file_change'  => true,
			'gs_alert_on_username_rename' => true,

			// Username hardening
			'gs_rename_admin_user'     => false,

			// Misc
			'gs_log_retention_days'    => 30,
			'gs_geoip_enabled'         => false,
			'gs_cloudflare_mode'       => false,
		];

		foreach ( $defaults as $key => $value ) {
			// add_option does nothing if option already exists — safe for upgrades.
			add_option( $key, $value, '', false );
		}
	}

	// ── Cron Events ──────────────────────────────
	private static function schedule_crons(): void {
		if ( ! wp_next_scheduled( 'gs_scheduled_malware_scan' ) ) {
			$frequency = get_option( 'gs_scan_frequency', 'daily' );
			wp_schedule_event( time(), $frequency, 'gs_scheduled_malware_scan' );
		}

		if ( ! wp_next_scheduled( 'gs_scheduled_integrity_check' ) ) {
			wp_schedule_event( time(), 'twicedaily', 'gs_scheduled_integrity_check' );
		}

		if ( ! wp_next_scheduled( 'gs_cleanup' ) ) {
			wp_schedule_event( time(), 'daily', 'gs_cleanup' );
		}
	}

	// ── Directories ──────────────────────────────
	private static function create_directories(): void {
		$dirs = [ GS_LOG_DIR, GS_QUARANTINE ];
		foreach ( $dirs as $dir ) {
			if ( ! is_dir( $dir ) ) {
				wp_mkdir_p( $dir );
				file_put_contents( $dir . '.htaccess', 'Deny from all' );
				file_put_contents( $dir . 'index.php', '<?php // Silence is golden.' );
			}
		}
	}

	// ── Activation Fingerprint ───────────────────
	/**
	 * Store a random secret used for internal HMAC operations.
	 * Only generated once — preserved across updates.
	 */
	private static function store_activation_hash(): void {
		if ( ! get_option( 'gs_secret_key' ) ) {
			update_option( 'gs_secret_key', wp_generate_password( 64, true, true ), false );
		}
	}

	// ── Helpers ──────────────────────────────────
	private static function remove_directory( string $dir ): void {
		if ( ! is_dir( $dir ) ) {
			return;
		}
		$files = new \RecursiveIteratorIterator(
			new \RecursiveDirectoryIterator( $dir, \RecursiveDirectoryIterator::SKIP_DOTS ),
			\RecursiveIteratorIterator::CHILD_FIRST
		);
		foreach ( $files as $fileinfo ) {
			$fn = $fileinfo->isDir() ? 'rmdir' : 'unlink';
			$fn( $fileinfo->getRealPath() );
		}
		rmdir( $dir );
	}
}
