<?php

namespace MGModule\DNSManager2\mgLibs\custom\dns\submodules\AWSRoute53;
use MGModule\DNSManager2\mgLibs\custom\dns\submodules\AWSRoute53 as awsRoute53;
use MGModule\DNSManager2 as main;

class AWSRoute53Request implements \MGModule\DNSManager2\mgLibs\custom\dns\submodules\AWSRoute53\AWSRoute53RequestInterface
{
    private $apiKey;
    private $apiSecret;
    private $apiUrl = 'route53.amazonaws.com';
    private $_curl;
    private $result;
    private $responseHandler;
    private $timeHeader;
    private $dateHeader;
    private $requestType;
    private $service = 'route53';
    private $region;
    private $relativeUrl;
    private $reqBody;
    private $action;

    public function __construct(awsRoute53\AWSRoute53ResponseInterface $responseHandler, $apiKey, $apiSecret, $region = false)
    {
        $this->apiKey = $apiKey;
        $this->apiSecret = $apiSecret;
        $this->region = $region ? $region : 'us-east-1';
        $this->responseHandler = $responseHandler;
    }
    
    public function makeRequest($url, $type, $action, $body = false)
    {
        $this->cleanResponse();
        
        $this->result = false;
        $this->reqBody = false;
        $this->requestType = $type;
        $this->relativeUrl = $url;   
        $this->action = $action;         
        $this->prepareConnectionObject($body);
        $this->setApiHeaders();
        
        $this->result = curl_exec($this->_curl);

        $this->log();
        
        if(curl_errno($this->_curl))
        {
            $this->error = curl_errno($this->_curl);
            $this->log($action.' Error');
            curl_close($this->_curl);
            $this->responseHandler->prepareTextResponse('error', $this->error);
        }

        curl_close($this->_curl);
        
        return $this->responseHandler->prepareResponse($this->result);
    }
    
    private function prepareConnectionObject($body = false)
    {
        $this->_curl = curl_init();
        
        curl_setopt($this->_curl, CURLOPT_URL, 'https://'.$this->apiUrl.$this->relativeUrl);
        curl_setopt($this->_curl, CURLOPT_CUSTOMREQUEST, $this->requestType);
        
        curl_setopt($this->_curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($this->_curl, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($this->_curl, CURLOPT_SSL_VERIFYHOST, true);
        curl_setopt($this->_curl, CURLINFO_HEADER_OUT, true);
        
        if($body)
        {
            $this->reqBody = $body;
            curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body); 
        }
    }
    
    private function setApiHeaders()
    {
        $this->timeHeader = gmdate('Ymd\THis\Z');
        $this->dateHeader = gmdate('Ymd');

        $signatrue = $this->getSignaure();

        curl_setopt($this->_curl, CURLOPT_HTTPHEADER, 
            array(
                "x-amz-date:$this->timeHeader",
                "authorization:AWS4-HMAC-SHA256 Credential=$this->apiKey/$this->dateHeader/$this->region/$this->service/aws4_request, SignedHeaders=content-length;content-type;host;user-agent;x-amz-date;x-amz-target, Signature=$signatrue"
            )
        );
    }

    private function getSignaure()
    {
        $parsedHash = $this->getSignatureKey();
        
        $scope = $this->createScope();        
       
        $canonical = $this->getCanonicalBody();

        $toSign = $this->createStringToSign($this->timeHeader, $scope, $canonical);
        $signature = hash_hmac('sha256', $toSign, $parsedHash);
        
        return $signature;
    }

    private function getCanonicalBody()
    {
        $payloadHash = $this->getPayloadHash();
        
        $url = explode('?', $this->relativeUrl);
        
        $contLenght = strlen($this->reqBody);
        $boundary = $this->requestType === 'POST' ?  
            "content-length:$contLenght\ncontent-type:application/x-www-form-urlencoded\n"
            : "content-length:\ncontent-type:\n";
        
        $canonical = $this->requestType."\n";
        $canonical .= $url[0]."\n$url[1]\n";
        $canonical .= $boundary."host:$this->apiUrl\nuser-agent:\n";
        $canonical .= "x-amz-date:$this->timeHeader\n";
        $canonical .= "x-amz-target:\n\ncontent-length;content-type;host;user-agent;x-amz-date;x-amz-target\n";
        $canonical .= $payloadHash;

        return $canonical;
    }

    private function log($action = false)
    {
        $addonConfig = main\addon::config();   
        logmodulecall(
            $addonConfig['name'], 
            'AWSRoute53 '.($action ? $action : $this->action), 
            curl_getinfo($this->_curl),
            $this->result,
            null,
            array($this->apiKey, $this->apiSecret)
        );               
    }
    
    private function getSignatureKey()
    {
        $dateKey = hash_hmac('sha256', $this->dateHeader, "AWS4{$this->apiSecret}", true);
        $regionKey = hash_hmac('sha256', $this->region, $dateKey, true);
        $serviceKey = hash_hmac('sha256', $this->service, $regionKey, true);
        $finalHash = hash_hmac('sha256', 'aws4_request', $serviceKey, true);
                 
        return $finalHash;
    }
    
    private function createStringToSign($longDate, $credentialScope, $creq)
    {
        $hash = hash('sha256', $creq);

        return "AWS4-HMAC-SHA256\n{$longDate}\n{$credentialScope}\n{$hash}";
    }    
    
    private function createScope()
    {
        return "$this->dateHeader/$this->region/$this->service/aws4_request";
    }    
    
    private function getPayloadHash()
    {
        return hash('sha256', $this->reqBody ? : '');
    }
    
    private function cleanResponse()
    {
        $rsponseHandlerClassName = get_class($this->responseHandler);
        $this->responseHandler = new $rsponseHandlerClassName;
    }
}
