<?php declare(strict_types = 1);

namespace MailPoet\Captcha;

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


use MailPoet\WP\Functions as WPFunctions;

/**
 * Evaluates client-side behavioral counters to decide whether a submission
 * looks human. Used when no CAPTCHA is configured: silent pass when signals
 * look human, escalate to the built-in CAPTCHA otherwise.
 */
class BehavioralSignals {
  const FIELD_NAME = 'behavioral_signals';

  const DEFAULT_MIN_TIME_MS = 2000;
  const DEFAULT_MIN_INTERACTIONS = 3;
  const DEFAULT_MIN_FIELD_FOCUS = 1;

  private WPFunctions $wp;

  public function __construct(
    WPFunctions $wp
  ) {
    $this->wp = $wp;
  }

  public function looksHuman(array $data): bool {
    $rawSignals = $data[self::FIELD_NAME] ?? null;
    $result = is_array($rawSignals);
    $signals = is_array($rawSignals) ? $rawSignals : [];

    $thresholds = [
      'min_time_ms' => self::DEFAULT_MIN_TIME_MS,
      'min_interactions' => self::DEFAULT_MIN_INTERACTIONS,
      'min_field_focus' => self::DEFAULT_MIN_FIELD_FOCUS,
    ];
    $timeMs = $this->intSignal($signals, 'time_ms');
    $fieldFocusCount = $this->intSignal($signals, 'focus_count');

    if ($timeMs < $thresholds['min_time_ms']) {
      $result = false;
    }
    if ($fieldFocusCount < $thresholds['min_field_focus']) {
      $result = false;
    }

    $mousemoveCount = $this->intSignal($signals, 'mm_count');
    $keydownCount = $this->intSignal($signals, 'kd_count');
    $scrollCount = $this->intSignal($signals, 'scroll_count');
    $isTouch = !empty($signals['touch']);

    if ($result) {
      // OR-logic across device-appropriate interaction channels handles
      // password-manager autofill (no keydown), mobile (no mousemove),
      // and pure-mouse users (no keydown).
      if ($isTouch) {
        $result = $scrollCount >= 1 || $keydownCount >= $thresholds['min_interactions'];
      } else {
        $result = $mousemoveCount >= $thresholds['min_interactions']
                  || $keydownCount >= $thresholds['min_interactions'];
      }
    }

    return (bool)$this->wp->applyFilters('mailpoet_behavioral_signals_looks_human', $result, $rawSignals, $data);
  }

  private function intSignal(array $signals, string $key): int {
    $value = $signals[$key] ?? null;
    return is_numeric($value) ? (int)$value : 0;
  }
}