<?php

###############################################################################
# DomainsCoZA.php
#
# @author Anil Kumar <akumar@codepunch.com>
# @link   https://codepunch.com
#
############################################################################### 

namespace CodePunch\LU\Registrars;

use Exception;
use CodePunch\Base\Util as UTIL;

###############################################################################

class DomainsCoZa extends RegistrarAPI {
	
	private $api_url_base  		= null;
	private $api_access_key		= null;
	private $api_secret_key		= null;
	
	###########################################################################
	
	// All the public functions below are required to be present in any
	// generic	plugin.
	
	###########################################################################
	
	public function __construct($cm, $apikey, $apisecret)
	{ 
		parent::__construct($cm);
		// Users can test with a sandbox if it is supported.
		// Look for apikey with a 'sandbox::' at the start.
		if(UTIL::starts_with($apikey, "sandbox::")) {
			$apikey = substr($apikey, 9);
			$this->use_sandbox(true);
		}
		else
			$this->use_sandbox(false);
		$this->api_access_key = $apikey;
		$this->api_secret_key = $apisecret;
	}
	
	###########################################################################
	
	public function use_sandbox($flag)
	{
		if($flag === true)	// Use sandbox URL
			$this->api_url_base = 'https://lapi-dev.domains.co.za/api/';
		else
			$this->api_url_base = 'https://api.domains.co.za/api/';
		return true;
	}
	
	###########################################################################
	// Provide the keys required for setup. In this case, we just need a 
	// username nad password. Users will see these as input names.
	
	public static function getKeyNames()
	{
		return array('API Username', 'API Password');
	}
	
	###########################################################################
	// Only domainlist and whois is supported for now. 
	// We could expand this to support ...
	//
	// 'domainlist', 'whois', 'availcheck', 'renew', 'pricing', 'balance', 
	// 'getcreatedata', 'register', 'getns', 'setns'
	
	public function supported() {
		return array('domainlist', 'whois');
	}
	
	###########################################################################
	// Test if the API settings are correct.

	public function isWorking() {
		$url = $this->construct_url("login");
		$postdata = [
			'username' => $this->api_access_key,
			'password' => $this->api_secret_key
		];
		$results = UTIL::curl_post_url($url, 10, false, false, $postdata);
		if($results['status'] == 200) {
			$result = json_decode($results['result'], true);
			if($result['intReturnCode'] == 1)
				return true;
			else {
				return $result['strMessage'];
			}
		}
		return "Unable to connect";
	}
	
	###########################################################################
	// Get the list of domains in account.
	// The callback can be used to send back the progress while in a loop.
	// callback(str_current_domain, int_total_domains_added_so_far, arr_new_set_of_domains);
	
	public function domainlist(?callable $callback=null) {
		$domains = array();
		$token = $this->getToken();
		$headers = [
			'Authorization: Bearer ' . $token
		];
		$pollcount = 50;
		$url = $this->construct_url('domain/list');
		$results = UTIL::curl_get_url($url, 10, false, $headers, "Watch My Domains SED/5.0");
		if($results['status'] == 200) {
			$result = json_decode($results['result'], true);
			if($result['intReturnCode'] == 1) {
				$thisdata = [];
				$domaincount = $result['intTotal'];
				foreach($result['arrDomains'] as $di) {
					$domain = $di['strDomainName'];
					$thisdata[] = $domain;
					if(count($thisdata) >= $pollcount) {
						if($callback != null && count($thisdata)) {
							$status = call_user_func($callback, "", count($domains), $thisdata);
							if($status == self::REGAPI_STOP)
								break;
						}
						if(count($thisdata))
							$domains = array_merge($domains, $thisdata);
						$thisdata = [];
					}
				}
				if(count($thisdata)) {
					call_user_func($callback, "", count($domains), $thisdata);
					$domains = array_merge($domains, $thisdata);
				}
			}
			else {
				throw new Exception($result['strMessage']);
			}
		}
		else 
			throw new Exception("Unable to connect to Domains.Co.Za API server");
		
		return $domains;
	}
	
	###########################################################################
	// Get the whois for the specified domain.
	
