<?php

namespace MGModule\DNSManager2\mgLibs\custom\dns\submodules;

use \MGModule\DNSManager2\mgLibs\custom\dns;
use \MGModule\DNSManager2\mgLibs\custom\dns\exceptions;
use \MGModule\DNSManager2\mgLibs\custom\dns\interfaces;
use \MGModule\DNSManager2\mgLibs\custom\dns\utils\Patterns;

class DynECT extends dns\SubmoduleAbstract implements interfaces\SubmoduleTTLInterface, interfaces\SubmoduleImportInterface, interfaces\SubmoduleRDNSInterface/*, interfaces\SubmoduleDNSSecInterface*/ {  
    CONST URL = 'https://api.dynect.net/REST/';
    CONST WSDL = 'https://api2.dynect.net/wsdl/current/Dynect.wsdl'; //'https://api2.dynect.net/wsdl/3.0.0/Dynect.wsdl'
    
    public $configFields = array(
        'username' =>array (
                'friendlyName'  =>  'Username',
                'validators' => array(
                    'required' => 'required',
                )
        ),
        'password' =>array (
                'friendlyName'  => 'User Password',
                'type'=> 'password',
                'validators' => array(
                    'required' => 'required',
                )
        ),
        'customer_name' =>array (
                'friendlyName'  => 'Customer Name',
        ),
        'hostname' =>array (
                'friendlyName'  =>  'Hostname/IP',
                'validators' => array(
                    'required' => 'required',
                )
        ),
        'ssl' =>array (
                'friendlyName'  =>  'Enable SSL',
                'type'=> 'yesno',
        ),
        'default_ip' =>array (
                'friendlyName'  =>  'Default IP',
                'validators' => array(
                    'pattern' => Patterns::IP4_OR_IP6,
                )
        ),
        'adminemail' =>array (
                'friendlyName'  =>  'Admin Email',
                'type'=> 'email',
                'validators' => array(
                    'required' => 'required',
                )
        ),
        'default_ttl' =>array ( //??
                'friendlyName'  =>  'Default TTL',
                'type' => 'number',
                'help' => 'Default TTL (in seconds) for records in the zone',
                'validators' => array(
                    'required' => 'required',
                ),
        ),
        /*'dnssec_contact_nickname'   =>  array(
            'friendlyName'  =>  'DNSSEC Contact Nickname',
            'type'          =>  'text',
        )*/
    );
    
    public $availableTypes =  array('A', 'AAAA', 'NS', 'MX', 'CNAME', 'DNAME', 'LOC', 'TXT', 'SRV', 'PTR', 'DS');
    
    private $token;
    
    public function getRecords($recordType = false){
        $return = $this->execute('GET', 'AllRecord/'.$this->domain);
        $out = array();
        
        foreach($return['data'] as $url)
        {
            $url        =   substr($url, 6);
            $type       =   substr($url, 0, strpos($url, 'Record'));
            
            if(!in_array(strtoupper($type), $this->getAvailableRecordTypes()))
            {
                continue;
            }
                
            $data    =   $this->execute('GET', $url);
            
            $record = new dns\record\Record();
            $record->line = $data['data']['record_id'];
            $record->name = $data['data']['fqdn'];
            $record->type = $data['data']['record_type'];
            $record->ttl =  $data['data']['ttl'];
            $record->createRDATAObject();
            $record->rdata->setDataFromArray($data['data']['rdata']);
            
            if($type == 'DS')
            {
                $record->rdata->digesttype  =    $data['data']['rdata']['digtype']; 
            }

            $out[] = $record;      
        }
        
        return $out;
    }
    
    private function recordToParamsArray(dns\record\Record $record) {
        $params = array(
            'rdata' => $record->rdata->toArray(false),
            'ttl' => $record->ttl,
        );
        
        return $params;
    }
    
    public function editRecord(dns\record\Record $record) {
        $params = $this->recordToParamsArray($record);
        
        $this->execute('PUT', $record->type.'Record/'.$this->domain.'/'.$record->nameToAbsolute($this->domain, false) . '/' . $record->line, $params);
        
        $this->publishZone();
    }
    
    public function deleteRecord(dns\record\Record $record) {
        $ret = $this->execute('DELETE', $record->type.'Record/'.$this->domain.'/'.$record->nameToAbsolute($this->domain, false) . '/' . $record->line);
        
        $this->publishZone();
    }
    
    public function addRecord(dns\record\Record $record) {
        $params = $this->recordToParamsArray($record);
        
        $this->execute('POST', $record->type.'Record/'.$this->domain.'/'.$record->nameToAbsolute($this->domain, false), $params);
        
        $this->publishZone();
    }
    
    public function zoneExists() {
        try {
            $this->execute('GET', 'Zone/' . $this->domain);
        } catch (exceptions\DNSSubmoduleException $e) {
            if($e->getCode() == dns\SubmoduleExceptionCodes::COMMAND_ERROR) {
                return false;
            }
            throw $e;
        }
        return true;
    }
    
    public function terminateZone() {
        $this->execute('DELETE','Zone/'.$this->domain);
    }
    
    public function activateZone() {
        $params = array(
            'ttl'           => $this->config['default_ttl'],
            'rname'         => $this->config['adminemail'],
            'serial_style'  => 'epoch'
        );
        if(!$this->zoneExists()) {
            $this->execute('POST','Zone/'.$this->domain, $params);
        }
        $this->execute('PUT','Zone/'.$this->domain, array('publish' => 'true'));
        
    }
    
