<?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 StormOnDemand extends dns\SubmoduleAbstract implements interfaces\SubmoduleIPInterface, interfaces\SubmoduleRDNSInterface, interfaces\SubmoduleTTLInterface, interfaces\SubmoduleImportInterface {  
    public $configFields = array(
        'username' => array(
            'friendlyName' => 'Username',
            'validators' => array(
                'required' => 'required',
            )
        ),
        'password' => array(
            'friendlyName' => 'Password',
            'type' => 'password',
            'validators' => array(
                'required' => 'required',
            )
        ),
        'default_ip' => array(
            'friendlyName' => 'Default IP',
            'validators' => array(
                'required' => 'required',
                'pattern' => Patterns::IP4_OR_IP6,
            )
        ),
    );
    
    public $availableTypes = array('A', 'AAAA', 'NS', 'MX', 'CNAME', 'TXT', 'PTR');
    
    public function testConnection() { 
       $info = $this->get('Utilities/Info/ping');
       return $info->ping ? true : false;
    }

    public function zoneExists() {   
        try {
            $out = $this->get('Network/DNS/Zone/list', array(
                'domain' => $this->domain,
                'page_size' => 9999
            )); 
        } catch (exceptions\DNSSubmoduleException $e) {
            if($e->getCode() == dns\SubmoduleExceptionCodes::COMMAND_ERROR) {
                return false;
            }
            throw $e;
        }
        
        foreach($out->items as $item) {
            if((string)$item->name == $this->domain) {                      
                return true;
            }
        }
        return false;
    }

    public function getRecords($recordType = false) {
        $out = $this->get('/Network/DNS/Record/list', array(
            'zone' => $this->domain
        )); 
        $return = array();
        foreach($out->items as $r) {
            $type = strtoupper((string)$r->type);
            if(in_array($type, $recordType!==false ? array(strtoupper($recordType)) : $this->getAvailableRecordTypes())) {
                $record = dns\record\Record::tryToCreateFromArray((array)$r);
                $record->line = (string)$r->id;
                $record->rdata->fromString((string)$r->rdata);
                $return[] = $record;
            }
        }
        return $return;
    }

    public function addRecord(dns\record\Record $record) {
        $params = array(
            'zone' => $this->domain,
            'name' => $record->nameToAbsolute($this->domain, false),
            'rdata' => $record->rdata->toString(),
            'ttl' => $record->ttl,
            'type' => $record->type,
        );
        
        $out = $this->get('Network/DNS/Record/create', $params);
    }

    public function editRecord(dns\record\Record $record) {
        $input = array( //TODO: sprawdzać czy należy do zona?
            'id' => $record->line,
            'name' => $record->nameToAbsolute($this->domain, false),
            'ttl' => $record->ttl,
            'rdata' => $record->rdata->toString(),
        );
        $this->get('Network/DNS/Record/update', $input);
    }

    public function deleteRecord(dns\record\Record $record) { //TODO: sprawdzanie przynależności
        $params = array('id' => $record->line); 
        $this->get('Network/DNS/Record/delete', $params);
    }

    public function removeRDNS($ip) {
        $this->get('Network/DNS/Reverse/delete', array('ip' => $ip));
    }

    public function updateRDNS($ip, $ttl = false, $value = false) {
        $this->get('Network/DNS/Reverse/update', array('ip' => $ip, 'hostname' => $value?:$this->domain));
    }
    
    public function getRDNSRecord($ip) {
        $out = $this->get('Network/IP/list', array(
            'page_size' => 9999
        ));
        
        foreach($out->items as $item) {
            if((string)$item->ip == $ip) {
                return dns\utils\ReverseDNSHelper::createPTRRecord($ip, 14400, $item->reverse_dns);
            }
        }
        return false;
    }

    public function activateZone() {        
        if($this->ip != '') {
            if(!filter_var($this->ip, FILTER_VALIDATE_IP)) {
                throw new exceptions\DNSSubmoduleException('IP is not valid!', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS);
            }
        } else {
            $this->ip = $this->config['default_ip'];
        }
        
        $out = $this->get('Network/DNS/Zone/create ', array(
            'name' => $this->domain,
            'zone_data' => array(
                'ip' => $this->ip,
            )
        ));
        
        if($out->active == '0')  {
            throw new exceptions\DNSSubmoduleException('Error occured:'.$out->error, dns\SubmoduleExceptionCodes::COMMAND_ERROR);
        }
    }
    

    public function terminateZone() {
        $out = $this->get('/Network/DNS/Zone/delete', array(
            'name' => $this->domain
        ));
        
        if(empty($out->deleted)) {
            throw new exceptions\DNSSubmoduleException('Error occured:'.$out->full_message, dns\SubmoduleExceptionCodes::COMMAND_ERROR);
        }      
    }
    
    private function get($function, $params = []) {
        $query = "https://api.stormondemand.com/".$function;
        if(count($params)>0) {
            $params = array('params'=>$params);
        }

        $curl = curl_init();		
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER,0);	
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST,0); 	
        curl_setopt($curl, CURLOPT_HEADER,0);
        curl_setopt($curl, CURLOPT_TIMEOUT, 50);

        curl_setopt($curl, CURLOPT_RETURNTRANSFER,1);	
        if(count($params)>0) {
            curl_setopt($curl,CURLOPT_POST,1);
            curl_setopt($curl,CURLOPT_POSTFIELDS,  json_encode($params));
        }
        curl_setopt($curl, CURLOPT_USERPWD, $this->config['username'].':'.$this->config['password']); 
        curl_setopt($curl, CURLOPT_URL, $query);			
        $result = curl_exec($curl); //var_dump($result); 
        
        if (curl_errno($curl)) {
            throw new exceptions\DNSSubmoduleException("cURL Error: " . curl_errno($curl) . " - " . curl_error($curl), dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM);
        }
        
        if($result == 'Authorization denied' || $result == 'Authorization required') {
            throw new exceptions\DNSSubmoduleException($result, dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM);
        }
        
        $out = json_decode($result);
        if(empty($out)) {
            throw new exceptions\DNSSubmoduleException('Unable to parse response', dns\SubmoduleExceptionCodes::INVALID_RESPONSE);
        }
        
        $this->checkForErrors($out);
        
        curl_close($curl);
        return $out;
    }

    private function checkForErrors($obj) {
        if(isset($obj->error) || isset($obj->error_class)) {
            $message = isset($obj->full_message)?$obj->full_message:$obj->error;
            throw new exceptions\DNSSubmoduleException($message, dns\SubmoduleExceptionCodes::COMMAND_ERROR);
        }
    }
    
    public function getZones() {
        $out = $this->get('Network/DNS/Zone/list', array(
            'page_size' => 9999
        )); 
        
        $return = array();
        foreach($out->items as $item) {
            $return[(string)$item->name] = (string)$item->master;
        }
        return $return;
    }
}