	public function whois($domain) {
		// To return the status 
		$responce = array('status'=>false,'error'=>'','data'=>'');
		
		$token = $this->getToken();
		$headers = [
			'Authorization: Bearer ' . $token
		];
		$url = $this->construct_url('domain');
		
		$db = $this->getConnectionManager()->getAuthentication()->getDatabase();
		$parts = UTIL::clean_domain_name($domain, $db->getDomainSuffixList());

		$tld = $parts['tld'];
		$pos = strrpos($domain, ".".$tld);
		if($pos !== false)
			$sld = substr($domain, 0, $pos);
		else
			throw new Exception("Unable to parse domain $domain");
		$url .= "/?sld=$sld&tld=$tld";

		$results = UTIL::curl_get_url($url, 10, false, $headers, "Watch My Domains SED/5.0");
		if($results['status'] == 200) {
			$result = json_decode($results['result'], true);
			if($result['intReturnCode'] == 1) {
				$info = UTIL::array_flatten($result);
				$this->formatDates($info);
				$this->cleanup_keys($info);
				$whois = UTIL::array_to_text($info);
				$responce['data'] = $whois;
				$responce['status'] = true;
				UTIL::debug_cli_print($whois);
			}
			else {
				throw new Exception($result['strMessage']);
			}
		}
		else 
			throw new Exception("Unable to connect to Domains.Co.Za API server");
		
		return $responce;
	}
	
	###########################################################################
	
	// All the public functions above are required to be present in any
	// generic	plugin.
	
	###########################################################################
	
	private function formatDates(&$info) {
		$tokens = array(
			'intExDate' => 'Registrar Registration Expiration Date',
			'intCrDate' => 'Created Date',
			'intUpDate' => 'Updated Date',
			'intSuspendDate' => 'Suspension Date',
			'intRedemptionDate' => 'Redemption Date',
			'intNSUpDate' => 'Name Servers Last Changed On',
			'intDeleteDate' => 'Deletion Date',
			'intLastRenewedDate' => 'Last Renewed Date'
			);
			
		foreach($tokens as $key => $value) {
			if(isset($info[$key])) {
				// Use a date format that is never ambiguous 
				// Example: 2022-Dec-25
				$info[$value] = date("Y-M-d", $info[$key]);
				unset($info[$key]);
			}
		}
	}
	
	###########################################################################
	
	private function getToken() {
		$token = null;
		$url = $this->construct_url("login");
		$postdata = [
			'username' => $this->api_access_key,
			'password' => $this->api_secret_key
		];
		$results = UTIL::curl_post_url($url, 10, false, false, $postdata);
		if($results['status'] == 200) {
			$result = json_decode($results['result'], true);
			if($result['intReturnCode'] == 1)
				$token = $result['token'];
			else {
				throw new Exception($result['strMessage']);
			}
		}
		else 
			throw new Exception("Unable to connect to Domains.Co.Za API server");
		return $token;
	}
	
	###########################################################################
	
	private function construct_url($endpoint) {
		return $this->api_url_base . $endpoint;
	}
	
	###########################################################################
	// Quick and dirty translation for the keys after flattening the nested
	// array data from the API.
	
