<?php

namespace WPProofShield\Admin;

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


use WPProofShield\Security\StatsStore;
use WPProofShield\Support\FeatureAccess\FeatureAccess;
use WPProofShield\Support\RequestInput;
use WPProofShield\Support\Settings;
use WPProofShield\Support\UiLanguage;

final class StatsPage {

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

	public function register(): void {
		add_action( 'admin_menu', array( $this, 'addSubmenu' ) );
	}

	public function addSubmenu(): void {
		add_submenu_page(
			'wpsh-settings',
			__( 'ProofShield Statistik', 'proofshield' ),
			__( 'Statistik', 'proofshield' ),
			'manage_options',
			'wpsh-stats',
			array( $this, 'render' )
		);
	}

	public function render(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		$ui_language = UiLanguage::current( $this->settings );
		$t           = $this->statsTexts( $ui_language );

		$admin_css_path = WPSH_DIR . 'assets/css/admin.css';
		$admin_css_ver  = is_readable( $admin_css_path ) ? (string) filemtime( $admin_css_path ) : WPSH_VERSION;
		wp_enqueue_style( 'wpsh-admin', WPSH_URL . 'assets/css/admin.css', array(), $admin_css_ver );
		wp_enqueue_script( 'wpsh-stats-admin', WPSH_URL . 'assets/js/admin-stats.js', array(), WPSH_VERSION, true );

		$range = RequestInput::getText( 'range' );
		$range = $range !== '' ? $range : '24h';
		if ( ! in_array( $range, array( '24h', '7d', '30d' ), true ) ) {
			$range = '24h';
		}

		$dashboard          = $this->stats->getDashboard( $range );
		$summary            = $dashboard['summary'] ?? array();
		$totals             = $summary['totals'] ?? array(
			'ok'      => 0,
			'blocked' => 0,
		);
		$median_solve       = $summary['median_solve_ms'] ?? null;
		$recent             = $dashboard['recent_24h'] ?? array(
			'ok'      => 0,
			'blocked' => 0,
		);
		$buckets            = $dashboard['buckets'] ?? array();
		$top_context        = (string) ( $dashboard['top_context'] ?? '-' );
		$top_context_meta   = $this->contextMeta( $top_context, $ui_language );
		$blocked_rate       = (int) ( $dashboard['blocked_rate_percent'] ?? 0 );
		$reasons            = $dashboard['reasons'] ?? array();
		$contact_forms      = $dashboard['contact_forms'] ?? array(
			'ok'      => 0,
			'blocked' => 0,
			'total'   => 0,
		);
		$ip_stats           = $dashboard['ip_stats'] ?? array();
		$ip_tracing_enabled = ! empty( $dashboard['ip_tracing_enabled'] );
		$world_map_enabled  = ! empty( $this->settings->get( 'stats_world_map_enabled', true ) );
		$private_ip_events  = (int) ( $dashboard['private_ip_events'] ?? 0 );
		$suspicious_ips     = array();
		foreach ( $ip_stats as $ip => $row ) {
			$blocked     = (int) ( $row['blocked'] ?? 0 );
			$strict_hits = (int) ( $row['strict_hits'] ?? 0 );
			$total_hits  = (int) ( $row['total'] ?? 0 );
			if ( $blocked <= 0 && $strict_hits <= 0 ) {
				continue;
			}
			$suspicious_ips[] = array(
				'ip'          => (string) $ip,
				'ip_display'  => $this->maskedIpLabel( (string) $ip ),
				'blocked'     => $blocked,
				'strict_hits' => $strict_hits,
				'total'       => $total_hits,
				'country'     => (string) ( $row['country'] ?? $t['unknown'] ),
				'lat'         => isset( $row['lat'] ) ? (float) $row['lat'] : null,
				'lng'         => isset( $row['lng'] ) ? (float) $row['lng'] : null,
			);
		}
		$context_rows = array();
		foreach ( ( $summary['by_context'] ?? array() ) as $ctx => $row ) {
			$ok             = (int) ( $row['ok'] ?? 0 );
			$blocked        = (int) ( $row['blocked'] ?? 0 );
			$total          = $ok + $blocked;
			$context_rows[] = array(
				'ctx'          => (string) $ctx,
				'ok'           => $ok,
				'blocked'      => $blocked,
				'total'        => $total,
				'blocked_rate' => $total > 0 ? (int) round( ( $blocked / $total ) * 100 ) : 0,
			);
		}
		usort( $context_rows, static fn ( array $a, array $b ): int => $b['total'] <=> $a['total'] );
		$contact_forms_ok      = (int) ( $contact_forms['ok'] ?? 0 );
		$contact_forms_blocked = (int) ( $contact_forms['blocked'] ?? 0 );
		$contact_forms_total   = (int) ( $contact_forms['total'] ?? 0 );
		$context_rows[]        = array(
			'ctx'          => 'contact_forms_total',
			'ok'           => $contact_forms_ok,
			'blocked'      => $contact_forms_blocked,
			'total'        => $contact_forms_total,
			'blocked_rate' => $contact_forms_total > 0 ? (int) round( ( $contact_forms_blocked / $contact_forms_total ) * 100 ) : 0,
		);
		$max_bucket_value      = 1;
		foreach ( $buckets as $bucket ) {
			$ok_val           = (int) ( $bucket['ok'] ?? 0 );
			$blocked_val      = (int) ( $bucket['blocked'] ?? 0 );
			$max_bucket_value = max( $max_bucket_value, $ok_val, $blocked_val );
		}

		$recent_total         = (int) ( $recent['ok'] ?? 0 ) + (int) ( $recent['blocked'] ?? 0 );
		$range_links          = array(
			'24h' => $t['range_24h'],
			'7d'  => $t['range_7d'],
			'30d' => $t['range_30d'],
		);
		$selected_range_label = (string) ( $range_links[ $range ] ?? $t['range_24h'] );

		wp_localize_script(
			'wpsh-stats-admin',
			'WPSH_STATS_CONFIG',
			array(
				'ipStats'         => $ip_stats,
				'blockedIps'      => $suspicious_ips,
				'tracingEnabled'  => $ip_tracing_enabled,
				'worldMapEnabled' => $world_map_enabled,
				'worldMapDataUrl' => $this->worldMapDataUrl(),
				'worldMapCentroidsUrl' => $this->worldMapCentroidsUrl(),
				'privateIpEvents' => $private_ip_events,
				'mapTitle'        => $t['world_map_title'],
				'language'        => $ui_language,
				'messages'        => UiLanguage::statsMessages( $ui_language ),
			)
		);

		?>
		<div class="wrap wpsh-stats">
			<div class="wpsh-admin-brand">
				<?php if ( $logo_url = $this->adminLogoUrl() ) : ?>
					<img class="wpsh-admin-brand__logo" src="<?php echo esc_url( $logo_url ); ?>" alt="<?php echo esc_attr( $t['title'] ); ?>" />
				<?php endif; ?>
				<h1><?php echo esc_html( $t['title'] ); ?></h1>
			</div>
			<nav class="wpsh-range-switch">
				<?php foreach ( $range_links as $key => $label ) : ?>
					<?php
					$url = add_query_arg(
						array(
							'page'  => 'wpsh-stats',
							'range' => $key,
						),
						admin_url( 'admin.php' )
					);
					?>
					<a class="wpsh-range-switch__link <?php echo $range === $key ? 'is-active' : ''; ?>" href="<?php echo esc_url( $url ); ?>">
						<?php echo esc_html( $label ); ?>
					</a>
				<?php endforeach; ?>
			</nav>

			<div class="wpsh-kpis">
				<article class="wpsh-kpi wpsh-kpi--ok">
					<h2><?php echo esc_html( $t['allowed'] ); ?></h2>
					<p class="wpsh-kpi__value"><?php echo esc_html( (string) ( (int) ( $totals['ok'] ?? 0 ) ) ); ?></p>
				</article>
				<article class="wpsh-kpi wpsh-kpi--blocked">
					<h2><?php echo esc_html( $t['blocked'] ); ?></h2>
					<p class="wpsh-kpi__value"><?php echo esc_html( (string) ( (int) ( $totals['blocked'] ?? 0 ) ) ); ?></p>
					<p class="wpsh-kpi__meta"><?php echo esc_html( (string) $blocked_rate ); ?>%</p>
				</article>
				<article class="wpsh-kpi">
					<h2><?php echo esc_html( $t['median_solve'] ); ?></h2>
					<p class="wpsh-kpi__value"><?php echo esc_html( (string) ( $median_solve !== null ? (int) $median_solve : 0 ) ); ?> ms</p>
				</article>
				<article class="wpsh-kpi">
					<h2><?php echo esc_html( $t['top_context'] ); ?></h2>
					<p class="wpsh-kpi__value"><?php echo esc_html( (string) ( $top_context_meta['label'] ?? $top_context ) ); ?></p>
					<p class="wpsh-kpi__meta"><?php echo esc_html( (string) $recent_total ); ?> <?php echo esc_html( $selected_range_label ); ?></p>
				</article>
			</div>

			<section class="wpsh-chart-card">
				<h2><?php echo esc_html( $selected_range_label ); ?></h2>
				<p class="description"><?php echo esc_html( $t['chart_hint'] ); ?></p>
				<?php if ( $recent_total === 0 ) : ?>
					<p class="description"><?php echo esc_html( $t['no_events_24h'] ); ?></p>
				<?php endif; ?>
				<div class="wpsh-trend" style="grid-template-columns: repeat(<?php echo esc_attr( (string) max( 1, count( $buckets ) ) ); ?>, minmax(0, 1fr));" aria-label="<?php echo esc_attr( $t['events_by_period'] ); ?>">
					<?php foreach ( $buckets as $bucket ) : ?>
						<?php
						$ok         = (int) ( $bucket['ok'] ?? 0 );
						$blocked    = (int) ( $bucket['blocked'] ?? 0 );
						$ok_px      = $ok > 0 ? max( 3, (int) round( ( $ok / $max_bucket_value ) * 130 ) ) : 2;
						$blocked_px = $blocked > 0 ? max( 3, (int) round( ( $blocked / $max_bucket_value ) * 130 ) ) : 2;
						?>
						<div class="wpsh-trend__col" title="<?php echo esc_attr( $bucket['label'] . ' | ok: ' . $ok . ' | blocked: ' . $blocked ); ?>">
							<span class="wpsh-trend__bars">
								<span class="wpsh-trend__bar wpsh-trend__bar--ok" style="height: <?php echo esc_attr( (string) $ok_px ); ?>px;"></span>
								<span class="wpsh-trend__bar wpsh-trend__bar--blocked" style="height: <?php echo esc_attr( (string) $blocked_px ); ?>px;"></span>
							</span>
							<span class="wpsh-trend__counts"><?php echo esc_html( $t['not_blocked_short'] ); ?> <?php echo esc_html( (string) $ok ); ?> | <?php echo esc_html( $t['blocked_short'] ); ?> <?php echo esc_html( (string) $blocked ); ?></span>
							<span class="wpsh-trend__label"><?php echo esc_html( (string) ( $bucket['label'] ?? '' ) ); ?></span>
						</div>
					<?php endforeach; ?>
				</div>
			</section>

			<section class="wpsh-chart-card">
				<h2><?php echo esc_html( $t['top_block_reasons'] ); ?></h2>
				<?php if ( ! $reasons ) : ?>
					<p class="description"><?php echo esc_html( $t['no_block_reasons'] ); ?></p>
				<?php else : ?>
					<table class="widefat striped">
						<thead>
							<tr>
								<th><?php echo esc_html( $t['reason'] ); ?></th>
								<th><?php echo esc_html( $t['count'] ); ?></th>
							</tr>
						</thead>
						<tbody>
							<?php foreach ( $reasons as $reason => $count ) : ?>
								<?php
								$meta        = $this->reasonMeta( (string) $reason, $ui_language );
								$label       = (string) ( $meta['label'] ?? (string) $reason );
								$description = (string) ( $meta['description'] ?? '' );
								?>
								<tr>
									<td>
										<span class="wpsh-help-label">
											<?php echo esc_html( $label ); ?>
											<?php if ( $description !== '' ) : ?>
												<span class="wpsh-help-tip" tabindex="0" title="<?php echo esc_attr( $description ); ?>">?</span>
											<?php endif; ?>
										</span>
									</td>
									<td><?php echo esc_html( (string) (int) $count ); ?></td>
								</tr>
							<?php endforeach; ?>
						</tbody>
					</table>
				<?php endif; ?>
			</section>

			<section class="wpsh-chart-card">
				<h2><?php echo esc_html( $t['world_map_title'] ); ?></h2>
				<?php if ( ! $ip_tracing_enabled ) : ?>
					<p class="description"><?php echo esc_html( $t['no_ip_data'] ); ?></p>
				<?php endif; ?>
				<?php if ( $world_map_enabled ) : ?>
					<div id="wpsh-world-map" class="wpsh-world-map"></div>
				<?php else : ?>
					<p class="description"><?php echo esc_html( $t['world_map_disabled'] ); ?></p>
				<?php endif; ?>
				<h3><?php echo esc_html( $t['suspicious_visitors'] ?? $t['blocked_visitors'] ); ?></h3>
				<table class="widefat striped wpsh-ip-table">
					<thead>
						<tr>
							<th><?php echo esc_html( $t['ip'] ); ?></th>
							<th><?php echo esc_html( $t['country'] ); ?></th>
							<th><?php echo esc_html( $t['status'] ); ?></th>
							<th><?php echo esc_html( $t['frequency'] ?? $t['count'] ); ?></th>
						</tr>
					</thead>
					<tbody id="wpsh-blocked-ip-rows">
						<tr>
							<td colspan="4"><?php echo esc_html( $t['loading_geo'] ); ?></td>
						</tr>
					</tbody>
				</table>
				<div id="wpsh-blocked-ip-pagination" class="wpsh-pagination"></div>
			</section>

			<h2><?php echo esc_html( $t['by_context'] ); ?></h2>
			<table class="widefat striped wpsh-context-table">
				<thead>
					<tr>
						<th><?php echo esc_html( $t['context'] ); ?></th>
						<th><?php echo esc_html( $t['blocked'] ); ?></th>
						<th><?php echo esc_html( $t['allowed'] ); ?></th>
						<th><?php echo esc_html( $t['total'] ); ?></th>
						<th><?php echo esc_html( $t['block_rate'] ); ?></th>
					</tr>
				</thead>
				<tbody>
				<?php if ( ! $context_rows ) : ?>
					<tr>
						<td colspan="5"><?php echo esc_html( $t['no_context_events'] ); ?></td>
					</tr>
				<?php else : ?>
					<?php foreach ( $context_rows as $row ) : ?>
						<?php
						$ctx_meta        = $this->contextMeta( (string) $row['ctx'], $ui_language );
						$ctx_label       = (string) ( $ctx_meta['label'] ?? (string) $row['ctx'] );
						$ctx_description = (string) ( $ctx_meta['description'] ?? '' );
						?>
						<tr>
							<td>
								<span class="wpsh-help-label">
									<strong><?php echo esc_html( $ctx_label ); ?></strong>
									<?php if ( $ctx_description !== '' ) : ?>
										<span class="wpsh-help-tip" tabindex="0" title="<?php echo esc_attr( $ctx_description ); ?>">?</span>
									<?php endif; ?>
								</span>
							</td>
							<td><span class="wpsh-pill wpsh-pill--blocked"><?php echo esc_html( (string) $row['blocked'] ); ?></span></td>
							<td><span class="wpsh-pill wpsh-pill--ok"><?php echo esc_html( (string) $row['ok'] ); ?></span></td>
							<td><?php echo esc_html( (string) $row['total'] ); ?></td>
							<td><?php echo esc_html( (string) $row['blocked_rate'] ); ?>%</td>
						</tr>
					<?php endforeach; ?>
				<?php endif; ?>
				</tbody>
				<tfoot>
					<tr>
						<th><?php echo esc_html( $t['all_contexts'] ); ?></th>
						<th><?php echo esc_html( (string) ( (int) ( $totals['blocked'] ?? 0 ) ) ); ?></th>
						<th><?php echo esc_html( (string) ( (int) ( $totals['ok'] ?? 0 ) ) ); ?></th>
						<th><?php echo esc_html( (string) ( ( (int) ( $totals['ok'] ?? 0 ) ) + ( (int) ( $totals['blocked'] ?? 0 ) ) ) ); ?></th>
						<th><?php echo esc_html( (string) $blocked_rate ); ?>%</th>
					</tr>
				</tfoot>
			</table>
		</div>
		<?php
	}

