<?php

namespace Buckhill\U2F\Authenticate;


use Buckhill\U2F\U2FHelper;

class Response
{
    protected $client_data;
    protected $key_handle;
    protected $signature_data;
    protected $counter = null;
    protected $decoded = [];

    public function __construct($client_data, $key_handle, $signature_data)
    {
        $this->client_data = $client_data;
        $this->key_handle = $key_handle;
        $this->signature_data = $signature_data;

        $this->decode();
    }

    public static function fromArray(array $input)
    {
        static::validate($input);

        return new static($input['clientData'], $input['keyHandle'], $input['signatureData']);
    }


    /**
     * @return string
     */
    public function getClientData()
    {
        return $this->client_data;
    }


    /**
     * @return string
     */
    public function getKeyHandle()
    {
        return $this->key_handle;
    }


    /**
     * @return string
     */
    public function getSignatureData()
    {
        return $this->signature_data;
    }


    /**
     * @return string
     */
    public function getChallenge()
    {
        return $this->fromDecoded('challenge');
    }

    /**
     * @param null $default
     *
     * @return null
     */
    public function getSignatureJSON($default = null)
    {
        return isset($this->decoded['json']) ? $this->decoded['json'] : $default;
    }


    /**
     * @return int
     */
    public function getCounter()
    {
        if(!is_int($this->counter))
        {
            throw new \DomainException("Invalid invocation of getCounter() method. There's no counter value at the moment.");
        }

        return $this->counter;
    }

    /**
     * @param $signature
     *
     * @return $this
     */
    public function setCounterFromSignature($signature)
    {
        $ctr = unpack("Nctr", substr($signature, 1, 4));

        if(is_array($ctr) && isset($ctr['ctr']))
        {
            $this->counter = $ctr['ctr'];
        }
        else
        {
            throw new \InvalidArgumentException("Invalid value provided for counter extraction.");
        }

        return $this;
    }

    /**
     * @param string $key
     * @param null   $default
     *
     * @return mixed
     */
    protected function fromDecoded($key = '', $default = null)
    {
        return (isset($this->decoded['array']) && isset($this->decoded['array'][$key])) ? $this->decoded['array'][$key] : $default;
    }


    /**
     * @return $this
     */
    protected function decode()
    {
        $decoded = U2FHelper::decode($this->getClientData());

        $json = @json_decode($decoded, true);

        if(empty($json))
        {
            throw new \InvalidArgumentException(sprintf("Invalid information provided for U2F Authentication response. Error: %s", json_last_error_msg()));
        }

        $this->decoded['json'] = $decoded;
        $this->decoded['array'] = $json;

        return $this;
    }

    /**
     * @param array $input
     */
    protected static function validate(array $input)
    {
        $required = ['clientData', 'keyHandle', 'signatureData'];

        foreach($required as $key)
        {
            if(!isset($input[$key]))
            {
                throw new \InvalidArgumentException(sprintf("Required key %s not present in the input array.", $key));
            }
        }
    }
}