<?php

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

require 'InterWorx/InterworxAPI.php';

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;
use \MGModule\DNSManager2\mgLibs\custom\dns\submodules\InterWorx\InterworxAPI;

class InterWorx extends dns\SubmoduleAbstract implements 
        interfaces\SubmoduleIPInterface, 
        interfaces\SubmoduleRDNSInterface, 
        interfaces\SubmoduleTTLInterface, 
        interfaces\SubmoduleImportInterface 
{      
    public $configFields = array(
        'serverusername' =>array (
                'friendlyName'  =>  'Email',
                'validators' => array(
                    'required' => 'required',
                )
        ),
        'serverpassword' =>array (
                'friendlyName'  => 'User Password',
                'type'=> 'password',
        ),
        'serveraccesshash' =>array (
                'friendlyName'  => 'API Key',
                'type'=> 'textarea',
        ),
        'serverip' =>array (
                'friendlyName'  =>  'Hostname/IP',
                'validators' => array(
                    'required' => 'required',
                )
        ),
        'serversecure' =>array (
                'friendlyName'  =>  'Enable SSL',
                'type'=> 'yesno',
        ),
        'default_ip' =>array (
                'friendlyName'  =>  'Default IP',  
                'validators' => array(
                    'required' => 'required',
                    'pattern' => Patterns::IP4_OR_IP6,
                )
        ),
        'serverport' =>array (
                'friendlyName'  =>  'Port',
        ),
    );
    
    public $availableTypes = array('A', 'AAAA', 'CNAME', 'MX', 'NAPTR', 'NS', 'PTR', 'SPF', 'SRV', 'TXT');
    
    public function testConnection() {
        $conn = $this->getConnectionToAPI();
        $res = $conn->nodeworx->users->list();
        if ($res['status'] != '0')
        {
            throw new exceptions\DNSSubmoduleException('Error - Authentication Error (NodeWorx)', dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM);
        }else{
            return true;
        }
    }
    
    public function getRecords($recordType = false) {
        $conn = $this->getConnectionToAPI();
        $out = $conn->nodeworx->dns->record->queryRecords(array('domain' => $this->domain));

        $return = array(); 
        if ($out['status'] != '0'){
            throw new exceptions\DNSSubmoduleException('Unable to fetch records from server', dns\SubmoduleExceptionCodes::COMMAND_ERROR);
        }

        foreach ($out['payload'] as $key => $r) {
            if (in_array((string)$r->type, $recordType!==false ? array(strtoupper($recordType)) : $this->getAvailableRecordTypes()))
            {
                $r->line = $r->record_id;
                $r->name = $r->host;
       
                switch ((string)$r->type) {
                    case 'A':
                        $r->rdata = array('address' => (string)$r->target);
                        break;
                    case 'AAAA':
                        $r->rdata = array('address' => (string)$r->target);
                        break;
                    case 'CNAME':
                        $r->rdata = array('cname' => (string)$r->target);
                        break;
                    case 'MX':
                        $rawRdata = explode(' ', (string)$r->target);
                        $r->rdata = array('preference' => $rawRdata[0],
                                          'exchange' => $rawRdata[1]);
                        break;
                    case 'NAPTR':
                        $rawRdata = explode(' ', (string)$r->target);
                        $r->rdata = array('order' => $rawRdata[0],
                                          'preference' => $rawRdata[1],
                                          'flags' => $rawRdata[2],
                                          'services' => $rawRdata[3],
                                          'regexp' => $rawRdata[4],
                                          'replacement' => $rawRdata[5]);
                        break;
                    case 'NS':
                        $r->rdata = array('nsdname' => (string)$r->target);
                        break;
                    case 'PTR':
                        $r->rdata = array('ptrdname' => (string)$r->target);
                        break;
                    case 'SPF':
                        $r->rdata = array('txtdata' => (string)$r->target);
                        break;
                    case 'SRV':
                        $rawRdata = explode(' ', (string)$r->target);
                        $r->rdata = array('priority' => $rawRdata[0],
                                          'weight' => $rawRdata[1],
                                          'port' => $rawRdata[2],
                                          'target' => $rawRdata[3]);
                        break;
                    case 'TXT':
                        $r->rdata = array('txtdata' => (string)$r->target);
                        break;
                }
                $return[] = dns\record\Record::tryToCreateFromArray((array)$r);
            }
        }
        return $return;
    }
    
    public function addRecord(dns\record\Record $record)
    {
        $input = $record->toMergedArray(false);
         
        $input['domain'] = $this->domain;

        $conn = $this->getConnectionToAPI();
        $res = $conn->nodeworx->dns->zone->queryZones(array('domain' => $this->domain));
        $zoneid = $res['payload'][0]->zone_id;
        $params = array('zone_id' => $zoneid,
                        'host' => $record->nameToAbsolute($this->domain, false),
                        'ttl' => $input['ttl']);
        
        switch ($input['type']) {
            case 'A':
                $params['ipaddress'] = $input['address'];
                $result = $conn->nodeworx->dns->record->addA($params);
                break;
            case 'AAAA':
                $params['ipaddress'] = $input['address'];
                $result = $conn->nodeworx->dns->record->addAAAA($params);
                break;
            case 'CNAME':
                $params['alias'] = $input['cname'];
                $result = $conn->nodeworx->dns->record->addCNAME($params);
                break;
            case 'MX':
                $params['preference'] = $input['preference'];
                $params['mail_server'] = $input['exchange'];
                $result = $conn->nodeworx->dns->record->addMX($params);
                break;
            case 'NAPTR':
                $params['domain'] = $input['name'];
                $params['order'] = $input['order'];
                $params['preference'] = $input['preference'];
                $params['flag'] = $input['flags'];
                $params['service'] = $input['services'];
                $params['regex'] = $input['regexp'];
                $params['replacement'] = $input['replacement'];
                $result = $conn->nodeworx->dns->record->addNAPTR($params);
                break;
            case 'NS':
                $params['nameserver'] = $input['nsdname'];
                $result = $conn->nodeworx->dns->record->addNS($params);
                break;
            case 'PTR':
                if(strlen($input['name']) < 7 && isset($_POST['ip']) && $_POST['ip'] != '')
                {
                    $input['name'] = $_POST['ip'];
                }

                $params['domain'] = $input['ptrdname'];
                $params['ip_data'] = $input['name'];

                $result = $conn->nodeworx->dns->record->addPTR($params);
                break;
            case 'SPF':
                $params['spf_record_value'] = $input['txtdata'];
                $result = $conn->nodeworx->dns->record->addSPF($params);
                break;
            case 'SRV':
                $requestdata = explode('.', $input['name']);
                $params['service'] = $requestdata[0];
                $params['protocol'] = $requestdata[1];
                $params['domain'] = $requestdata[2].'.'.$requestdata[3];
                $params['priority'] = $input['priority'];
                $params['weight'] = $input['weight'];
                $params['port'] = $input['port'];
                $params['target'] = $input['target'];
                $result = $conn->nodeworx->dns->record->addSRV($params);
                break;
            case 'TXT':
                $params['text'] = $input['txtdata'];
                $result = $conn->nodeworx->dns->record->addTXT($params);
                break;
            default:
                break;
        }
        
        $this->logAction('addRecord', $params, $record);
        
        if($result['status'] != 0)
        {
            throw new exceptions\DNSSubmoduleException($result['payload'], dns\SubmoduleExceptionCodes::COMMAND_ERROR);
        }

        return true;
    }
    
    public function editRecord(dns\record\Record $record) {
        $conn = $this->getConnectionToAPI();
        $records = $this->getRecords();
        
        if(empty($records))
            return false;
        
        $lines = array();
        foreach($records as $r) {
            $lines[$r->line] = $r;
        }

        if(!isset($lines[$record->line]) || !$record->isEqualTo($lines[$record->line])) {
            $input = $record->toMergedArray(false);
            
            $params = (array('record_id' => $input['line'],
                             'host' => substr($input['name'], 0, -1),
                             'ttl' => $input['ttl']));

            switch ($input['type']) {
                case 'A':
                    $params['ipaddress'] = $input['address'];
                    $result = $conn->nodeworx->dns->record->editA($params);
                    break;
                case 'AAAA':
                    $params['ipaddress'] = $input['address'];
                    $result = $conn->nodeworx->dns->record->editAAAA($params);
                    break;
                case 'CNAME':
                    $params['alias'] = $input['cname'];
                    $result = $conn->nodeworx->dns->record->editCNAME($params);
                    break;
                case 'MX':
                    $params['preference'] = $input['preference'];
                    $params['mail_server'] = $input['exchange'];
                    $result = $conn->nodeworx->dns->record->editMX($params);
                    break;
                case 'NAPTR':
                    $params['domain'] = substr($input['name'], 0, -1);
                    $params['order'] = $input['order'];
                    $params['preference'] = $input['preference'];
                    $params['flag'] = $input['flags'];
                    $params['service'] = $input['services'];
                    $params['regex'] = $input['regexp'];
                    $params['replacement'] = $input['replacement'];
                    $result = $conn->nodeworx->dns->record->editNAPTR($params);
                    break;
                case 'NS':
                    $params['nameserver'] = $input['nsdname'];
                    $result = $conn->nodeworx->dns->record->editNS($params);
                    break;
                case 'PTR':
                    $params['domain'] = $input['ptrdname'];
//                    $params['ip_data'] = $this->domain;
                    $result = $conn->nodeworx->dns->record->editPTR($params);
                    break;
                case 'SPF':
                    $params['spf_record_value'] = $input['txtdata'];
                    $result = $conn->nodeworx->dns->record->editSPF($params);
                    break;
                case 'SRV':
                    $requestdata = explode('.', $input['name']);
                    $params['service'] = $requestdata[0];
                    $params['protocol'] = $requestdata[1];
                    $params['domain'] = $requestdata[2].'.'.$requestdata[3];
                    $params['priority'] = $input['priority'];
                    $params['weight'] = $input['weight'];
                    $params['port'] = $input['port'];
                    $params['target'] = $input['target'];
                    $result = $conn->nodeworx->dns->record->editSRV($params);
                    break;
                case 'TXT':
                    $params['text'] = $input['txtdata'];
                    $result = $conn->nodeworx->dns->record->editTXT($params);
                    break;
                default:
                    break;
            }
            $this->logAction('editRecord', $params, $record);
            if($result['status'] != 0)
            {
                throw new exceptions\DNSSubmoduleException($result['payload'], dns\SubmoduleExceptionCodes::COMMAND_ERROR);
            }
        }
        
        
    }
    
    public function deleteRecord(dns\record\Record $record) {
        $conn = $this->getConnectionToAPI();
        $result = $conn->nodeworx->dns->record->delete(array('record_id' => $record['line']));
        if($result['status'] != 0)
        {
            throw new exceptions\DNSSubmoduleException($result['payload'], dns\SubmoduleExceptionCodes::INVALID_PARAMETERS);
        }
    }
    
    public function zoneExists() {
        try {
            $conn = $this->getConnectionToAPI();
        
            $out = $conn->nodeworx->dns->zone->listZones();
        } catch (exceptions\DNSSubmoduleException $e) {
            if($e->getCode() == dns\SubmoduleExceptionCodes::COMMAND_ERROR) {
                return false;
            }
            throw $e;
        }
        
        foreach($out['payload'] as $zone) {
            if((string)$zone->domain == $this->domain) {
                return true;
            }
        }
        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'];
        }
        $params = array(
            'domain' => $this->domain,
            'ipaddr' => $this->ip
        );
        if($this->config['template'] != "")
            $params['template'] = $this->config['template']; 
        
        try {
            $conn = $this->getConnectionToAPI();
            $result = $conn->nodeworx->dns->zone->add($params);
            if($result['status'] != 0)
            {
                throw new exceptions\DNSSubmoduleException($result['payload'], dns\SubmoduleExceptionCodes::INVALID_PARAMETERS);
            }
        } catch (exceptions\DNSSubmoduleException $e) {
            throw $e;
        }
    }
   
    public function terminateZone() {
        $conn = $this->getConnectionToAPI();
        $res = $conn->nodeworx->dns->zone->queryZones(array('domain' => $this->domain));
        $zoneid = $res['payload'][0]->zone_id;
        $result = $conn->nodeworx->dns->zone->delete(array('zone_id' => $zoneid));
        if($result['status'] != 0)
        {
            throw new exceptions\DNSSubmoduleException($result['payload'], dns\SubmoduleExceptionCodes::INVALID_PARAMETERS);
        }
    }
    
    public function getZones()
    {
        $out = array();
        $conn = $this->getConnectionToAPI();
        
        $res = $conn->nodeworx->dns->zone->listZones();

        if($res['status'] != 0)
        {
            throw new exceptions\DNSSubmoduleException($res['payload'], dns\SubmoduleExceptionCodes::INVALID_PARAMETERS);
        }

        foreach($res['payload'] as $zone)
        {
            $out[$zone->domain] = '';
        }

        return $out;
    }
    
    private function getConnectionToAPI()
    {
        $conn = new InterworxAPI();
        $doRequest = $conn->getInstance($this->config);
        return $doRequest;
    }
    private function logAction($action, $request, $response)
    {
        $addonConfig = \MGModule\DNSManager2\addon::config();   
        logmodulecall(
            $addonConfig['name'], 
            'InterWorx '.($action), 
            array($request),
            array($response),
            null,
            array($this->config['hostname'], $this->config['hostname'], $this->config['hash'])
        ); 
    }
}