	private function statsTexts( string $language ): array {

		return match ( $language ) {
			'en' => array(
				'title'                => 'ProofShield Statistics',
				'range_24h'            => 'Today',
				'range_7d'             => '7 days',
				'range_30d'            => '30 days',
				'contact_form_submits' => 'Contact form submits',
				'allowed'              => 'Not blocked',
				'blocked'              => 'Blocked',
				'median_solve'         => 'Median solve',
				'top_context'          => 'Top context',
				'events_last_24h'      => 'events in selected range',
				'chart_hint'           => 'Green = not blocked, red = blocked',
				'no_events_24h'        => 'No events in the selected range yet.',
				'events_by_period'     => 'Events by period',
				'not_blocked_short'    => 'N',
				'blocked_short'        => 'B',
				'top_block_reasons'    => 'Top block reasons',
				'no_block_reasons'     => 'No block reasons in selected range.',
				'reason'               => 'Reason',
				'count'                => 'Count',
				'world_map_title'      => 'World map by IP origin',
				'no_ip_data'           => 'No IP data available. Enable IP tracking in settings.',
				'world_map_disabled'   => 'World map display is disabled in settings. The IP table remains available.',
				'blocked_visitors'     => 'Blocked visitors',
				'suspicious_visitors'  => 'Suspicious visitors',
				'ip'                   => 'IP',
				'country'              => 'Country',
				'status'               => 'Status',
				'loading_geo'          => 'Loading geo data...',
				'by_context'           => 'By context',
				'context'              => 'Context',
				'total'                => 'Total',
				'frequency'            => 'Frequency',
				'block_rate'           => 'Block rate',
				'no_context_events'    => 'No context events yet.',
				'all_contexts'         => 'All contexts',
				'unknown'              => 'Unknown',
			),
			'es' => array(
				'title'              => 'Estadisticas de ProofShield',
				'range_24h'          => 'Hoy',
				'range_7d'           => '7 dias',
				'range_30d'          => '30 dias',
				'allowed'            => 'No bloqueado',
				'blocked'            => 'Bloqueado',
				'median_solve'       => 'Resolucion mediana',
				'top_context'        => 'Contexto principal',
				'events_last_24h'    => 'eventos en el rango seleccionado',
				'chart_hint'         => 'Verde = no bloqueado, rojo = bloqueado',
				'no_events_24h'      => 'Aun no hay eventos en el rango seleccionado.',
				'events_by_period'   => 'Eventos por periodo',
				'not_blocked_short'  => 'N',
				'blocked_short'      => 'B',
				'top_block_reasons'  => 'Motivos principales de bloqueo',
				'no_block_reasons'   => 'No hay motivos de bloqueo en el rango seleccionado.',
				'reason'             => 'Motivo',
				'count'              => 'Cantidad',
				'world_map_title'    => 'Mapa mundial por origen IP',
				'no_ip_data'         => 'No hay datos IP. Activa el seguimiento IP en ajustes.',
				'world_map_disabled' => 'La vista del mapa mundial esta desactivada en ajustes. La tabla IP sigue disponible.',
				'blocked_visitors'   => 'Visitantes bloqueados',
				'ip'                 => 'IP',
				'country'            => 'Pais',
				'status'             => 'Estado',
				'loading_geo'        => 'Cargando datos geo...',
				'by_context'         => 'Por contexto',
				'context'            => 'Contexto',
				'total'              => 'Total',
				'block_rate'         => 'Tasa de bloqueo',
				'no_context_events'  => 'Todavia no hay eventos de contexto.',
				'all_contexts'       => 'Todos los contextos',
				'unknown'            => 'Desconocido',
			),
			'ru' => array(
				'title'              => 'Statistika ProofShield',
				'range_24h'          => 'Segodnya',
				'range_7d'           => '7 dney',
				'range_30d'          => '30 dney',
				'allowed'            => 'Ne zablokirovano',
				'blocked'            => 'Zablokirovano',
				'median_solve'       => 'Median solve',
				'top_context'        => 'Top kontekst',
				'events_last_24h'    => 'sobytiy v vybrannom periode',
				'chart_hint'         => 'Zelenyy = ne zablokirovano, krasnyy = zablokirovano',
				'no_events_24h'      => 'V vybrannom periode sobytiy poka net.',
				'events_by_period'   => 'Sobytiya po periodam',
				'not_blocked_short'  => 'N',
				'blocked_short'      => 'B',
				'top_block_reasons'  => 'Top prichin blokirovki',
				'no_block_reasons'   => 'Net prichin blokirovki v vybrannom periode.',
				'reason'             => 'Prichina',
				'count'              => 'Kolichestvo',
				'world_map_title'    => 'Karta mira po proishozhdeniyu IP',
				'no_ip_data'         => 'Net dannyh IP. Vklyuchite otslezhivanie IP v nastroikah.',
				'world_map_disabled' => 'Otobrazhenie karty mira otklyucheno v nastroikah. Tablitsa IP ostayetsya dostupnoy.',
				'blocked_visitors'   => 'Zablokirovannye posetiteli',
				'ip'                 => 'IP',
				'country'            => 'Strana',
				'status'             => 'Status',
				'loading_geo'        => 'Zagruzka geo dannyh...',
				'by_context'         => 'Po kontekstu',
				'context'            => 'Kontekst',
				'total'              => 'Vsego',
				'block_rate'         => 'Protsent blokirovki',
				'no_context_events'  => 'Sobytiy po kontekstu poka net.',
				'all_contexts'       => 'Vse konteksty',
				'unknown'            => 'Neizvestno',
			),
			'fr' => array(
				'title'              => 'Statistiques ProofShield',
				'range_24h'          => 'Aujourd hui',
				'range_7d'           => '7 jours',
				'range_30d'          => '30 jours',
				'allowed'            => 'Non bloque',
				'blocked'            => 'Bloque',
				'median_solve'       => 'Resolution mediane',
				'top_context'        => 'Contexte principal',
				'events_last_24h'    => 'evenements sur la periode selectionnee',
				'chart_hint'         => 'Vert = non bloque, rouge = bloque',
				'no_events_24h'      => 'Aucun evenement sur la periode selectionnee.',
				'events_by_period'   => 'Evenements par periode',
				'not_blocked_short'  => 'N',
				'blocked_short'      => 'B',
				'top_block_reasons'  => 'Principales raisons de blocage',
				'no_block_reasons'   => 'Aucune raison de blocage sur la periode selectionnee.',
				'reason'             => 'Raison',
				'count'              => 'Nombre',
				'world_map_title'    => 'Carte du monde par origine IP',
				'no_ip_data'         => 'Aucune donnee IP disponible. Activez le suivi IP dans les reglages.',
				'world_map_disabled' => 'La carte du monde est desactivee dans les reglages. Le tableau IP reste disponible.',
				'blocked_visitors'   => 'Visiteurs bloques',
				'ip'                 => 'IP',
				'country'            => 'Pays',
				'status'             => 'Statut',
				'loading_geo'        => 'Chargement des donnees geo...',
				'by_context'         => 'Par contexte',
				'context'            => 'Contexte',
				'total'              => 'Total',
				'block_rate'         => 'Taux de blocage',
				'no_context_events'  => 'Aucun evenement de contexte pour le moment.',
				'all_contexts'       => 'Tous les contextes',
				'unknown'            => 'Inconnu',
			),
			'ja' => array(
				'title'              => 'ProofShield Toukei',
				'range_24h'          => 'Kyou',
				'range_7d'           => '7 nichi',
				'range_30d'          => '30 nichi',
				'allowed'            => 'Burokku nashi',
				'blocked'            => 'Burokku',
				'median_solve'       => 'Median solve',
				'top_context'        => 'Top kontekisuto',
				'events_last_24h'    => 'sentaku kikan no ibento',
				'chart_hint'         => 'Midori = burokku nashi, aka = burokku',
				'no_events_24h'      => 'Sentaku kikan ni ibento ga arimasen.',
				'events_by_period'   => 'Kikan betsu ibento',
				'not_blocked_short'  => 'N',
				'blocked_short'      => 'B',
				'top_block_reasons'  => 'Top burokku riyuu',
				'no_block_reasons'   => 'Sentaku kikan ni burokku riyuu ga arimasen.',
				'reason'             => 'Riyuu',
				'count'              => 'Kensuu',
				'world_map_title'    => 'IP shusshin no sekai chizu',
				'no_ip_data'         => 'IP de-ta ga arimasen. settei de IP torakkingu o yuukou ni shite kudasai.',
				'world_map_disabled' => 'Sekai chizu hyouji wa settei de off desu. IP table wa tsuzuke te tsukaemasu.',
				'blocked_visitors'   => 'Burokku sareta houmonsha',
				'ip'                 => 'IP',
				'country'            => 'Kuni',
				'status'             => 'Sutetasu',
				'loading_geo'        => 'Geo de-ta no yomikomi chuu...',
				'by_context'         => 'Kontekisuto betsu',
				'context'            => 'Kontekisuto',
				'total'              => 'Goukei',
				'block_rate'         => 'Burokku ritsu',
				'no_context_events'  => 'Kontekisuto ibento wa mada arimasen.',
				'all_contexts'       => 'Subete no kontekisuto',
				'unknown'            => 'Fumei',
			),
			'pt' => array(
				'title'              => 'Estatisticas ProofShield',
				'range_24h'          => 'Hoje',
				'range_7d'           => '7 dias',
				'range_30d'          => '30 dias',
				'allowed'            => 'Nao bloqueado',
				'blocked'            => 'Bloqueado',
				'median_solve'       => 'Resolucao mediana',
				'top_context'        => 'Contexto principal',
				'events_last_24h'    => 'eventos no periodo selecionado',
				'chart_hint'         => 'Verde = nao bloqueado, vermelho = bloqueado',
				'no_events_24h'      => 'Ainda nao ha eventos no periodo selecionado.',
				'events_by_period'   => 'Eventos por periodo',
				'not_blocked_short'  => 'N',
				'blocked_short'      => 'B',
				'top_block_reasons'  => 'Principais motivos de bloqueio',
				'no_block_reasons'   => 'Nao ha motivos de bloqueio no periodo selecionado.',
				'reason'             => 'Motivo',
				'count'              => 'Quantidade',
				'world_map_title'    => 'Mapa mundial por origem de IP',
				'no_ip_data'         => 'Nenhum dado de IP disponivel. Ative o rastreamento de IP nas configuracoes.',
				'world_map_disabled' => 'A visualizacao do mapa mundial esta desativada nas configuracoes. A tabela de IP continua disponivel.',
				'blocked_visitors'   => 'Visitantes bloqueados',
				'ip'                 => 'IP',
				'country'            => 'Pais',
				'status'             => 'Status',
				'loading_geo'        => 'Carregando dados de geo...',
				'by_context'         => 'Por contexto',
				'context'            => 'Contexto',
				'total'              => 'Total',
				'block_rate'         => 'Taxa de bloqueio',
				'no_context_events'  => 'Ainda nao ha eventos de contexto.',
				'all_contexts'       => 'Todos os contextos',
				'unknown'            => 'Desconhecido',
			),
			'zh' => array(
				'title'              => 'ProofShield Tongji',
				'range_24h'          => 'Jintian',
				'range_7d'           => '7 tian',
				'range_30d'          => '30 tian',
				'allowed'            => 'Wei zulan',
				'blocked'            => 'Yi zulan',
				'median_solve'       => 'Median solve',
				'top_context'        => 'Top changjing',
				'events_last_24h'    => 'suo xuan shijian nei shijian',
				'chart_hint'         => 'Lvse = wei zulan, hongse = yi zulan',
				'no_events_24h'      => 'Suo xuan shijian nei hai meiyou shijian.',
				'events_by_period'   => 'An shijian qujian tongji',
				'not_blocked_short'  => 'N',
				'blocked_short'      => 'B',
				'top_block_reasons'  => 'Zhuyao zulan yuanyin',
				'no_block_reasons'   => 'Suo xuan qujian nei meiyou zulan yuanyin.',
				'reason'             => 'Yuanyin',
				'count'              => 'Shuliang',
				'world_map_title'    => 'An IP laiyuan de shijie ditu',
				'no_ip_data'         => 'Meiyou IP shuju. Qing zai shezhi zhong qiyong IP genzong.',
				'world_map_disabled' => 'Shijie ditu xianshi yi zai shezhi zhong guanbi. IP biaoge reng keyi shiyong.',
				'blocked_visitors'   => 'Bei zulan de fangke',
				'ip'                 => 'IP',
				'country'            => 'Guojia',
				'status'             => 'Zhuangtai',
				'loading_geo'        => 'Zhengzai jiazai geo shuju...',
				'by_context'         => 'An changjing',
				'context'            => 'Changjing',
				'total'              => 'Zongji',
				'block_rate'         => 'Zulan lv',
				'no_context_events'  => 'Huanmeiyou changjing shijian.',
				'all_contexts'       => 'Suoyou changjing',
				'unknown'            => 'Weizhi',
			), 'it', 'nl', 'pl', 'tr' => array(
				'title'                => 'ProofShield Statistics',
				'range_24h'            => 'Today',
				'range_7d'             => '7 days',
				'range_30d'            => '30 days',
				'contact_form_submits' => 'Contact form submits',
				'allowed'              => 'Not blocked',
				'blocked'              => 'Blocked',
				'median_solve'         => 'Median solve',
				'top_context'          => 'Top context',
				'events_last_24h'      => 'events in selected range',
				'chart_hint'           => 'Green = not blocked, red = blocked',
				'no_events_24h'        => 'No events in the selected range yet.',
				'events_by_period'     => 'Events by period',
				'not_blocked_short'    => 'N',
				'blocked_short'        => 'B',
				'top_block_reasons'    => 'Top block reasons',
				'no_block_reasons'     => 'No block reasons in selected range.',
				'reason'               => 'Reason',
				'count'                => 'Count',
				'world_map_title'      => 'World map by IP origin',
				'no_ip_data'           => 'No IP data available. Enable IP tracking in settings.',
				'world_map_disabled'   => 'World map display is disabled in settings. The IP table remains available.',
				'blocked_visitors'     => 'Blocked visitors',
				'suspicious_visitors'  => 'Suspicious visitors',
				'ip'                   => 'IP',
				'country'              => 'Country',
				'status'               => 'Status',
				'loading_geo'          => 'Loading geo data...',
				'by_context'           => 'By context',
				'context'              => 'Context',
				'total'                => 'Total',
				'frequency'            => 'Frequency',
				'block_rate'           => 'Block rate',
				'no_context_events'    => 'No context events yet.',
				'all_contexts'         => 'All contexts',
				'unknown'              => 'Unknown',
			), default => array(
				'title'                => 'ProofShield Statistik',
				'range_24h'            => 'Heute',
				'range_7d'             => '7 Tage',
				'range_30d'            => '30 Tage',
				'contact_form_submits' => 'Kontaktformular-Submits',
				'allowed'              => 'Nicht geblockt',
				'blocked'              => 'Geblockt',
				'median_solve'         => 'Median Solve',
				'top_context'          => 'Top-Kontext',
				'events_last_24h'      => 'Events im gewählten Zeitraum',
				'chart_hint'           => 'Grün = nicht geblockt, Rot = geblockt',
				'no_events_24h'        => 'Noch keine Events im gewählten Zeitraum.',
				'events_by_period'     => 'Events pro Zeitraum',
				'not_blocked_short'    => 'N',
				'blocked_short'        => 'B',
				'top_block_reasons'    => 'Top-Blockgründe',
				'no_block_reasons'     => 'Keine Blockgründe im gewählten Zeitraum.',
				'reason'               => 'Grund',
				'count'                => 'Anzahl',
				'world_map_title'      => 'Weltkarte nach IP-Herkunft',
				'no_ip_data'           => 'Keine IP-Daten vorhanden. Aktiviere IP-Tracking in den Einstellungen.',
				'world_map_disabled'   => 'Die Weltkartenansicht ist in den Einstellungen deaktiviert. Die IP-Tabelle bleibt verfügbar.',
				'blocked_visitors'     => 'Geblockte Besucher',
				'suspicious_visitors'  => 'Verdächtige Besucher',
				'ip'                   => 'IP',
				'country'              => 'Land',
				'status'               => 'Status',
				'loading_geo'          => 'Lade Geodaten...',
				'by_context'           => 'Nach Kontext',
				'context'              => 'Kontext',
				'total'                => 'Gesamt',
				'frequency'            => 'Häufigkeit',
				'block_rate'           => 'Blockrate',
				'no_context_events'    => 'Noch keine Kontextevents vorhanden.',
				'all_contexts'         => 'Alle Kontexte',
				'unknown'              => 'Unbekannt',
			),
		};
	}