	private function cleanup_keys(&$info) {
		$xlate = array(
		
		'arrRegistrantstrContactID' => 'Registrant ID',
		'arrRegistrantstrContactName' => 'Registrant Name',
		'arrRegistrantstrContactCompany' => 'Registrant Organization',
		'arrRegistrantstrContactEmail' => 'Registrant Email',
		'arrRegistrantstrContactNumber' => 'Registrant Phone',
		'arrRegistrantstrContactFax' => 'Registrant Fax',
		'arrRegistrantstrContactAddress0' => 'Registrant Address1',
		'arrRegistrantstrContactAddress1' => 'Registrant Address2',
		'arrRegistrantstrContactAddress2' => 'Registrant Address3',
		'arrRegistrantstrContactCity' => 'Registrant City',
		'arrRegistrantstrContactProvince' => 'Registrant State',
		'arrRegistrantstrContactPostalCode' => 'Registrant ZIP',
		'arrRegistrantstrContactCountry' => 'Registrant Country',
		'arrRegistrantstrContactType' => 'Registrant Contact Type',
		
		'arrBillingstrContactID' => 'Billing Contact ID',
		'arrBillingstrContactName' => 'Billing Contact Name',
		'arrBillingstrContactCompany' => 'Billing Organization',
		'arrBillingstrContactEmail' => 'Billing Email',
		'arrBillingstrContactNumber' => 'Billing Phone',
		'arrBillingstrContactFax' => 'Billing Fax',
		'arrBillingstrContactAddress0' => 'Billing Address1',
		'arrBillingstrContactAddress1' => 'Billing Address2',
		'arrBillingstrContactAddress2' => 'Billing Address3',
		'arrBillingstrContactCity' => 'Billing City',
		'arrBillingstrContactProvince' => 'Billing State',
		'arrBillingstrContactPostalCode' => 'Billing ZIP',
		'arrBillingstrContactCountry' => 'Billing Country',
		'arrBillingstrContactType' => 'Billing Contact Type',
		
		'arrAdminstrContactID' => 'Admin Contact ID',
		'arrAdminstrContactName' => 'Admin Contact Name',
		'arrAdminstrContactCompany' => 'Admin Organization',
		'arrAdminstrContactEmail' => 'Admin Email',
		'arrAdminstrContactNumber' => 'Admin Phone',
		'arrAdminstrContactFax' => 'Admin Fax',
		'arrAdminstrContactAddress0' => 'Admin Address1',
		'arrAdminstrContactAddress1' => 'Admin Address2',
		'arrAdminstrContactAddress2' => 'Admin Address3',
		'arrAdminstrContactCity' => 'Admin City',
		'arrAdminstrContactProvince' => 'Admin State',
		'arrAdminstrContactPostalCode' => 'Admin ZIP',
		'arrAdminstrContactCountry' => 'Admin Country',
		'arrAdminstrContactType' => 'Admin Contact Type',
		
		'arrTechstrContactID' => 'Tech Contact ID',
		'arrTechstrContactName' => 'Tech Contact Name',
		'arrTechstrContactCompany' => 'Tech Organization',
		'arrTechstrContactEmail' => 'Tech Email',
		'arrTechstrContactNumber' => 'Tech Phone',
		'arrTechstrContactFax' => 'Tech Fax',
		'arrTechstrContactAddress0' => 'Tech Address1',
		'arrTechstrContactAddress1' => 'Tech Address2',
		'arrTechstrContactAddress2' => 'Tech Address3',
		'arrTechstrContactCity' => 'Tech City',	
		'arrTechstrContactProvince' => 'Tech State',
		'arrTechstrContactPostalCode' => 'Tech ZIP',
		'arrTechstrContactCountry' => 'Tech Country',
		'arrTechstrContactType' => 'Tech Contact Type',
		
		'arrRegistrantstrStatus' => 'Registrant EPP Status',
		'arrRegistrantstrVerificationStatus' => 'Registrant Verification Status',
		'arrAdminstrStatus' => 'Admin EPP Status',
		'arrTechstrStatus' => 'Tech EPP Status',
		'arrBillingstrStatus' => 'Billing EPP Status',
		'strNameserverType' => 'Name Server Type',
		'externalRef' => 'Customized External Reference',
		
		'strDomainName' => 'Domain',
		'strStatus' => 'Status', 
		'strEppStatus' => 'EPP Status',
		'bPrivacy' => 'Privacy',
		'bDNSSec' => 'DNSSec',
		'strDns' => 'Premium DNS',
		'autorenew' => 'Auto Renew',
		'bSupportsRestore' => 'Supports Restore',
		
		'strApiHost' => 'API Host',
		'strMessage' => 'API Call Status',
		'intReturnCode' => 'API Call Return Code',
		'strUUID' => 'API Call Identifier',
		
		'objResellerusername' => 'Reseller Username',
		'objResellerlowBalance' => 'Reseller Low Balance',
		'objResellerbalance' => 'Reseller Balance',
		'objReselleraccountType' => 'Reseller Account Type',
		
		
		'arrNameservers0' => 'Name Server 0',
		'arrNameservers1' => 'Name Server 1',
		'arrNameservers2' => 'Name Server 2',
		'arrNameservers3' => 'Name Server 3',
		'arrNameservers4' => 'Name Server 4',
		'arrNameservers5' => 'Name Server 5',
		'arrNameservers6' => 'Name Server 6',
		'arrNameservers7' => 'Name Server 7',
		'arrNameservers8' => 'Name Server 8',
		);
		
		UTIL::array_xlate_keys($info, $xlate);
	}
}