    protected function publishZone(){
        $this->execute('PUT','Zone/'.$this->domain, array(
            'publish'   =>  1
        ));
    }
    
    private function execute($method, $function, $params = array()){
        if(empty($this->token) && $function != 'Session') {
            $this->login();
        }
        
        $header = array();
        $header[] = 'Content-Type: application/json';
        if ($function != 'Session'){
            $header[] = 'Auth-Token: '. $this->token;
        }
        
        $url    = self::URL . $function . '/';
        if (!empty($params) && strtoupper($method) == 'GET') {
            $params = $this->getParams($params);
            $url .= '?'.$params;
        }
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));
        if (!empty($params)) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
        }
        curl_setopt($ch, CURLOPT_TIMEOUT, 20);
        $curl_result = curl_exec($ch);
        //$this->http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        
        if (curl_errno($ch)) {
            throw new exceptions\DNSSubmoduleException("cURL Error: " . curl_errno($ch) . " - " . curl_error($ch), dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM);
        }
        curl_close($ch);
        
        $json = json_decode($curl_result, true);
        if(empty($json)) {
            throw new exceptions\DNSSubmoduleException('Unable to parse response', dns\SubmoduleExceptionCodes::INVALID_RESPONSE);
        }
        
        if($json['status'] != 'success') { 
            if(!empty($json['msgs'])) {
                $errors = array();
                foreach($json['msgs'] as $message) {
                    $errors[] = $message['INFO'];
                }
                
                throw new exceptions\DNSSubmoduleException(implode('; ', $errors), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
            }
            throw new exceptions\DNSSubmoduleException('Unknown Error', dns\SubmoduleExceptionCodes::COMMAND_ERROR);
        }
        
        return $json;
    }
    
    private function getParams($params){
        $string = '';
        foreach ($params as $key => $value){
            $string .= $key.'='.$value.'&';
        }
        $string = substr($string, 0, -1); //erase last &
        return $string;
    }
    
    private function login(){
        $result = $this->execute('POST', 'Session', 
                array(
                    'customer_name' => $this->config['customer_name'],
                    'user_name' => $this->config['username'], 
                    'password' => $this->config['password']
                )
        );
        
        $this->token = trim($result['data']['token']);
    }
    
    public function testConnection() { 
        $this->login();
    }

    public function getZones() {
        $result = $this->execute('GET', 'Zone' . $this->domain);
        
        $out = array();
        foreach($result as $zone) {
            $out[$zone->zone] = '';
        }
        return $out;
    }
    
    /**
     * 
     * @return boolean
     */
    public function isSigned()
    {
        return $this->getSignKeys() ? true : false;
    }
    
    public function generateSignKeys() {
    }

    /**
     * 
     * @return Array of \MGModule\DNSManager2\mgLibs\custom\dns\submodules\dnssec\Key
     */
    public function getSignKeys()
    {
        try
        {
            $ret    =   $this->execute('GET', 'DNSSEC/'.$this->domain);
            $dnssec =   new dns\dnssec\DnsSec();
            
            foreach($ret['data']['keys'] as $k) 
            {
                $key    =   null;
                if($k['type'] == 'ZSK')
                {
                    $key = new dns\dnssec\ZSK();
                }
                else
                {
                    $key = new dns\dnssec\KSK();
                }
                
                $key->setId($k['dnssec_key_id']);
                $key->setExpires($k['expire_ts']);
                $key->setLifetime($k['lifetime']);
                $key->setBits($k['bits']);
                
                //DNSKEY
                $dnskey = new dns\record\type\DNSKEY();
                $dnskey->setProtocol($k['dnskey']['protocol']);
                $dnskey->setFlags($k['dnskey']['flags']);
                $dnskey->setAlgorithm($k['dnskey']['algorithm']);
                $dnskey->setPublicKey($k['dnskey']['public_key']);
                
                $key->setDnsKey($dnskey);
                
                //DS
                if($k['ds']['keytag'])
                {
                    $ds = new dns\record\type\DS();
                    $ds->setAlgorithm($k['ds']['algorithm']);
                    $ds->setKeytag($k['ds']['keytag']);
                    $ds->setDigestType($k['ds']['digtype']);
                    $ds->setDigest($k['ds']['digest']);

                    $key->setDs($ds);
                }
                
                $dnssec->addKey($key);
            }
            
            return $dnssec;
        }
        catch(exceptions\DNSSubmoduleException $ex)
        {
            if(strpos($ex->getMessage(), 'No such service in your account'))
            {
                return null;
            }

            throw $ex;
        }
    }

    public function sign() {
        $this->execute('POST', 'DNSSEC/'.$this->domain, array(
            'keys'  =>  array
            (
                array
                (
                    'type'          =>  'KSK',
                    'algorithm'     =>  'RSA/SHA-1',
                    'bits'          =>  2048,
                    'start_ts'      =>  time(),
                    'lifetime'      =>  60*60*24*356,
                    'overlap'       =>  60*60*24*7,
                    // use default value 
                    //'expire_ts' =>  ''
                ),
                array
                (
                    'type'          =>  'ZSK',
                    'algorithm'     =>  'RSA/SHA-1',
                    'bits'          =>  2048,
                    'start_ts'      =>  time(),
                    'lifetime'      =>  60*60*24*356,
                    'overlap'       =>  60*60*24*7,
                    // use default value 
                    //'expire_ts' =>  ''
                )
            ),
            'contact_nickname'  =>  $this->config['dnssec_contact_nickname']
        ));
    }

    public function unsign() {
        $this->execute('DELETE', 'DNSSEC/'.$this->domain);
    }
}