<?php

namespace WPProofShield\Support;

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

use DateTimeImmutable;
use DateTimeZone;
use WPProofShield\Security\StatsStore;
use WPProofShield\Support\FeatureAccess\FeatureAccess;

final class AdminMailReporter {

	private const WEEKLY_HOOK = 'wpsh_weekly_stats_report';

	public function __construct(
		private Settings $settings,
		private StatsStore $stats,
		private FeatureAccess $feature_access
	) {
	}

	public function register(): void {
		add_filter( 'cron_schedules', array( $this, 'registerWeeklySchedule' ) );


		// Free build keeps no locked scheduling path for premium reports.
		wp_clear_scheduled_hook( self::WEEKLY_HOOK );
	}

	public function registerWeeklySchedule( array $schedules ): array {
		if ( ! isset( $schedules['weekly'] ) ) {
			$schedules['weekly'] = array(
				'interval' => 7 * DAY_IN_SECONDS,
				'display'  => 'Once Weekly',
			);
		}

		return $schedules;
	}

	public function syncSchedules(): void {
		$this->syncWeeklySchedule();
	}

	public function nextMondayAtEightTimestamp( ?int $now = null, ?DateTimeZone $timezone = null ): int {
		$now       = $now ?? time();
		$timezone  = $timezone ?? $this->siteTimezone();
		$base      = ( new DateTimeImmutable( '@' . $now ) )->setTimezone( $timezone );
		$candidate = $base->setTime( 8, 0 );
		$dayOfWeek = (int) $candidate->format( 'N' );

		if ( $dayOfWeek === 1 ) {
			if ( $candidate->getTimestamp() > $now ) {
				return $candidate->getTimestamp();
			}

			return $candidate->modify( '+7 days' )->getTimestamp();
		}

		$daysUntilMonday = 8 - $dayOfWeek;
		return $candidate->modify( '+' . $daysUntilMonday . ' days' )->getTimestamp();
	}

	public function sendWeeklyReport(): void {
		if ( ! $this->canSendWeeklyReport() ) {
			return;
		}

		$admin_email = sanitize_email( (string) get_option( 'admin_email', '' ) );
		if ( $admin_email === '' ) {
			return;
		}

		$language        = UiLanguage::normalize( (string) $this->settings->get( 'ui_language', UiLanguage::DEFAULT ) );
		$dashboard       = $this->stats->getDashboard( '7d' );
		$stats_url       = admin_url( 'admin.php?page=wpsh-stats' );
		$logging_enabled = $this->settings->isEnabled( 'logging_enabled' );

		$mail = $this->buildWeeklyReportMail( $language, $dashboard, $logging_enabled, $stats_url );
		wp_mail( $admin_email, $mail['subject'], $mail['body'] );
	}

