<?php

namespace App\Http\Controllers\Gateway;
use GuzzleHttp\ClientInterface as HttpClient;
use Illuminate\Support\Str;
use JMS\Serializer\SerializerInterface;

class KrakenAPI
{
    protected $key;     // API key
    protected $secret;  // API secret
    protected $url;     // API base URL
    protected $version; // API version
    protected $curl;    // curl handle
    private const API_URL        = 'https://api.kraken.com';
    private const API_VERSION    = 0;
    private const API_USER_AGENT = 'Kraken PHP API Agent';
   // private SerializerInterface $serializer;

    /**
     * Constructor for KrakenAPI
     *
     * @param string $key API key
     * @param string $secret API secret
     * @param string $url base URL for Kraken API
     * @param string $version API version
     * @param bool $sslverify enable/disable SSL peer verification.  disable if using beta.api.kraken.com
     */
   /* function __construct($key, $secret, $url='https://api.kraken.com', $version='0', $sslverify=true)
    {

        /* check we have curl */
     /*   if(!function_exists('curl_init')) {
            print "[ERROR] The Kraken API client requires that PHP is compiled with 'curl' support.\n";
            exit(1);
        }

        $this->key = $key;
        $this->secret = $secret;
        $this->url = $url;
        $this->version = $version;
        $this->curl = curl_init();

        curl_setopt_array($this->curl, array(
                CURLOPT_SSL_VERIFYPEER => $sslverify,
                CURLOPT_SSL_VERIFYHOST => 2,
                CURLOPT_USERAGENT => 'Kraken PHP API Agent',
                CURLOPT_POST => true,
                CURLOPT_RETURNTRANSFER => true)
        );
    }*/
    public function __construct(
        private HttpClient $client,
        private NonceGenerator $nonce,
        private SerializerInterface $serializer,
        private string $key,
        private string $secret,
        private ?string $otp = null
    ) {
    }

    function __destruct()
    {
        if(function_exists('curl_close')) {
            curl_close($this->curl);
        }
    }

    /**
     * Query public methods
     *
     * @param string $method method name
     * @param array $request request parameters
     * @return array request result on success
     * @throws KrakenAPIException
     */
    function QueryPublic($method, array $request = array())
    {
        // build the POST data string
        $postdata = http_build_query($request, '', '&');

        // make request
        curl_setopt($this->curl, CURLOPT_URL, $this->url . '/' . $this->version . '/public/' . $method);
        curl_setopt($this->curl, CURLOPT_POSTFIELDS, $postdata);
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, array());
        $result = curl_exec($this->curl);
        if($result===false)
            throw new KrakenAPIException('CURL error: ' . curl_error($this->curl));

        // decode results
        $result = json_decode($result, true);
        if(!is_array($result))
            throw new KrakenAPIException('JSON decode error');

        return $result;
    }

    /**
     * Query private methods
     *
     * @param string $method method path
     * @param array $request request parameters
     * @return array request result on success
     * @throws KrakenAPIException
     */
    function QueryPrivate($method, array $request = array())
    {
        if(!isset($request['nonce'])) {
            // generate a 64 bit nonce using a timestamp at microsecond resolution
            // string functions are used to avoid problems on 32 bit systems
            $nonce = explode(' ', microtime());
            $request['nonce'] = $nonce[1] . str_pad(substr($nonce[0], 2, 6), 6, '0');
        }

        // build the POST data string
        $postdata = http_build_query($request, '', '&');

        // set API key and sign the message
        $path = '/' . $this->version . '/private/' . $method;
        $sign = hash_hmac('sha512', $path . hash('sha256', $request['nonce'] . $postdata, true), base64_decode($this->secret), true);
        $headers = array(
            'API-Key: ' . $this->key,
            'API-Sign: ' . base64_encode($sign)
        );

        // make request
        curl_setopt($this->curl, CURLOPT_URL, $this->url . $path);
        curl_setopt($this->curl, CURLOPT_POSTFIELDS, $postdata);
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, $headers);
        $result = curl_exec($this->curl);
        if($result===false)
            throw new KrakenAPIException('CURL error: ' . curl_error($this->curl));

        // decode results
        $result = json_decode($result, true);
        if(!is_array($result))
            throw new KrakenAPIException('JSON decode error');

        return $result;
    }

    public function getDepositAddresses(string $asset, string $method, bool $new = false): array
    {
        return $this->QueryPrivate(
            'DepositAddresses',
            [
                'asset'  => 'XBT',
                'method' => 'Bitcoin',
                'new'    => $new,
            ]
        );
    }


    public function getDepositMethods(string $asset)
    {
        return $this->QueryPrivate(
            'DepositMethods',
            ['asset' => 'XBT']
        )->result[0] ?? null;
    }


    /**
     * Make request
     *
     * @param  string  $method  API Endpoint
     * @param  string  $responsePayload  Payload Class
     * @param  array  $parameters  Request data
     * @param  string  $requestMethod  Request Method to use default: POST
     * @return Response
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function request(string $method, string $responsePayload, array $parameters = [], $requestMethod = 'POST'): Response
    {
        $headers  = ['User-Agent' => self::API_USER_AGENT];
        $isPublic = Str::startsWith($method, 'public/');

        if (!$isPublic) {
            if ($this->otp) {
                $parameters['otp'] = $this->otp;
            }

            $parameters['nonce'] = $this->nonce->generate();
            $headers['API-Key']  = $this->key;
            $headers['API-Sign'] = $this->makeSignature($method, $parameters);
        }

        switch ($requestMethod) {
            case 'GET':
                $response = $this->client->request($requestMethod, self::API_URL . $this->buildPath($method), [
                    'headers' => $headers,
                    'query'   => $parameters,
                    'verify'  => true,
                ]);
                break;

            default:
                $response = $this->client->request($requestMethod, self::API_URL . $this->buildPath($method), [
                    'headers'     => $headers,
                    'form_params' => $parameters,
                    'verify'      => true,
                ]);
        }


        $responseObject = $this->serializer->deserialize(
            $response->getBody()->getContents(),
            $responsePayload,
            'json'
        );

        if ($responseObject->hasErrors()) {
            throw KrakenApiErrorException::fromArray($responseObject->error);
        }

        return $responseObject;
    }

    /**
     * @param  string  $method
     * @return string
     */
    private function buildPath(string $method): string
    {
        return '/' . self::API_VERSION . '/' . $method;
    }

    /**
     * Message signature using HMAC-SHA512 of (URI path + SHA256(nonce + POST data))
     * and base64 decoded secret API key
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return string
     */
    private function makeSignature(string $method, array $parameters = []): string
    {
        $queryString = http_build_query($parameters, '', '&');

        $signature = hash_hmac(
            'sha512',
            $this->buildPath($method) . hash('sha256', $parameters['nonce'] . $queryString, true),
            base64_decode($this->secret),
            true
        );

        return base64_encode($signature);
    }

}
class KrakenAPIException extends \ErrorException {};
