<?php
require_once "config.php";
require_once SITE_LOGICS."deposit/ext/ESnappedAPI.php";

use Firebase\JWT\JWT;

class APNWebHook extends System{

    private $data;
    
    /**
     * 
     * @var ESnappedAPI
     */
    private $apnIntegration;
    
    public function __construct(){
        parent::__construct();
 
        $this -> initComponents();
        $this -> setParameters();
        $this -> validation();
    }   
    
    private function initComponents(){
        header("Content-type: text/json");
        $this -> apnIntegration = new ESnappedAPI();
        
        $logDirs = [
            "Logs/APN/", "Logs/APN/Error"
        ];
                
        foreach($logDirs as $key => $dir){
            if(!@file_exists($dir)){
                if(mkdir($dir))
                    echo "Created - ({$key})\n";
            }
        }
    }
    
    private function setParameters(){
        $this -> data = file_get_contents("php://input");
    }
    
    private function validation(){
        $conditions = [
            "E01" => [
                "bool" => $_SERVER["REQUEST_METHOD"] != 'POST',
                "desc" => "Invalid Request Method: {$_SERVER['REQUEST_METHOD']}"
            ],
            "E02" => [
                "bool" => !$this -> apnIntegration -> isAuthenticated(),
                "desc" => "Webhook Bad State"
            ]
        ];
        
        foreach($conditions as $code => $cond){
            if($cond["bool"]){
                $fileName = "Log_".date("Y-m-d").".log";
                
                $message = json_encode([
                    "code" => $code,
                    "message" => $cond["desc"]
                ]);
                
                error_log("{$message}\n", 3, "Logs/APN/Error/{$fileName}");
                die($message);
            }
        }
    }
    
    private function updateTempCCDepositTransaction($tempDepositData){
        $this -> accessModify("UPDATE_TEMP_CCDEPOSIT", [
            "('){$tempDepositData["invoice_num"]}(')",
            $tempDepositData["credited"]
        ], false);
    }
    
    private function creditTransaction($tempDepositData){
        $comment = "[APN Reference No.]\\n{$tempDepositData["invoice_num"]}";
                  
        $method = VAL_STR_CCDEPOSIT_METHOD;
        
        $this -> accessModify("INSERT_WB_DEPOSIT_TRANSACTION", [
            $tempDepositData[COLUMN_TRANSACTION_NUMBER],
            $tempDepositData[COLUMN_USER_ACCOUNT],
            $tempDepositData[COLUMN_AMOUNT],
            $tempDepositData[COLUMN_CURRENCY],
            $method,
            "Credit Card Deposit",
            $tempDepositData[COLUMN_DEPOSIT_DATE],
            $tempDepositData[COLUMN_FEE],
            $comment,
            $tempDepositData[COLUMN_CREATE_TIME],
            NO_COUNT,
            NO_COUNT,
            $tempDepositData[COLUMN_MESSAGE],
        ], false);
    }
    
    private function sendTransactionCompleteNotice($txn){
        $member = $this -> getRowData($this -> getAccountCommon($txn[COLUMN_USER_ACCOUNT]));
        
        $params = [
            $member[COLUMN_USER_ACCOUNT],
            $this -> getNameCommon($member[COLUMN_USER_ACCOUNT]),
            $txn[COLUMN_TRANSACTION_NUMBER],
            date("Y-m-d H:i:s"),
            $txn["currency"],
            $this -> intToCurrency($txn[COLUMN_AMOUNT] + $txn[COLUMN_FEE], $txn[COLUMN_CURRENCY]),
            $txn["currency"],
            $this -> intToCurrency($txn[COLUMN_AMOUNT], $txn[COLUMN_CURRENCY])
        ];
        
        $this -> sendMailByTmp('deposit_ccd_complete.xml'
            , $params
            , $this -> getColumnData($member, COLUMN_MAIL)
            , VAR_CS_MAIL_ADDRESS);
    }
    