	/**
	 * @param array<string,mixed> $dashboard
	 * @return array{subject:string,body:string}
	 */
	public function buildWeeklyReportMail( string $language, array $dashboard, bool $logging_enabled, string $stats_url ): array {
		$text = $this->mailText( UiLanguage::normalize( $language ) );
		if ( ! $logging_enabled ) {
			return array(
				'subject' => $text['weekly_subject_disabled'],
				'body'    => $text['weekly_disabled_intro'] . "\n\n" .
					$text['settings_link'] . ': ' . admin_url( 'admin.php?page=wpsh-settings' ),
			);
		}

		$summary      = isset( $dashboard['summary'] ) && is_array( $dashboard['summary'] ) ? $dashboard['summary'] : array();
		$totals       = isset( $summary['totals'] ) && is_array( $summary['totals'] ) ? $summary['totals'] : array();
		$blocked      = (int) ( $totals['blocked'] ?? 0 );
		$ok           = (int) ( $totals['ok'] ?? 0 );
		$total        = $blocked + $ok;
		$blocked_rate = $total > 0 ? round( ( $blocked / $total ) * 100, 1 ) : 0.0;
		$ok_rate      = $total > 0 ? round( ( $ok / $total ) * 100, 1 ) : 0.0;
		$top_context  = (string) ( $dashboard['top_context'] ?? '-' );
		$median       = (int) ( $summary['median_solve_ms'] ?? 0 );
		$reasons      = isset( $dashboard['reasons'] ) && is_array( $dashboard['reasons'] ) ? $dashboard['reasons'] : array();
		$contexts     = isset( $summary['by_context'] ) && is_array( $summary['by_context'] ) ? $summary['by_context'] : array();
		$assessment   = $this->assessmentText( $blocked, $total, $blocked_rate, $text );

		$reason_lines = array();
		$reason_rank  = 1;
		foreach ( array_slice( $reasons, 0, 5, true ) as $reason => $count ) {
			$reason_label = $this->reasonLabel( (string) $reason, UiLanguage::normalize( $language ) );
			if ( $reason_label === (string) $reason ) {
				$reason_lines[] = $reason_rank . '. ' . (string) $reason . ': ' . (int) $count;
			} else {
				$reason_lines[] = $reason_rank . '. ' . $reason_label . ' (' . (string) $reason . '): ' . (int) $count;
			}
			++$reason_rank;
		}
		if ( $reason_lines === array() ) {
			$reason_lines[] = $text['none'];
		}

		$context_lines = array();
		foreach ( $contexts as $context => $row ) {
			$context_ok      = (int) ( $row['ok'] ?? 0 );
			$context_blocked = (int) ( $row['blocked'] ?? 0 );
			$context_label   = $this->contextLabel( (string) $context, UiLanguage::normalize( $language ) );
			if ( $context_label === (string) $context ) {
				$context_lines[] = (string) $context . ': ok=' . $context_ok . ', blocked=' . $context_blocked;
			} else {
				$context_lines[] = $context_label . ' (' . (string) $context . '): ok=' . $context_ok . ', blocked=' . $context_blocked;
			}
		}
		if ( $context_lines === array() ) {
			$context_lines[] = $text['none'];
		}

		$next_steps = $this->nextStepLines( $blocked_rate, $reasons, $text );

		$body = implode(
			"\n",
			array(
				$text['weekly_intro'],
				'',
				$text['summary'] . ':',
				$text['period'] . ': 7 ' . $text['days'],
				$text['decisions'] . ': ' . $total,
				$text['blocked'] . ': ' . $blocked,
				$text['allowed'] . ': ' . $ok,
				$text['block_rate'] . ': ' . $this->formatPercent( $blocked_rate, UiLanguage::normalize( $language ) ),
				$text['allowed_rate'] . ': ' . $this->formatPercent( $ok_rate, UiLanguage::normalize( $language ) ),
				$text['top_context'] . ': ' . $this->topContextLine( $top_context, UiLanguage::normalize( $language ) ),
				$text['median_solve'] . ': ' . ( $median > 0 ? ( $median . ' ms' ) : $text['none'] ),
				'',
				$text['assessment'] . ':',
				$assessment,
				'',
				$text['top_reasons'] . ':',
				implode( "\n", $reason_lines ),
				'',
				$text['contexts'] . ':',
				implode( "\n", $context_lines ),
				'',
				$text['next_steps'] . ':',
				implode( "\n", $next_steps ),
				'',
				$text['stats_link'] . ': ' . $stats_url,
			)
		);

		return array(
			'subject' => sprintf( $text['weekly_subject'], $blocked ),
			'body'    => $body,
		);
	}

	private function syncWeeklySchedule(): void {
		$next = wp_next_scheduled( self::WEEKLY_HOOK );
		if ( ! $this->canSendWeeklyReport() ) {
			if ( $next ) {
				wp_clear_scheduled_hook( self::WEEKLY_HOOK );
			}
			return;
		}

		if ( ! $next ) {
			wp_schedule_event( $this->nextMondayAtEightTimestamp(), 'weekly', self::WEEKLY_HOOK );
		}
	}

	private function canSendWeeklyReport(): bool {
		if ( ! false ) {
			return false;
		}

		return $this->settings->isEnabled( 'stats_weekly_email_enabled' );
	}

	private function topContextLine( string $context, string $language ): string {
		if ( $context === '' || $context === '-' ) {
			return '-';
		}

		$label = $this->contextLabel( $context, $language );
		if ( $label === $context ) {
			return $context;
		}

		return $label . ' (' . $context . ')';
	}

	private function formatPercent( float $value, string $language ): string {
		$rounded = round( $value, 1 );
		if ( (float) (int) $rounded === $rounded ) {
			return (string) (int) $rounded . '%';
		}

		$decimal_separator = $language === 'de' ? ',' : '.';
		return number_format( $rounded, 1, $decimal_separator, '' ) . '%';
	}

	/**
	 * @param array<string,string> $text
	 */
	private function assessmentText( int $blocked, int $total, float $blocked_rate, array $text ): string {
		if ( $total === 0 ) {
			return $text['assessment_no_data'];
		}

		if ( $blocked >= 50 && $blocked_rate >= 95 ) {
			return $text['assessment_high_pressure'];
		}

		if ( $blocked_rate >= 80 ) {
			return $text['assessment_elevated_pressure'];
		}

		if ( $blocked_rate >= 50 ) {
			return $text['assessment_moderate_pressure'];
		}

		return $text['assessment_low_pressure'];
	}

