<?php

namespace Buckhill\Yubico\Modes\OATH;


use Buckhill\RFC4226\OTP as Authenticator;
use Buckhill\Yubico\Exceptions\InvalidOTPException;

class YubikeyOATH
{
    protected $identifier;
    protected $secret;
    protected $counter = 0; // Last value of the counter saved to KSM
    protected $window = 1000;

    public function __construct($identifier, $secret, $counter = 0)
    {
        $this->identifier = $this->rmwhitespace($identifier);
        $this->secret = $this->rmwhitespace($secret);
        $this->counter = $counter;
    }


    /**
     * @param array $data
     *
     * @return static
     */
    public static function fromArray(array $data)
    {
        static::validate($data);

        return new static($data['identifier'], $data['secret'], isset($data['counter']) ? $data['counter'] : 0);
    }


    /**
     * @param array $data
     */
    protected static function validate(array $data)
    {
        $required = ['identifier', 'secret'];

        foreach($required as $key)
        {
            if(!isset($data[$key]))
            {
                throw new \InvalidArgumentException(sprintf("Required key %s not specified.", $key));
            }
        }
    }


    /**
     * @param OTP $otp
     *
     * @return bool|int
     * @throws InvalidOTPException
     */
    public function verify(OTP $otp)
    {
        $verifier = $this->getVerifier($this->secret);

        $found_at = $verifier->where($otp->getOTP(), $this->counter, $this->counter + $this->window);

        if(false === $found_at)
        {
            throw new InvalidOTPException(sprintf("Invalid OTP. No match found after %d iterations.", $this->window));
        }

        return $found_at;
    }

    /**
     * @param $str
     *
     * @return mixed
     */
    protected function rmwhitespace($str)
    {
        return str_replace(' ', '', $str);
    }


    /**
     * @param $secret
     *
     * @return Authenticator
     */
    protected function getVerifier($secret)
    {
        $verifier = new Authenticator($secret);

        $verifier->setConversionMethod(function($secret){
            return hex2bin($secret);
        });

        return $verifier;
    }
}