<?php

namespace Buckhill\Yubico\Modes\Yubikey;

use Buckhill\Yubico\Exceptions\InvalidValueException;

class OTP
{
    const CIPHER = 'aes-128-ecb';
    const OTP_WITH_PUB_ID_LEN = 44;

    protected $value;

    public function __construct($otp)
    {
        $this->value = $otp;
    }


    /**
     * @param Yubikey $key
     *
     * @return DecryptionResult
     */
    public function decrypt(Yubikey $key)
    {
        $encrypted = $this->getEncryptedValue();

        $result = openssl_decrypt($encrypted, static::CIPHER, $key->getSecretKey(), OPENSSL_NO_PADDING | OPENSSL_RAW_DATA);

        if($result === false)
        {
            throw new \UnexpectedValueException(openssl_error_string());
        }

        return new DecryptionResult(bin2hex($result), $key);
    }


    /**
     * @return bool
     */
    public function hasPublicId()
    {
        return strlen($this->value) === static::OTP_WITH_PUB_ID_LEN;
    }


    /**
     * @return string
     */
    public function getPublicId()
    {
        return substr($this->value, 0, 12);
    }


    /**
     * @return string
     */
    public function getEncryptedValue()
    {
        $encrypted = $this->hasPublicId() ? substr($this->value, 12) : $this->value;

        return $this->hex2bin($this->modhex2hex($encrypted));
    }


    /**
     * @param $str
     *
     * @return string
     */
    public function modhex2hex($str)
    {
        return strtr($str, "cbdefghijklnrtuv", "0123456789abcdef");
    }


    /**
     * @param $input
     *
     * @return string
     * @throws InvalidValueException
     */
    public function hex2bin($input)
    {
        $result = @hex2bin($input);

        if(false === $result)
        {
            throw new InvalidValueException(sprintf("Invalid input for hex2bin: %s", $input));
        }

        return $result;
    }
}