<?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 Plesk extends dns\SubmoduleAbstract implements interfaces\SubmoduleImportInterface { 

    public $configFields = array(
        'host' => array(
            'friendlyName'  =>  'Host',
            'validators' => array(
                'required' => 'required',
            )
        ),
        'username' => array(
            'friendlyName' => 'Username',
            'validators' => array(
                'required' => 'required',
            )
        ),
        'password' => array(
            'friendlyName' => 'Password',
            'type'=> 'password',
            'validators' => array(
                'required' => 'required',
            )
        ),
        'substriction_domain' => array(
            'friendlyName' => 'Subscription Domain',
            'validators' => array(
                'required' => 'required',
            )
        ),
        'default_ip' => array(
             'friendlyName' => 'Default IP',
            'validators' => array(
                'pattern' => Patterns::IP4_OR_IP6,
            )
        ),
    );
    
    public $availableTypes = array('A', 'AAAA', 'NS', 'MX', 'CNAME', 'TXT', 'SRV', 'PTR');

    private $webspace_id = null;
    
    private function get($packet) {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, "https://{$this->config['host']}:8443/enterprise/control/agent.php");
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $packet);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_HTTPHEADER, array(
            "HTTP_AUTH_LOGIN: {$this->config['username']}",
            "HTTP_AUTH_PASSWD: {$this->config['password']}",
            "HTTP_PRETTY_PRINT: TRUE",
            "Content-Type: text/xml")
        );

        $retval = curl_exec($curl);
        if (curl_errno($curl)) {
            throw new exceptions\DNSSubmoduleException("cURL Error: " . curl_errno($curl) . " - " . curl_error($curl), dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM);
        }
        
        curl_close($curl);
        
        @$result = simplexml_load_string($retval);
        if($result===FALSE) {
            throw new exceptions\DNSSubmoduleException('Unable to parse response', dns\SubmoduleExceptionCodes::INVALID_RESPONSE);
        }
        
        if($result->system->status == 'error') {
            throw new exceptions\DNSSubmoduleException((string)$result->system->errtext, dns\SubmoduleExceptionCodes::COMMAND_ERROR);
        }
        
        return $result;
    }
    
    public function testConnection(){
        $this->webspace_id= $this->getWebspaceID();
        $ok = $this->webspace_id !== false && $this->webspace_id > 0;
        return true;
    }
    
    private function getWebspaceID(){
        if(empty($this->config['substriction_domain'])){
            throw new exceptions\DNSSubmoduleException('Invalid substriction domain in configuration', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS);
        }
        
        $packet = '<packet>
                <webspace>
                <get>
                   <filter>
                      <name>'.$this->config['substriction_domain'].'</name>
                   </filter>
                   <dataset>
                      <hosting/>
                   </dataset>
                </get>
                </webspace>
                </packet>';
        
        $return = $this->get($packet);
        if(isset($return->webspace->get->result->id)){
            return (string)$return->webspace->get->result->id;
        }else{
            if($return->webspace->get->result->errcode == 1013){
                throw new exceptions\DNSSubmoduleException('Substriction domain does not exist', dns\SubmoduleExceptionCodes::COMMAND_ERROR);
            }
            
            $this->throwError($return->webspace->get->result);
        }
    }
    
    private function throwError($result) {
        if($result->status == 'error') {
            throw new exceptions\DNSSubmoduleException((string)$result->errtext, dns\SubmoduleExceptionCodes::COMMAND_ERROR);
        } else if (strpos($result, 'Please') !== false) {
            throw new exceptions\DNSSubmoduleException($result, dns\SubmoduleExceptionCodes::COMMAND_ERROR);
        } else {
            throw new exceptions\DNSSubmoduleException('Unexpected error', dns\SubmoduleExceptionCodes::COMMAND_ERROR);
        }
    }
    
    private function getDomainInfo(){
        $packet = '<packet>
                        <site>
                          <get>
                            <filter>
                               <name>'.$this->domain.'</name>
                            </filter>
                            <dataset>
                              <hosting/>
                            </dataset>
                          </get>
                        </site>
                      </packet>';
        
        $return = $this->get($packet);
        if(isset($return->site->get->result->result->id)){
            return (int)$return->site->get->result->result->id;
        }elseif($return->site->get->result->status == 'ok'){
            return (int)$return->site->get->result->id;
        }else{
            $this->throwError($return->site->get->result);
        }
    }
    
    public function getRecords($recordType = false) {
        $domain_id = $this->getDomainInfo();
        if($domain_id === false || $domain_id <= 0)
            return false;
        
        $packet = '<packet>
                        <dns>
                            <get_rec>
                                <filter>
                                    <site-id>'.$domain_id.'</site-id>
                                </filter>
                             </get_rec>
                        </dns>
                    </packet> ';
        
        $return = $this->get($packet); 
        
        $out = array();
        if((string)$return->dns->get_rec->result->status == 'ok'){
            foreach($return->dns->get_rec->result as $record_data){
                $type = strtoupper((string)$record_data->data->type);
                
                if(in_array($type, $recordType!==false ? array(strtoupper($recordType)) : $this->getAvailableRecordTypes())) {
                    $record = new dns\record\Record();
                    $record->line = (string)$record_data->id;
                    $record->name = (string)$record_data->data->host;
                    $record->type = $type;
                    $record->createRDATAObject($type);
                    
                    switch($type) {                            
                        case 'MX':
                            $record->rdata->preference = (string)$record_data->data->opt;
                            $record->rdata->exchange = (string)$record_data->data->value;
                            break;
                        case 'SRV':
                            $record->rdata->fromString((string)$record_data->data->opt . ' ' . (string)$record_data->data->value);
                            break;
                        case 'PTR':
                            if($record_data->data->opt)
                                $record->name .= "/" . $record_data->data->opt;
                            $record->rdata->setFirstProperty((string)$record_data->data->value);
                            break;
                        default:
                            $record->rdata->setFirstProperty((string)$record_data->data->value);
                            break;
                    }
                    
                    $out[] = $record;
                }
            }
        } else {
            $this->throwError($return->dns->get_rec->result);
        }
        return $out;
    }

    public function addRecord(dns\record\Record $record) { 
        $domain_id = $this->getDomainInfo();
        if($domain_id === false || $domain_id <= 0)
            return false;
        
        switch($record->type) {                            
            case 'MX':
                $value = $record->rdata->exchange;
                $opt = $record->rdata->preference;
                break;
            case 'SRV':
                $value = $record->rdata->target;
                $opt = str_replace("\t", ' ', $record->rdata->toString(['priority','weight','port']));
                break;
            case 'TXT':
               $value = trim($record->rdata->toString(), '"');
                $opt = '';
                break;
            default:
                $value = $record->rdata->toString();
                $opt = '';
                break;
        }

        if($record->type === 'PTR') {
            $host = str_replace('@', '', $record->nameToRelative($this->domain));
            $opt = explode('/',$host)[1];
            if(!($opt == '8' || $opt =='16' || $opt =='24'))
                $this->throwError('Please specify the subnet mask for the IP. Right now Plesk supports only the 8, 16 and 24 masks.');

            $host = explode('/',$host)[0];
            $packet = '<packet>
                        <dns>
                            <add_rec>
                                <site-id>' . $domain_id . '</site-id>
                                <type>' . $record->type . '</type>
                                <host>' . $host . '</host>
                                <value>' . $value . '</value> 
                                ' . (!empty($opt) ? "<opt>$opt</opt>" : '') . '
                            </add_rec>
                        </dns>
                    </packet>';
        }
        else
        {
            $packet = '<packet>
                        <dns>
                            <add_rec>
                                <site-id>' . $domain_id . '</site-id>
                                <type>' . $record->type . '</type>
                                <host>' . str_replace('@', '', $record->nameToRelative($this->domain)) . '</host>
                                <value>' . $value . '</value> 
                                ' . (!empty($opt) ? "<opt>$opt</opt>" : '') . '
                            </add_rec>
                        </dns>
                    </packet>';
        }

        $return = $this->get($packet);

        if((string)$return->dns->add_rec->result->status === 'ok'){
            return true;
        }

        $this->throwError($return->dns->add_rec->result);
    }

    public function editRecord(dns\record\Record $record) {
        $this->deleteRecord($record);
        $record->nameToAbsolute($this->domain);
        $this->addRecord($record);
    }

    public function deleteRecord(dns\record\Record $record) {
        $packet = '<packet>
                    <dns>
                        <del_rec>
                            <filter>
                                <id>'.$record->line.'</id>
                            </filter>
                        </del_rec>
                    </dns>
                </packet>';
            
        $return = $this->get($packet);
            
        if((string)$return->dns->del_rec->result->status == 'ok'){
            return true;
        } 
            
        $this->throwError($return->dns->del_rec->result);        
    }

    public function zoneExists() {
        try {
            $domain_id = $this->getDomainInfo();
        } catch (exceptions\DNSSubmoduleException $e) {
            if($e->getCode() == dns\SubmoduleExceptionCodes::COMMAND_ERROR) {
                return false;
            }
            throw $e;
        }
        return $domain_id !== false && $domain_id > 0;
    }
    
    public function activateZone() { 
        if(empty($this->webspace_id)){
            $this->webspace_id = $this->getWebspaceID();
        }
        
        if(empty($this->webspace_id)){
            return false; //TODO: error?
        }
         
        $packet = '<packet>
                        <site>
                            <add>
                                <gen_setup>
                                    <name>'.$this->domain.'</name>
                                    <webspace-id>'.$this->webspace_id.'</webspace-id>
                                </gen_setup>
                            </add>
                        </site>
                    </packet>';
        
        $return = $this->get($packet);
        
        if((string)$return->site->add->result->status == 'ok'){
            return true;
        }
        
        $this->throwError($return->site->add->result);
    }

    public function terminateZone() {
        if($this->domain == $this->config['substriction_domain']){
            throw new exceptions\DNSSubmoduleException('You cannot delete subscription domain', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS);
        }
        
        if(empty($this->webspace_id)){
            $this->webspace_id = $this->getWebspaceID();
        }
        
        if(empty($this->webspace_id)){
            return false;
        }
        
        $packet = '<packet>
                        <site>
                            <del>
                                <filter>
                                    <name>'.$this->domain.'</name>
                                </filter>
                            </del>
                        </site>
                    </packet>';
        
        $return = $this->get($packet);
        
        if((string)$return->site->del->result->status == 'ok'){
            return true;
        }
            
        $this->throwError($return->site->del->result);
    }
 
    public function getZones() {
        $packet = '<packet>
                        <webspace>
                            <get>
                                <filter/>
                                <dataset>
                                    <gen_info/>    
                                </dataset>
                            </get>
                        </webspace>
                    </packet>';
        $return = $this->get($packet);
        
        $out = array();
        if((string)$return->webspace->get->result->status == 'ok'){
            foreach($return->webspace->get->result as $info) {
                $out[(string)$info->data->gen_info->name] = (string)$info->data->gen_info->dns_ip_address;
            }
        }
        return $out;
    }
    
}
