<?php
/**
 * Shared Captcha Counters
 *
 * Roadmap item #29: per-provider passed/failed counters that back a small
 * dashboard widget. Storage is a single `wp_options` row per provider to keep
 * writes cheap and avoid table churn.
 *
 * Values are kept deliberately low-resolution (daily buckets, 30 days) so the
 * footprint stays negligible and the data resets itself naturally.
 *
 * @package WP_PowerSuite\Captcha\Shared
 * @since 1.1.0
 */

namespace WP_PowerSuite\Captcha\Shared;

/**
 * Counters helper.
 *
 * Data shape per provider:
 *   [
 *     'days' => [
 *        '2026-04-25' => ['passed' => 12, 'failed' => 3],
 *        ...
 *     ]
 *   ]
 */
final class Counters {

	/** Retain this many days of daily buckets. */
	private const RETENTION_DAYS = 30;

	/**
	 * Record a single event. No-op if `$provider_id` is empty.
	 *
	 * @param string $provider_id Module id (e.g. `google-recaptcha`).
	 * @param bool   $passed      True for a successful verification.
	 */
	public static function record( string $provider_id, bool $passed ): void {
		$provider_id = sanitize_key( $provider_id );
		if ( $provider_id === '' ) {
			return;
		}
		$option = self::optionName( $provider_id );
		$data   = get_option( $option, [] );
		if ( ! is_array( $data ) || ! isset( $data['days'] ) || ! is_array( $data['days'] ) ) {
			$data = [ 'days' => [] ];
		}
		$today = gmdate( 'Y-m-d' );
		if ( ! isset( $data['days'][ $today ] ) || ! is_array( $data['days'][ $today ] ) ) {
			$data['days'][ $today ] = [
				'passed' => 0,
				'failed' => 0,
			];
		}
		$bucket = &$data['days'][ $today ];
		if ( $passed ) {
			$bucket['passed'] = (int) $bucket['passed'] + 1;
		} else {
			$bucket['failed'] = (int) $bucket['failed'] + 1;
		}
		unset( $bucket );

		self::prune( $data['days'] );

		update_option( $option, $data, false );
	}

	/**
	 * Return totals for a provider over the retention window.
	 *
	 * @return array{passed:int,failed:int,days:array<string,array{passed:int,failed:int}>}
	 */
	public static function summary( string $provider_id ): array {
		$provider_id = sanitize_key( $provider_id );
		$empty       = [
			'passed' => 0,
			'failed' => 0,
			'days'   => [],
		];
		if ( $provider_id === '' ) {
			return $empty;
		}
		$data = get_option( self::optionName( $provider_id ), [] );
		if ( ! is_array( $data ) || empty( $data['days'] ) || ! is_array( $data['days'] ) ) {
			return $empty;
		}
		$passed = 0;
		$failed = 0;
		foreach ( $data['days'] as $bucket ) {
			if ( ! is_array( $bucket ) ) {
				continue;
			}
			$passed += isset( $bucket['passed'] ) ? (int) $bucket['passed'] : 0;
			$failed += isset( $bucket['failed'] ) ? (int) $bucket['failed'] : 0;
		}
		ksort( $data['days'] );
		return [
			'passed' => $passed,
			'failed' => $failed,
			'days'   => $data['days'],
		];
	}

	/**
	 * Drop any day buckets older than RETENTION_DAYS.
	 *
	 * @param array<string,array<string,int>> $days
	 */
	private static function prune( array &$days ): void {
		if ( count( $days ) <= self::RETENTION_DAYS ) {
			return;
		}
		ksort( $days );
		while ( count( $days ) > self::RETENTION_DAYS ) {
			array_shift( $days );
		}
	}

	private static function optionName( string $provider_id ): string {
		return 'wpps_captcha_counters_' . $provider_id;
	}
}