	private function adminLogoUrl(): string {
		$preferred = $this->license_gate->isEnabled()
			? array( 'assets/images/proofshield-pro.png', 'assets/images/logo-pro.png', 'assets/images/pro-logo.png' )
			: array( 'assets/images/proofshield-free.png', 'assets/images/logo-free.png', 'assets/images/free-logo.png' );

		$fallback = array( 'assets/images/proofshield-logo.png', 'assets/images/logo.png' );

		foreach ( array_merge( $preferred, $fallback ) as $relative ) {
			$absolute_path = WPSH_DIR . $relative;
			if ( is_readable( $absolute_path ) ) {
				$version = (string) filemtime( $absolute_path );
				return (string) add_query_arg( 'ver', $version, WPSH_URL . $relative );
			}
		}

		return '';
	}

	private function worldMapDataUrl(): string {
		$relative_path = 'assets/data/countries-110m.json';
		$absolute_path = WPSH_DIR . $relative_path;
		if ( ! is_readable( $absolute_path ) ) {
			return '';
		}

		$version = (string) filemtime( $absolute_path );
		return (string) add_query_arg( 'ver', $version, WPSH_URL . $relative_path );
	}

	private function worldMapCentroidsUrl(): string {
		$relative_path = 'assets/data/country-centroids.csv';
		$absolute_path = WPSH_DIR . $relative_path;
		if ( ! is_readable( $absolute_path ) ) {
			return '';
		}

		$version = (string) filemtime( $absolute_path );
		return (string) add_query_arg( 'ver', $version, WPSH_URL . $relative_path );
	}