	/**
	 * @param array<string,mixed>   $reasons
	 * @param array<string,string> $text
	 * @return array<int,string>
	 */
	private function nextStepLines( float $blocked_rate, array $reasons, array $text ): array {
		$steps   = array();
		$blocked = array_sum(
			array_map(
				static fn( mixed $count ): int => (int) $count,
				$reasons
			)
		);

		$js_required = isset( $reasons['wpsh_js_required'] ) ? (int) $reasons['wpsh_js_required'] : 0;
		if ( $blocked > 0 && $js_required > 0 && ( $js_required / $blocked ) >= 0.7 ) {
			$steps[] = '1. ' . $text['step_js_required'];
		}

		$rate_limited = isset( $reasons['wpsh_rate_limited'] ) ? (int) $reasons['wpsh_rate_limited'] : 0;
		if ( $rate_limited > 0 ) {
			$steps[] = ( count( $steps ) + 1 ) . '. ' . $text['step_rate_limit'];
		}

		if ( $blocked_rate >= 95 ) {
			$steps[] = ( count( $steps ) + 1 ) . '. ' . $text['step_false_positive_check'];
		}

		$steps[] = ( count( $steps ) + 1 ) . '. ' . $text['step_open_stats'];
		return $steps;
	}

	private function reasonLabel( string $reason, string $language ): string {
		$labels_de = array(
			'wpsh_js_required'    => 'JavaScript war deaktiviert/fehlte',
			'wpsh_rate_limited'   => 'Anfrage-Limit wurde erreicht',
			'wpsh_honeypot'       => 'Honeypot-Feld wurde ausgefüllt',
			'wpsh_expired'        => 'Challenge war abgelaufen',
			'wpsh_invalid_pow'    => 'Proof-of-Work war ungültig',
			'wpsh_replay'         => 'Replay-Versuch erkannt',
			'wpsh_missing_fields' => 'Erforderliche Schutzfelder fehlten',
		);
		$labels_en = array(
			'wpsh_js_required'    => 'JavaScript was disabled/missing',
			'wpsh_rate_limited'   => 'Request rate limit reached',
			'wpsh_honeypot'       => 'Honeypot field was filled',
			'wpsh_expired'        => 'Challenge was expired',
			'wpsh_invalid_pow'    => 'Proof-of-Work was invalid',
			'wpsh_replay'         => 'Replay attempt detected',
			'wpsh_missing_fields' => 'Required protection fields were missing',
		);

		$labels = $language === 'de' ? $labels_de : $labels_en;
		return isset( $labels[ $reason ] ) ? $labels[ $reason ] : $reason;
	}

	private function contextLabel( string $context, string $language ): string {
		$labels_de = array(
			'login'           => 'WordPress-Login',
			'login:no_js'     => 'WordPress-Login ohne JavaScript',
			'comment'         => 'Kommentarformular',
			'comment:no_js'   => 'Kommentarformular ohne JavaScript',
			'form:no_js'      => 'Kontaktformular ohne JavaScript',
			'form:no_js_view' => 'No-JS-Hinweis für Formulare angezeigt',
			'form:cf7'        => 'Contact Form 7',
			'form:elementor'  => 'Elementor-Formular',
			'form:wpforms'    => 'WPForms-Formular',
			'woo_login'       => 'WooCommerce-Login',
		);
		$labels_en = array(
			'login'           => 'WordPress login',
			'login:no_js'     => 'WordPress login without JavaScript',
			'comment'         => 'Comment form',
			'comment:no_js'   => 'Comment form without JavaScript',
			'form:no_js'      => 'Contact form without JavaScript',
			'form:no_js_view' => 'No-JS form notice shown',
			'form:cf7'        => 'Contact Form 7',
			'form:elementor'  => 'Elementor form',
			'form:wpforms'    => 'WPForms form',
			'woo_login'       => 'WooCommerce login',
		);

		$labels = $language === 'de' ? $labels_de : $labels_en;
		if ( isset( $labels[ $context ] ) ) {
			return $labels[ $context ];
		}

		if ( str_starts_with( $context, 'form:' ) ) {
			return $language === 'de' ? 'Benutzerdefiniertes Formular' : 'Custom form';
		}

		return $context;
	}

	private function siteTimezone(): DateTimeZone {
		if ( function_exists( 'wp_timezone' ) ) {
			return wp_timezone();
		}

		$tz = (string) get_option( 'timezone_string', 'UTC' );
		if ( $tz !== '' ) {
			try {
				return new DateTimeZone( $tz );
			} catch ( \Throwable ) {
				return new DateTimeZone( 'UTC' );
			}
		}

		return new DateTimeZone( 'UTC' );
	}

