<?php

namespace App\Services;

class PhysicalAttendanceGateCode
{
    /** @var string */
    protected $secret;

    /** @var int */
    protected $period;

    public function __construct()
    {
        $this->secret = (string) config('physical_attendance_gate.secret');
        $this->period = max(30, (int) config('physical_attendance_gate.period_seconds', 60));
    }

    public function isConfigured()
    {
        return $this->secret !== '';
    }

    /**
     * Current code and countdown for the counter display.
     *
     * @param  int  $electionId
     * @return array{code:string,seconds_remaining:int,period_seconds:int}
     */
    public function currentPayload($electionId)
    {
        $slot = $this->currentSlot();
        $code = $this->codeForSlot((int) $electionId, $slot);
        $expiresAt = ($slot + 1) * $this->period;
        $secondsRemaining = max(0, $expiresAt - time());

        return [
            'code' => $code,
            'seconds_remaining' => $secondsRemaining,
            'period_seconds' => $this->period,
        ];
    }

    /**
     * Accept current, previous, or next slot to tolerate minor clock skew.
     *
     * @param  int  $electionId
     * @param  string  $input
     * @return bool
     */
    public function validates($electionId, $input)
    {
        if (!$this->isConfigured()) {
            return false;
        }

        $normalized = strtoupper(preg_replace('/\s+/', '', (string) $input));

        if (strlen($normalized) < 4) {
            return false;
        }

        $slot = $this->currentSlot();

        foreach ([$slot - 1, $slot, $slot + 1] as $s) {
            if ($s < 0) {
                continue;
            }
            if (hash_equals($this->codeForSlot((int) $electionId, $s), $normalized)) {
                return true;
            }
        }

        return false;
    }

    protected function currentSlot()
    {
        return intdiv(time(), $this->period);
    }

    /**
     * Deterministic code for one election and time slot.
     *
     * @param  int  $electionId
     * @param  int  $slot
     * @return string
     */
    protected function codeForSlot($electionId, $slot)
    {
        $material = (string) $electionId.'|'.(string) $slot;
        $hex = hash_hmac('sha256', $material, $this->secret);

        return strtoupper(substr($hex, 0, 8));
    }
}