	private function maskedIpLabel( string $ip ): string {
		if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) {
			$parts = explode( '.', $ip );
			if ( count( $parts ) === 4 ) {
				$parts[3] = 'x';
				return implode( '.', $parts );
			}
		}

		if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) && function_exists( 'inet_pton' ) ) {
			$packed = inet_pton( $ip );
			if ( $packed !== false ) {
				$hex    = unpack( 'n8', $packed );
				$groups = array_map(
					static fn ( $group ): string => strtolower( dechex( (int) $group ) ),
					is_array( $hex ) ? $hex : array()
				);
				if ( count( $groups ) === 8 ) {
					return $groups[0] . ':' . $groups[1] . ':' . $groups[2] . ':x:x:x:x:x';
				}
			}
		}

		return $ip;
	}

	/**
	 * @return array{label:string,description:string}
	 */
	private function reasonMeta( string $reason, string $language ): array {

		$default_map = array(
			'wpsh_replay'           => array(
				'label'       => 'Token mehrfach verwendet',
				'description' => 'Das gleiche ProofShield-Token wurde erneut abgesendet. Tokens sind einmalig gültig, um Replay-Angriffe zu verhindern.',
			),
			'wpsh_pow_missing'      => array(
				'label'       => 'Sicherheitsnachweis fehlt',
				'description' => 'Die Anfrage enthielt keinen ProofShield-Nachweis (Token/Counter/Hash). Typisch bei Bots, deaktiviertem JS oder fehlerhafter Integration.',
			),
			'wpsh_pow_missing_soft' => array(
				'label'       => 'Sicherheitsnachweis fehlt (Soft-Block)',
				'description' => 'Ein erforderlicher Nachweis fehlte in einem Modus mit abgestufter Reaktion. Die Anfrage wurde als verdächtig markiert und entsprechend eingeschränkt.',
			),
			'wpsh_pow_invalid'      => array(
				'label'       => 'Ungültiger Sicherheitsnachweis',
				'description' => 'Der Sicherheitsnachweis war vorhanden, konnte aber nicht validiert werden (Signatur, Hash, Kontext oder Fingerprint ungültig).',
			),
			'wpsh_expired'          => array(
				'label'       => 'Challenge abgelaufen',
				'description' => 'Das Challenge-Token wurde zu spät abgesendet und war beim Prüfen bereits abgelaufen.',
			),
			'wpsh_rate_limited'     => array(
				'label'       => 'Zu viele Anfragen',
				'description' => 'Die Quelle hat in kurzer Zeit zu viele fehlgeschlagene oder auffällige Requests verursacht und wurde temporär gedrosselt.',
			),
			'wpsh_honeypot'         => array(
				'label'       => 'Honeypot ausgelöst',
				'description' => 'Ein verstecktes Formularfeld wurde befüllt. Das ist ein starkes Bot-Signal und führt zur Blockierung.',
			),
			'wpsh_secure_block'     => array(
				'label'       => 'Hohes Risiko erkannt',
				'description' => 'Secure+ hat die Anfrage anhand der Signale als hohes Risiko eingestuft und direkt blockiert.',
			),
			'wpsh_hard_challenge'   => array(
				'label'       => 'Erhöhte Prüfung angefordert',
				'description' => 'Die Anfrage wurde nicht final blockiert, aber auf eine strengere Challenge-Stufe eskaliert.',
			),
			'wpsh_bad_request'      => array(
				'label'       => 'Ungültige Anfrage',
				'description' => 'Pflichtparameter fehlten oder waren ungültig. Die Anfrage konnte nicht verarbeitet werden.',
			),
			'wpsh_js_required'      => array(
				'label'       => 'JavaScript erforderlich',
				'description' => 'Ein geschütztes Formular wurde ohne JavaScript abgesendet. Ohne JavaScript kann kein gültiger ProofShield-Nachweis erstellt werden.',
			),
		);
		$en_map      = array(
			'wpsh_replay'           => array(
				'label'       => 'Replay token reuse',
				'description' => 'The same ProofShield token was submitted more than once. Tokens are single-use to prevent replay attacks.',
			),
			'wpsh_pow_missing'      => array(
				'label'       => 'Security proof missing',
				'description' => 'The request did not include ProofShield proof fields (token/counter/hash). Common for bots, disabled JS, or broken integrations.',
			),
			'wpsh_pow_missing_soft' => array(
				'label'       => 'Security proof missing (soft block)',
				'description' => 'A required proof field was missing in a softer enforcement mode. The request was flagged as suspicious and restricted accordingly.',
			),
			'wpsh_pow_invalid'      => array(
				'label'       => 'Invalid security proof',
				'description' => 'A proof was present but validation failed (signature, hash, context, or fingerprint mismatch).',
			),
			'wpsh_expired'          => array(
				'label'       => 'Challenge expired',
				'description' => 'The challenge token was submitted after its validity window had expired.',
			),
			'wpsh_rate_limited'     => array(
				'label'       => 'Too many requests',
				'description' => 'This source triggered too many failed or suspicious requests and was temporarily rate-limited.',
			),
			'wpsh_honeypot'         => array(
				'label'       => 'Honeypot triggered',
				'description' => 'A hidden form field was filled. This is a strong bot indicator and causes blocking.',
			),
			'wpsh_secure_block'     => array(
				'label'       => 'High risk detected',
				'description' => 'Secure+ classified the request as high-risk and blocked it immediately.',
			),
			'wpsh_hard_challenge'   => array(
				'label'       => 'Hard challenge required',
				'description' => 'The request was escalated to a stricter challenge level instead of being fully accepted.',
			),
			'wpsh_bad_request'      => array(
				'label'       => 'Invalid request',
				'description' => 'Required parameters were missing or invalid, so the request could not be processed.',
			),
			'wpsh_js_required'      => array(
				'label'       => 'JavaScript required',
				'description' => 'A protected form was submitted without JavaScript. Without JavaScript, no valid ProofShield proof can be generated.',
			),
		);
		$map         = in_array( $language, array( 'de' ), true ) ? $default_map : $en_map;
		if ( isset( $map[ $reason ] ) ) {
			return $map[ $reason ];
		}

		if ( str_starts_with( $reason, 'wpsh_' ) ) {
			return in_array( $language, array( 'de' ), true ) ? array(
				'label'       => 'Unbekannter Schutzgrund',
				'description' => 'Es wurde ein interner Schutzgrund protokolliert, der noch keinen eigenen Anzeigetext hat.',
			) : array(
				'label'       => 'Unknown protection reason',
				'description' => 'A protection event was recorded with an internal reason code not yet mapped to a display text.',
			);
		}

		return array(
			'label'       => $reason,
			'description' => '',
		);
	}

	/**
	 * @return array{label:string,description:string}
	 */
	private function contextMeta( string $context, string $language ): array {

		$default_map = array(
			'login'               => array(
				'label'       => 'WordPress Login',
				'description' => 'Anfragen am WordPress-Login (wp-login.php und verwandte Login-Flows).',
			),
			'login:no_js'         => array(
				'label'       => 'WordPress Login: Kein JavaScript',
				'description' => 'Login-Anfragen ohne JavaScript. Diese Anfragen werden geblockt, weil kein ProofShield-Nachweis erzeugt werden kann.',
			),
			'comment'             => array(
				'label'       => 'WordPress Kommentare',
				'description' => 'Formular- und API-Anfragen rund um WordPress-Kommentare.',
			),
			'woo_login'           => array(
				'label'       => 'WooCommerce Login',
				'description' => 'Kunden-Login im WooCommerce Bereich, z. B. "Mein Konto".',
			),
			'replay_test'         => array(
				'label'       => 'Systemtest: Replay-Schutz',
				'description' => 'Interner Testkontext, der absichtlich doppelte Token-Nutzung simuliert, um Replay-Blockierung zu prüfen.',
			),
			'contact_forms_total' => array(
				'label'       => 'Kontaktformular-Submits',
				'description' => 'Gesamtauswertung über alle geschützten Formular-Kontexte (form:*), aufgeteilt in geblockt und nicht geblockt.',
			),
			'form:elementor'      => array(
				'label'       => 'Kontaktformular: Elementor',
				'description' => 'Submits aus Elementor Forms.',
			),
			'form:cf7'            => array(
				'label'       => 'Kontaktformular: Contact Form 7',
				'description' => 'Submits aus Contact Form 7 (CF7).',
			),
			'form:wpforms'        => array(
				'label'       => 'Kontaktformular: WPForms',
				'description' => 'Submits aus WPForms.',
			),
			'form:enfold'         => array(
				'label'       => 'Kontaktformular: Enfold',
				'description' => 'Submits aus dem Enfold-Formularsystem.',
			),
			'form:gravityforms'   => array(
				'label'       => 'Kontaktformular: Gravity Forms',
				'description' => 'Submits aus Gravity Forms.',
			),
			'form:ninjaforms'     => array(
				'label'       => 'Kontaktformular: Ninja Forms',
				'description' => 'Submits aus Ninja Forms.',
			),
			'form:formidable'     => array(
				'label'       => 'Kontaktformular: Formidable Forms',
				'description' => 'Submits aus Formidable Forms.',
			),
			'form:fluentforms'    => array(
				'label'       => 'Kontaktformular: Fluent Forms',
				'description' => 'Submits aus Fluent Forms.',
			),
			'form:jetformbuilder' => array(
				'label'       => 'Kontaktformular: JetFormBuilder',
				'description' => 'Submits aus JetFormBuilder.',
			),
			'form:everestforms'   => array(
				'label'       => 'Kontaktformular: Everest Forms',
				'description' => 'Submits aus Everest Forms.',
			),
			'form:bricks'         => array(
				'label'       => 'Kontaktformular: Bricks',
				'description' => 'Submits aus Bricks-Formularen.',
			),
			'form:divi'           => array(
				'label'       => 'Kontaktformular: Divi',
				'description' => 'Submits aus Divi-Formularen.',
			),
			'form:no_js'          => array(
				'label'       => 'Kontaktformular: Kein JavaScript',
				'description' => 'Formular-Submits ohne JavaScript. Diese Anfragen werden geblockt und als No-JS-Fälle protokolliert.',
			),
			'form:no_js_view'     => array(
				'label'       => 'Kontaktformular: No-JS Seitenaufruf',
				'description' => 'Seitenaufrufe mit deaktiviertem JavaScript auf Seiten mit geschützten Formularen.',
			),
		);
		$en_map      = array(
			'login'               => array(
				'label'       => 'WordPress login',
				'description' => 'Requests on the WordPress login flow (wp-login.php and related endpoints).',
			),
			'login:no_js'         => array(
				'label'       => 'WordPress login: no JavaScript',
				'description' => 'Login requests without JavaScript. These requests are blocked because no ProofShield proof can be generated.',
			),
			'comment'             => array(
				'label'       => 'WordPress comments',
				'description' => 'Requests related to WordPress comment forms and comment submissions.',
			),
			'woo_login'           => array(
				'label'       => 'WooCommerce login',
				'description' => 'Customer login requests in WooCommerce, e.g. "My Account".',
			),
			'replay_test'         => array(
				'label'       => 'System test: replay protection',
				'description' => 'Internal test context that intentionally reuses tokens to verify replay blocking behavior.',
			),
			'contact_forms_total' => array(
				'label'       => 'Contact form submits',
				'description' => 'Aggregated totals across all protected form contexts (form:*), split into blocked and not blocked.',
			),
			'form:elementor'      => array(
				'label'       => 'Contact form: Elementor',
				'description' => 'Submissions from Elementor Forms.',
			),
			'form:cf7'            => array(
				'label'       => 'Contact form: Contact Form 7',
				'description' => 'Submissions from Contact Form 7 (CF7).',
			),
			'form:wpforms'        => array(
				'label'       => 'Contact form: WPForms',
				'description' => 'Submissions from WPForms.',
			),
			'form:enfold'         => array(
				'label'       => 'Contact form: Enfold',
				'description' => 'Submissions from the Enfold form system.',
			),
			'form:gravityforms'   => array(
				'label'       => 'Contact form: Gravity Forms',
				'description' => 'Submissions from Gravity Forms.',
			),
			'form:ninjaforms'     => array(
				'label'       => 'Contact form: Ninja Forms',
				'description' => 'Submissions from Ninja Forms.',
			),
			'form:formidable'     => array(
				'label'       => 'Contact form: Formidable Forms',
				'description' => 'Submissions from Formidable Forms.',
			),
			'form:fluentforms'    => array(
				'label'       => 'Contact form: Fluent Forms',
				'description' => 'Submissions from Fluent Forms.',
			),
			'form:jetformbuilder' => array(
				'label'       => 'Contact form: JetFormBuilder',
				'description' => 'Submissions from JetFormBuilder.',
			),
			'form:everestforms'   => array(
				'label'       => 'Contact form: Everest Forms',
				'description' => 'Submissions from Everest Forms.',
			),
			'form:bricks'         => array(
				'label'       => 'Contact form: Bricks',
				'description' => 'Submissions from Bricks forms.',
			),
			'form:divi'           => array(
				'label'       => 'Contact form: Divi',
				'description' => 'Submissions from Divi forms.',
			),
			'form:no_js'          => array(
				'label'       => 'Contact form: no JavaScript',
				'description' => 'Form submissions without JavaScript. These requests are blocked and logged as no-JS cases.',
			),
			'form:no_js_view'     => array(
				'label'       => 'Contact form: no-JS pageview',
				'description' => 'Page views with JavaScript disabled on pages that contain protected forms.',
			),
		);
		$map         = in_array( $language, array( 'de' ), true ) ? $default_map : $en_map;
		if ( isset( $map[ $context ] ) ) {
			return $map[ $context ];
		}

		if ( str_starts_with( $context, 'form:' ) ) {
			return in_array( $language, array( 'de' ), true ) ? array(
				'label'       => 'Kontaktformular (individuell/generisch)',
				'description' => 'Individueller Kontext aus generischem Selektor-Schutz oder eigener Formular-Integration.',
			) : array(
				'label'       => 'Contact form (custom/generic)',
				'description' => 'A custom context from generic selector-based protection or a custom form integration.',
			);
		}

		return array(
			'label'       => $context,
			'description' => '',
		);
	}
}