	/**
	 * @return array<string,string>
	 */
	private function mailText( string $language ): array {
		$de = array(
			'weekly_subject'          => 'ProofShield Wochenreport: %d Angriffe geblockt',
			'weekly_subject_disabled' => 'ProofShield Wochenreport: Logging deaktiviert',
			'weekly_intro'            => 'Wöchentlicher ProofShield-Sicherheitsreport (letzte 7 Tage).',
			'weekly_disabled_intro'   => 'Das ProofShield-Logging ist deaktiviert. Es konnten keine Statistikdaten ausgewertet werden.',
			'period'                  => 'Zeitraum',
			'days'                    => 'Tage',
			'summary'                 => 'Kurzüberblick',
			'decisions'               => 'Anfragen gesamt (ok + blocked)',
			'blocked'                 => 'Geblockt',
			'allowed'                 => 'Erlaubt',
			'block_rate'              => 'Blockrate',
			'allowed_rate'            => 'Erlaubt-Rate',
			'top_context'             => 'Top-Kontext',
			'median_solve'            => 'Median Solve-Zeit',
			'top_reasons'             => 'Top Blockgründe',
			'contexts'                => 'Kontexte',
			'assessment'              => 'Einschätzung',
			'next_steps'              => 'Empfohlene nächste Schritte',
			'assessment_no_data'      => 'Keine ausreichenden Daten für eine Belastungsbewertung.',
			'assessment_high_pressure'     => 'Sehr hoher Bot-Druck erkannt. Schutz greift stark.',
			'assessment_elevated_pressure' => 'Erhöhter Bot-Druck erkannt.',
			'assessment_moderate_pressure' => 'Mittelmäßiger Bot-Druck erkannt.',
			'assessment_low_pressure'      => 'Niedriger Bot-Druck erkannt.',
			'step_js_required'        => 'Prüfen Sie, ob legitime Nutzer JavaScript im Browser aktiviert haben (insbesondere Login/Formulare).',
			'step_rate_limit'         => 'Rate-Limit-Einstellungen und Trusted-IPs prüfen, falls legitime Systeme betroffen sind.',
			'step_false_positive_check' => 'Bei sehr hoher Blockrate kurz auf False Positives prüfen (z. B. eigene Monitoring- oder Automation-Tools).',
			'step_open_stats'         => 'Details in der Statistikseite prüfen (Kontexte, Gründe, Zeitverlauf).',
			'stats_link'              => 'Zur Statistik',
			'settings_link'           => 'Zu den Einstellungen',
			'none'                    => 'Keine Daten',
		);
		$en = array(
			'weekly_subject'          => 'ProofShield weekly report: %d attacks blocked',
			'weekly_subject_disabled' => 'ProofShield weekly report: logging disabled',
			'weekly_intro'            => 'Weekly ProofShield security report (last 7 days).',
			'weekly_disabled_intro'   => 'ProofShield logging is disabled, so no statistics could be evaluated.',
			'period'                  => 'Period',
			'days'                    => 'days',
			'summary'                 => 'Quick summary',
			'decisions'               => 'Total requests (ok + blocked)',
			'blocked'                 => 'Blocked',
			'allowed'                 => 'Allowed',
			'block_rate'              => 'Block rate',
			'allowed_rate'            => 'Allow rate',
			'top_context'             => 'Top context',
			'median_solve'            => 'Median solve time',
			'top_reasons'             => 'Top block reasons',
			'contexts'                => 'Contexts',
			'assessment'              => 'Assessment',
			'next_steps'              => 'Recommended next steps',
			'assessment_no_data'      => 'Not enough data to assess attack pressure.',
			'assessment_high_pressure'     => 'Very high bot pressure detected. Protection is actively blocking traffic.',
			'assessment_elevated_pressure' => 'Elevated bot pressure detected.',
			'assessment_moderate_pressure' => 'Moderate bot pressure detected.',
			'assessment_low_pressure'      => 'Low bot pressure detected.',
			'step_js_required'        => 'Check whether legitimate users have JavaScript enabled (especially on login/forms).',
			'step_rate_limit'         => 'Review rate-limit settings and trusted IPs if legitimate systems are affected.',
			'step_false_positive_check' => 'With very high block rates, quickly validate false positives (e.g. your own monitoring/automation tools).',
			'step_open_stats'         => 'Open the stats page for detailed context, reasons, and trends.',
			'stats_link'              => 'Stats page',
			'settings_link'           => 'Settings page',
			'none'                    => 'No data',
		);

		return $language === 'de' ? $de : $en;
	}
}