    private function sendTransactionFailedNotice($txn){
        $member = $this -> getRowData($this -> getAccountCommon($txn[COLUMN_USER_ACCOUNT]));
        
        $params = [
            $member[COLUMN_USER_ACCOUNT],
            $this -> getNameCommon($member[COLUMN_USER_ACCOUNT]),
            $txn[COLUMN_TRANSACTION_NUMBER],
            date("Y-m-d H:i:s"),
            $txn["currency"],
            $this -> intToCurrency($txn[COLUMN_AMOUNT] + $txn[COLUMN_FEE], $txn[COLUMN_CURRENCY]),
            $txn["currency"],
            $this -> intToCurrency($txn[COLUMN_AMOUNT], $txn[COLUMN_CURRENCY])
        ];
        
        $this -> sendMailByTmp('deposit_ccd_fail.xml'
            , $params
            , $this -> getColumnData($member, COLUMN_MAIL)
            , VAR_CS_MAIL_ADDRESS);
    }
    
    private function isJSON($string){
        return is_string($string) 
            && is_array(json_decode($string, true)) 
            && (json_last_error() == JSON_ERROR_NONE) ? true : false;
    }
    
    private function decryptESnappedJWTString($payload){
        try{
            return JWT::decode($payload, "u4YP9kfWQe", ["HS256"]);;
        }catch(Exception $e){
            return false;
        }
    }
    
    private function getDecryptedPayloadObject(){        
        if(!empty($this -> data) && $this -> isJSON($this -> data)){
            $plstr = json_decode($this -> data);
            
            if(isset($plstr -> payload)){
                $obj = $this -> decryptESnappedJWTString($plstr -> payload);
                
                return $obj ? $obj : false;
            }
        }else
            return false;
    }
    
    public function receive(){
        $decpload = $this -> getDecryptedPayloadObject();
        
        $msgtime = date("Y-m-d H:i:s");
        $fileName = "Log_".date("Y-m-d").".log";

        if($decpload){
            $tempDepositData = $this -> apnIntegration -> getTempDepositDataBy('invoice_num', $decpload -> reference);
           
            if($tempDepositData){
                if($tempDepositData["credited"] == 0){
                    $tempDepositData["deposit_date"] = date("Y-m-d H:i:s");
                    $tempDepositData["remoteIpAddr"] = $_SERVER["REMOTE_ADDR"];
                    
                    switch($decpload -> status){
                        case "PAID":{
                            $tempDepositData["credited"] = 1;                          
                            $this -> creditTransaction($tempDepositData);
                            $this -> updateTempCCDepositTransaction($tempDepositData);
                            $this -> sendTransactionCompleteNotice($tempDepositData);
                            break;
                        }
                        
                        case "FAILED":{
                            $tempDepositData["credited"] = 2;
                            $this -> updateTempCCDepositTransaction($tempDepositData);
                            $this -> sendTransactionFailedNotice($tempDepositData);
                            break;
                        }
                    } 
                    
                    $tempDepositData["amount"]  = $this -> intToCurrency($tempDepositData["amount"], $tempDepositData["currency"]);
                    $tempDepositData["fee"]     = $this -> intToCurrency($tempDepositData["fee"], $tempDepositData["currency"]);
                    
                    $message = print_r($tempDepositData, true);   
                    error_log("[$msgtime]\n{$message}\n\n", 3, "Logs/APN/{$fileName}");
                }else{
                    switch($tempDepositData["credited"]){
                        case 1:
                            error_log("[$msgtime]\nInvoice Already Credited: {$decpload -> reference}\n\n", 3, "Logs/APN/{$fileName}");
                            break;
                        case 2:
                            error_log("[$msgtime]\nFailed Invoice: {$decpload -> reference}\n\n", 3, "Logs/APN/{$fileName}");
                            break;
                    }
                }
            }else
                error_log("[$msgtime]\nReference num doesn't exist: {$decpload -> reference}\n\n", 3, "Logs/APN/{$fileName}");
        }else{
            $remoteIp = $_SERVER["REMOTE_ADDR"];
            error_log("[$msgtime]\nDenied Input[{$remoteIp}]: {$this -> data}\n\n", 3, "Logs/APN/{$fileName}");
        }
    }
}

$hooks = new APNWebHook();
$hooks -> receive();