<?php
use Firebase\JWT\JWT;

class EasyPaymentLinkAPI extends System {
    
    private static $instance;
    
    /**
     * 
     * @var string
     */
    private $payload;
    
    /**
     * 
     * @var object
     */
    private $eplconf;
    
    /**
     * 
     * @var object
     */
    private $midconf;
    
    /**
     * 
     * @var int
     */
    private $httpcode;
    
    /**
     * 
     * @var string
     */
    private $formlink;
    
    /**
     * 
     * @var string
     */
    private $processError   = NO_STRING;
   
    /**
     * 
     * @var string
     */
    private $language       = "en";
    
    /**
     * 
     * @var string
     */
    private $userAccount;
    
    /**
     * 
     * @var double
     */
    private $amount;
    
    /**
     * 
     * @var double
     */
    private $fee;
    
    /**
     * 
     * @var currency
     */
    private $currency;
    
    public function __construct(){
        parent::__construct();
        $this -> setParameter();
    }
    
    public static function getInstance(){
        if(!isset(self::$instance))
            self::$instance = new EasyPaymentLinkAPI();
        
        return self::$instance;
    }
    
    private function setParameter(){
        $this -> eplconf = $this -> getSettingConfiguration("easy_payment_link");
        $this -> midconf = $this -> getSettingConfiguration("middleware_conf");
    }
    
    private function validation(){
        $invalid = [];
        
        if($this -> eplconf){
            $currencies = explode(",", $this -> eplconf -> currencies);
            
            if(!in_array($this -> currency, $currencies))
                $invalid[] = "Invalid Currency *{$this -> currency}*";
        }else
            $invalid[] = "EasyPaymentLink settings could be missing or disabled.";
                
        if($this -> isLoopData($invalid))
            throw new Exception(print_r($invalid, true));
    }
    
    private function logDetails($content){
        $timestamp = date("Y-m-d H:i:s");
        
        $logDir = SITE_ROOT."api/Logs/APN";
        
        if(!@file_exists($logDir))
            mkdir($logDir, 0777, true);
        
        $logFile = "{$logDir}/Log_EZ".date("Y-m-d").".log";        
        error_log("[{$timestamp}]\n\n{$content}\n\n", VAL_INT_3, $logFile);
    }
    
    public function setUserAccount($userAccount){
        $this -> userAccount = $userAccount;
        return $this;   
    }
    
    public function setAmount($amount){
        $this -> amount = $amount;
        return $this;
    }
    
    public function setCurrency($currency){
        $this -> currency = $currency;
        return $this;
    }
    
    public function setFee($fee){
        $this -> fee = $fee;
        return $this;
    }
    
    public function setLanguage($language){
        $this -> language = $language;
        return $this;
    }
    
    public function generatePayload(){
        $this -> validation();
        
        $account = $this -> getRowData($this -> getAccountCommon($this -> userAccount));
        $txnsnum = $this -> getTransactionNumberCommon(VAR_TRANSACTION_DEPOSIT);
        
        $tokenPayload = [
            "purpose" => "EasyPaymentLink",
            "created" => date("Y-m-d H:i:s"),
            "data" => base64_encode(json_encode([
                "user_account" => $this -> getColumnData($account, PARAM_USER_ACCOUNT),
                "transaction_number" => $txnsnum,
                "currency" => $this -> currency,
                "amount" => $this -> currencyToInt($this -> amount, $this -> currency),
                "language" => $this -> language,
                "fee" => $this -> currencyToInt($this -> fee, $this -> currency)
            ]))
        ];
        
        $midtoken = JWT::encode($tokenPayload, 
                $this -> midconf -> token_secretkey,
                $this -> midconf -> algorithm);
        
        $callbck = SITE_PROTOCOL."://".SITE_DOMAIN_FULL."/{$this -> language}/middleware?token={$midtoken}";
        
        $this -> logDetails("{$this -> getColumnData($account, PARAM_USER_ACCOUNT)}\n{$callbck}");
        
        $telnum = preg_replace("/(\-| |\(|\))/", NO_STRING, $this -> getColumnData($account, PARAM_TEL));
        
        $country_code = $this -> getColumnData($account, PARAM_COUNTRY);
        $mobile_num = $this -> omittedPhoneNumber($telnum, $country_code);
        
        $this -> payload = json_encode([
            "customer" => [
                "first_name" => $this -> getColumnData($account, PARAM_FIRST_NAME),
                "last_name" => $this -> getColumnData($account, PARAM_LAST_NAME),
                "billing_address" => [
                    "line1" => $this -> getColumnData($account, PARAM_ADDRESS),
                    "line2" => "",
                    "city_municipality" => $this -> getColumnData($account, PARAM_CITY),
                    "zip" => $this -> getColumnData($account, PARAM_ZIP_CODE),
                    "state_province_region" => $this -> getColumnData($account, PARAM_STATE),
                    "country_code" => $country_code
                ],
                "contact" => [
                    "email" => $this -> getColumnData($account, PARAM_MAIL),
                    "mobile" => $mobile_num
                ]
            ],
            "payment" => [
                "description" => " ",
                "amount" => $this -> amount,
                "currency" => $this -> currency,
                "merchant_reference_id" => $txnsnum
            ],
            "route" => [
                "callback_url" => $callbck,
                "notify_user" => true
            ],
            "key" => $this -> eplconf -> link_key
        ]);
        
        return $this;
    }
    
    public function process(){
        $handle = curl_init();
        
        curl_setopt_array($handle, [
            CURLOPT_URL => $this -> eplconf -> payment_link,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_ENCODING => "utf8",
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => "POST",
            CURLOPT_POSTFIELDS => $this -> payload,
            CURLOPT_HTTPHEADER => [
                "Content-type: application/json"
            ]
        ]);
        
        $output = curl_exec($handle);
        
        $this -> httpcode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
        
        if($err = curl_error($handle)){
            $this -> processError = $err;
        }else{
            if($this -> httpcode == "200"){
                if($this -> checkJSONString($output)){
                    $output = json_decode($output);
                    $this -> formlink = $output -> link;
                }
            }else
                $this -> processError = $output;
        }
        
        curl_close($handle);
    }
    
    protected function omittedPhoneNumber($tel, $country){
        $country = $this -> getRowData($this -> accessSelect("SELECT_MST_COUNTRY", [$country]));
        
        // omit the + sign if included
        $phoneNum = strpos($tel, "+") === 0 ? substr($tel, 1) : $tel;
        
        $found = "";
        
        if($this -> beginsWith($country["phone_code"], $phoneNum, $found)){
            return "0".substr($phoneNum, strlen($found));
        }
        
        return $tel;
    }
    
    protected function beginsWith($beginsWith, $phrase, &$found = null){
        if(is_array($beginsWith)){
            foreach($beginsWith as $begin){
                if(substr($phrase, 0, strlen($begin)) === $begin){
                    if($found !== null){
                        $found = $beginsWith;
                    }
                    
                    return true;
                }
            }
        }else if(is_string($beginsWith)){
            if(strpos($beginsWith, ",") !== false){
                $beginsWith = explode(",", $beginsWith);
                return $this -> beginsWith($beginsWith, $phrase, $found);
            }
            
            if(substr($phrase, 0, strlen($beginsWith)) === $beginsWith){
                if($found !== null){
                    $found = $beginsWith;
                }
                
                return true;
            }
        }
    }
    
    public function getHttpCode(){
        return $this -> httpcode;
    }
    
    public function redirectToForm(){
        if(isset($this -> formlink))
            header("Location: {$this -> formlink}");
    }
}