<?php

declare(strict_types=1);

namespace SiteFort\Modules\Reporting;

use SiteFort\Modules\Notifications\NotificationService;

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

/**
 * Email Digest - sends periodic summary emails of security activity.
 *
 * Supports daily, weekly, and monthly frequencies. Collects stats from the
 * audit log and scan results, then renders a branded HTML email.
 *
 */
class EmailDigest {

	private array $settings;

	/**
	 * Escape a plugin-owned table identifier for direct interpolation.
	 */
	private static function table_identifier( string $table ): string {
		if ( $table === '' || strlen( $table ) > 64 || ! preg_match( '/^[A-Za-z0-9_]+$/', $table ) ) {
			throw new \InvalidArgumentException( 'Invalid table identifier.' );
		}

		return '`' . str_replace( '`', '``', $table ) . '`';
	}

	public function __construct( array $settings ) {
		$this->settings = $settings;

		// Register custom cron schedules that WordPress does not ship with.
		add_filter( 'cron_schedules', [ $this, 'register_cron_schedules' ] );
	}

	/**
	 * Register weekly and monthly cron schedules.
	 *
	 * WordPress only provides 'hourly', 'twicedaily', and 'daily' out of the box.
	 *
	 * @param array $schedules Existing cron schedules.
	 * @return array Modified schedules.
	 */
	public function register_cron_schedules( array $schedules ): array {
		if ( ! isset( $schedules['weekly'] ) ) {
			$schedules['weekly'] = [
				'interval' => 604800,
				'display'  => 'Once Weekly',
			];
		}
		if ( ! isset( $schedules['monthly'] ) ) {
			$schedules['monthly'] = [
				'interval' => 2592000,
				'display'  => 'Once Monthly',
			];
		}
		return $schedules;
	}

	/**
	 * Send the periodic digest email.
	 * Hooked to sitefort_email_digest cron.
	 */
	public function send_digest(): void {
		if ( empty( $this->settings['digest_enabled'] ) ) {
			return;
		}

		$recipient = NotificationService::normalize_mail_recipients( $this->settings['digest_recipient'] ?? '' );
		if ( empty( $recipient ) ) {
			$recipient = NotificationService::normalize_mail_recipients( get_option( 'admin_email' ) );
		}
		$period    = $this->settings['digest_frequency'] ?? 'weekly';

		$stats = $this->collect_stats( $period );

		$html    = $this->render_digest( $stats, $period );
		$brand   = NotificationService::get_branding_context();
		$subject = sprintf(
			'[%s] %s Security Digest - %s',
			(string) $brand['brand_name'],
			ucfirst( $period ),
			get_bloginfo( 'name' )
		);

		$sent = NotificationService::send_html_mail( $recipient, $subject, $html, (string) $brand['email_from_name'] );
		if ( ! $sent && function_exists( 'sitefort_log' ) ) {
			sitefort_log( sprintf( '[%s Reporting] Digest email delivery failed.', (string) $brand['brand_name'] ) );
		}
	}

	private function collect_stats( string $period ): array {
		$now    = time();
		$ranges = [
			'daily'   => 86400,
			'weekly'  => 604800,
			'monthly' => 2592000,
		];
		$since  = $now - ( $ranges[ $period ] ?? 604800 );

		global $wpdb;
		$table = self::table_identifier( $wpdb->prefix . 'sitefort_audit_log' );
		$table_name = trim( $table, '`' );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Checks existence of a plugin-owned audit table before collecting digest metrics.
		$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) );

		$stats = [
			'period'          => $period,
			'since'           => $since,
			'until'           => $now,
			'total_events'    => 0,
			'firewall_blocks' => 0,
			'login_failures'  => 0,
			'scan_findings'   => 0,
			'user_changes'    => 0,
			'top_blocked_ips' => [],
		];

		if ( $table_exists ) {
			// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			$stats['total_events'] = (int) $wpdb->get_var( $wpdb->prepare(
				"SELECT COUNT(*) FROM {$table} WHERE created_at >= %d",
				$since
			) );
			$stats['firewall_blocks'] = (int) $wpdb->get_var( $wpdb->prepare(
				"SELECT COUNT(*) FROM {$table} WHERE event = 'Firewall Block' AND created_at >= %d",
				$since
			) );
			$stats['login_failures'] = (int) $wpdb->get_var( $wpdb->prepare(
				"SELECT COUNT(*) FROM {$table} WHERE event = 'Login Failed' AND created_at >= %d",
				$since
			) );
			// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		}

		$published_id = \SiteFort\Includes\SettingsStore::get_field( \SiteFort\Includes\SettingsStore::COL_STATE, 'published_scan_id', null );
		if ( $published_id && class_exists( '\SiteFort\Modules\Scanner\Repository\IssueRepository' ) ) {
			$counts                 = \SiteFort\Modules\Scanner\Repository\IssueRepository::getCounts();
			$stats['scan_findings'] = $counts['active'] ?? 0;
		}

		return $stats;
	}

	private function render_digest( array $stats, string $period ): string {
		$period_label = ucfirst( $period );
		$timezone     = wp_timezone();
		$date_range   = sprintf(
			'%s - %s (%s)',
			wp_date( 'M j', (int) $stats['since'], $timezone ),
			wp_date( 'M j', (int) $stats['until'], $timezone ),
			$timezone->getName()
		);

		$data = NotificationService::build_template_data(
			$period_label . ' Security Digest',
			[
				'period_label' => $period_label,
				'date_range'   => $date_range,
				'stats'        => $stats,
			]
		);

		return NotificationService::render_template_file(
			dirname( __DIR__ ) . '/Notifications/templates/security-digest.php',
			$data
		);
	}

	public function schedule_digest(): void {
		$hook = 'sitefort_email_digest';

		if ( empty( $this->settings['digest_enabled'] ) ) {
			wp_clear_scheduled_hook( $hook );
			return;
		}

		$frequency  = $this->settings['digest_frequency'] ?? 'weekly';
		$recurrence = $frequency === 'daily' ? 'daily' : ( $frequency === 'monthly' ? 'monthly' : 'weekly' );

		// Only reschedule if the event is not already scheduled with the correct recurrence.
		$next = wp_next_scheduled( $hook );
		if ( $next ) {
			$existing_schedule = wp_get_schedule( $hook );
			if ( $existing_schedule === $recurrence ) {
				// Already scheduled with the correct frequency - no changes needed.
				return;
			}
			// Frequency changed: clear old schedule and set the new one.
			wp_clear_scheduled_hook( $hook );
		}

		wp_schedule_event( time(), $recurrence, $hook );
	}
}
