<?php

declare(strict_types=1);

namespace BotGuard\Services\Captcha;

/**
 * Self-hosted slider-alignment puzzle (the default, key-free challenge).
 *
 * On issue() the server picks a random target offset and stores it with the
 * verification session. The client renders a draggable piece and a notch at the
 * target, and the user drags to align them. verify() checks the submitted
 * position is within tolerance of the target. On its own the puzzle is modest
 * security; its real value is combined with the behavioral mouse/timing modules
 * that score *how* the piece was dragged.
 */
final class PuzzleProvider implements CaptchaProviderInterface
{
    private const TRACK_WIDTH = 280;
    private const PIECE_SIZE = 42;
    private const TOLERANCE = 10;

    public function name(): string
    {
        return 'puzzle';
    }

    public function issue(): array
    {
        // Target somewhere in the right ~60% of the track so a drag is required.
        $min = (int) (self::TRACK_WIDTH * 0.35);
        $max = self::TRACK_WIDTH - self::PIECE_SIZE - 6;
        $target = random_int($min, $max);

        return [
            'client' => [
                'type'        => 'slider',
                'track_width' => self::TRACK_WIDTH,
                'piece_size'  => self::PIECE_SIZE,
                'target'      => $target,
                'tolerance'   => self::TOLERANCE,
            ],
            'answer' => (string) $target,
        ];
    }

    public function verify(array $input, ?string $serverAnswer, string $clientIp = ''): CaptchaResult
    {
        if ($serverAnswer === null || $serverAnswer === '') {
            return CaptchaResult::fail($this->name(), ['reason' => 'no_challenge_issued']);
        }

        $position = (int) ($input['position'] ?? -9999);
        $solveTime = max(0, (int) ($input['solve_time_ms'] ?? 0));
        $retries = max(0, (int) ($input['retries'] ?? 0));
        $target = (int) $serverAnswer;

        $aligned = abs($position - $target) <= self::TOLERANCE;

        return new CaptchaResult(
            verified: $aligned,
            provider: $this->name(),
            solveTimeMs: $solveTime,
            retries: $retries,
            detail: [
                'target'    => $target,
                'position'  => $position,
                'delta'     => abs($position - $target),
                'tolerance' => self::TOLERANCE,
            ],
        );
    }
}
