<?php

/*
 * This file is part of the AuthStack project.
 *
 * (c) Buckhill Ltd <development@buckhill.co.uk>
 *
 * This source file is subject to the Buckhill license that is bundled
 * with this source code in the file LICENSE.
 */

namespace AuthStack\TwoFactor\Verifier\Implementation;

use Buckhill\RFC4226\OTP;
use AuthStack\TwoFactor\Verifier\VerifierInterface;
use AuthStack\TwoFactor\Verifier\Traits\TraitResultHandler;
use AuthStack\TwoFactor\Verifier\Traits\TraitErrorHandler;
use AuthStack\TwoFactor\Verifier\Traits\TraitFetchFromMetadata;

class RFC4226 implements VerifierInterface
{
    use TraitErrorHandler;
    use TraitFetchFromMetadata;
    use TraitResultHandler;

    /**
     * @param $otp
     * @return bool
     */
    public function verify($otp)
    {
        $secret     = $this->fromMetadata('secret');
        $algo       = $this->fromMetadata('algo', 'sha1');
        $counter    = (int)$this->fromMetadata('counter', 0);
        $window     = (int)$this->fromMetadata('window', 100); // $window is how many times we'll increment the counter in order to try to match the OTP
        $auth       = new OTP($secret, $algo);

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

        //--------------------------------------------------------------------------------------------------------------
        // OTP class throws an exception in case of errors
        //--------------------------------------------------------------------------------------------------------------

        $result = false;

        try
        {
            $value     = $auth->where($otp, $counter, $window);

            if(false === $value)
            {
                $this->setError(sprintf("No OTP match found. Started at counter value %d, stopped at %d", $counter, $counter + $window));

                $result = false;
            }
            else
            {
                // Save at which counter value we got the OTP match and advance by 1
                $this->setResult([
                    'counter' => $value + 1
                ]);

                $result = true;
            }
        }
        catch(\Exception $e)
        {
            $this->setError($e->getMessage());
        }

        return $result;
    }
}