<?php
include_once "../../system/lib/config.php";

class Flat3Webhook extends System {
    
    /**
     * 
     * @var String
     */
    private $ipAddress;
    
    /**
     * 
     * @var String
     */
    private $requestData;
    
    /**
     * 
     * @var String
     */
    private $depositData;
    
    /**
     * 
     * @var String
     */
    private $accessLog;
    
    /**
     * 
     * @var String
     */
    private $transLog;
    
    /**
     * 
     * @var String
     */
    private $errorLog;
    
    /**
     * 
     * @var String
     */
    private $webhookName;
    
    /**
     * 
     * @var object
     */
    private $devSetting             = NULL;
    
    /**
     * 
     * @var boolean
     */
    private $devSettingEnabled     = false;
    
    public function __construct($webhookName = NO_STRING){
        parent::__construct();
        
        $this -> webhookName = $webhookName;
        
        $this -> setParameter();
        $this -> requestDetails();
        $this -> validation();
    }
    
    private function setParameter(){    
        $this -> ipAddress      = $this -> getColumnData($_SERVER, "REMOTE_ADDR");
        $this -> requestData    = $this -> getDataGet("data");
        
        $this -> accessLog      = "../Logs/SAPAY/Access";
        $this -> transLog       = "../Logs/SAPAY";
        $this -> errorLog       = "../Logs/SAPAY/Error";
        
        $this -> devSetting = $this -> getSettingConfiguration("flat3_webhook");
    }
    
    private function requestDetails(){
        $details = [
            "ipAddress" => $this -> getColumnData($_SERVER, "REMOTE_ADDR"),
            "method" => $this -> getColumnData($_SERVER, "REQUEST_METHOD"),
            "headers" => apache_request_headers(),
            "requestData" => [
                "POST" => http_build_query($_POST),
                "GET" => http_build_query($_GET)
            ]
        ];
        
        if($this -> webhookName != NO_STRING)
            $details["webhookName"] = $this -> webhookName;
        
        $this -> logDetails(print_r($details, true), VAL_INT_1);
    }
    
    private function validation(){
        $validationRemarks = [];
        
        $directories = [
            $this -> accessLog,
            $this -> transLog,
            $this -> errorLog
        ];
        
        foreach($directories as $directory){
            if(!@file_exists($directory))
                mkdir($directory);
        }
        
        switch($this -> checkFlat3HookIpAddress()){
            case NO_COUNT:
            case VAL_INT_2:
                $validationRemarks[] = [
                    "code" => "E01",
                    "message" => "Webhook IP Whitelister is missing"
                ];
                break;
                
            case VAL_INT_3:
                $validationRemarks[] = [
                    "code" => "E02",
                    "message" => "Invalid IP Address:  [{$this -> ipAddress}]"
                ];
                break;
        }
        
        switch($this -> checkWebhookNameAvailability()){
            case NO_COUNT:
            case VAL_INT_2:
                $validationRemarks[] = [
                    "code" => "E06",
                    "message" => "Webhook Availability checker not found"
                ];
                break;
            case VAL_INT_3:
                $validationRemarks[] = [
                    "code" => "E07",
                    "message" => "Webhook ({$this -> webhookName}) is blocklisted."
                ];
                break;
            case VAL_INT_4:
                $validationRemarks[] = [
                    "code" => "E08",
                    "message" => "No specified webhook name."
                ];
                break;
        }
        
        if($this -> requestData != NO_STRING){
            $this -> gatherPotentialDepositData();
            $this -> verifyTransactionExistence();
            
            $takens = array_column($this -> depositData, "taken");
            if($this -> isLoopData($takens)){
                $takens = json_encode($takens);
                
                $validationRemarks[] = [
                    "code" => "E04",
                    "message" => "Transaction crediting cannot be processed due to the occurence of Potential Duplicate Transaction. {$takens}"
                ];
            }
            
            $userIds = array_column($this -> depositData, "userId");
            if($this -> isLoopData($userIds)){                
                $fetchedAccounts = array_column($this -> accessSelect("SELECT_ACCOUNT_NUMS", 
                    [$this -> getWhere("userId", COLUMN_USER_ACCOUNT)]), COLUMN_USER_ACCOUNT);

                $found = [];
                
                foreach($userIds as $userId){
                    if(!in_array($userId, $fetchedAccounts))
                        $found[] = $userId;
                }
                
                if($this -> isLoopData($found)){
                    $found = json_encode($found);
                    
                    $validationRemarks[] = [
                        "code" => "E05",
                        "message" => "Non-existential Account No(s). found: {$found}"
                    ];
                }
            }
        }else{
            $validationRemarks[] = [
                "code" => "E03",
                "message" => "Request Data is Empty"
            ];
        }
        
        if($this -> isLoopData($validationRemarks)){
            $this -> logDetails(print_r($validationRemarks, true), VAL_INT_2);
            die("NG");
        }
    }
    
    private function gatherPotentialDepositData(){
        $this -> depositData = [];
        
        $splitData = explode('__', $this -> requestData);
        
        foreach($splitData as $val){
            $arrExplodeTmp1 = explode('/', $val);
            $arrExplodeTmp2 = explode('@', $arrExplodeTmp1[VAL_INT_1]);
            
            $this -> depositData[] = [
                "userId" => $arrExplodeTmp2[VAL_INT_1],
                "depositAmount" => $arrExplodeTmp2[NO_COUNT],
                "flat3TransId" => $arrExplodeTmp1[NO_COUNT]
            ];
        }
    }
    
    /**
     * 
     * @param unknown $arrayCol
     * @param unknown $tableCol
     * @return string
     */
    private function getWhere($arrayCol, $tableCol){
        $orConds = NO_STRING;
        
        if($this -> isLoopData($this -> depositData)){
            foreach($this -> depositData as $key => $arrVal){
                $needle = $this -> getColumnData($arrVal, $arrayCol);
                
                $operator = ($key === 0 
                    ? "AND" 
                    : "OR");
                        
                $orConds .= "{$operator} {$tableCol} = ('){$needle}(') ";
            }
        }
         
        return $orConds;
    }
    
    private function verifyTransactionExistence(){
        $flat3Trans = $this -> accessSelect("SELECT_FLAT3_DEPOSIT_TRANSNUM", 
            [$this -> getWhere("flat3TransId", "flat3_transaction_id")]);
            
        foreach($flat3Trans as $row){            
            $transaction = $this -> getColumnData($row, "flat3_transaction_id");
                                   
            foreach($this -> depositData as &$arrValue){
                if(strcasecmp($this -> getColumnData($arrValue, "flat3TransId"), $transaction) === 0)
                    $arrValue["taken"] = $this -> getColumnData($arrValue, "flat3TransId");
            }
        }
        
        if($this -> isLoopData($this -> depositData)){
            $readableTrans = print_r($this -> depositData, true);
            $this -> logDetails("Deposit Transaction(s): {$readableTrans}", VAL_INT_1);
        }
    }
    
    private function checkWebhookNameAvailability(){
        if($this -> webhookName == NO_STRING)
            return VAL_INT_4;
            
        if($this -> devSetting != null && is_object($this -> devSetting)){
            /**
             *
             * this condition can bypass the validation if returns false.
             *
             */
            if(!$this -> devSettingEnabled)
                return VAL_INT_1;
            
            if(isset($this -> devSetting -> blocked_webhooks)){
                $blockedWebhookNames = explode(",", $this -> devSetting -> blocked_webhooks);
                
                if(in_array($this -> webhookName, $blockedWebhookNames))
                    return VAL_INT_3;
                
            }else
                return VAL_INT_2;
            
            return VAL_INT_1;
        }
        
        return NO_COUNT;
    }
    
    private function checkFlat3HookIpAddress(){
        if($this -> devSetting != null && is_object($this -> devSetting)){
            /**
             * 
             * this condition can bypass the validation if returns false.
             * 
             */
            if(!$this -> devSettingEnabled)
                return VAL_INT_1;
            
            if(isset($this -> devSetting -> ip_address)){
                $ipAddresses = explode(",", $this -> devSetting -> ip_address);
                    
                if(in_array($this -> ipAddress, $ipAddresses))
                    return VAL_INT_1;
                
            }else
                return VAL_INT_2;
                
            return VAL_INT_3;
        }
        
        return NO_COUNT;
    }
    
    private function logDetails($content, $logType){
        $logDate        = date("Y-m-d");
        $logTimestamp   = date("[Y-m-d H:i:s]");
        
        $logFile = NO_STRING;
        
        switch($logType){
            case VAL_INT_1:
                $logFile = $this -> accessLog;
                break;
            case VAL_INT_2:
                $logFile = $this -> errorLog;
                break;
            case VAL_INT_3:
                $logFile = $this -> transLog;
                break;
        }
        
        error_log(
            "{$logTimestamp}\r\n{$content}\r\n", VAL_INT_3,
            "{$logFile}/Log_{$logDate}.log");
    }
    
    private function sendCompleteMail(Array $deposit){
        $userAccount = $this -> getColumnData($deposit, "userId");
        
        $emailParams = [
            $userAccount,
            $this -> getNameCommon($userAccount),
            $this -> getColumnData($deposit, "transaction_number"),
            $this -> getColumnData($deposit, "date"),
            JPY,
            ltrim($this -> getColumnData($deposit, "depositAmount"), NO_COUNT),
            JPY,
            $this -> getColumnData($deposit, "toProcessAmount")
        ];
        
        $this -> sendMailByTmp('deposit_jpv_complete.xml', 
                $emailParams,
                $this -> getEMailCommon($userAccount),
                VAR_CS_MAIL_ADDRESS);
    }
    
    private function readableTransactionData($transStack){
        $readableCols = [
            "Transaction Number",
            "User Account",
            "Amount Reflected",
            "Currency",
            "Method",
            "Deposit Bank",
            "Deposit Date",
            "Fee",
            "Comment",
            "Create Time",
            "Process User",
            "Type",
            "Error Flag",
            "FLAT3 Transaction ID"
        ];
        
        $readableReturn = [];
        
        foreach($transStack as $key => $value)
            $readableReturn[$readableCols[$key]] = $value;
        
        return print_r($readableReturn, true);
    }
    
    public function processTransactionData($method, $depositBank){
        $transactionDate = date("Y-m-d H:i:s");
        
        foreach($this -> depositData as &$deposit){            
            $deposit["date"]                = $transactionDate;
            $deposit["transaction_number"]  = $this -> getTransactionNumberCommon(VAR_TRANSACTION_DEPOSIT);
            $deposit["toProcessAmount"]     = floor($this -> getColumnData($deposit, "depositAmount") / 1.05);
            
            $transStack = [
                $this -> getColumnData($deposit, "transaction_number"),
                $this -> getColumnData($deposit, "userId"),
                $this -> getColumnData($deposit, "toProcessAmount"),
                JPY,
                $method,
                $depositBank,
                $this -> getColumnData($deposit, "date"),
                NO_COUNT,
                $this -> getColumnData($deposit, "flat3TransId").'/'
                    .$this -> getColumnData($deposit, "depositAmount").'@'
                    .$this -> getColumnData($deposit, "userId"),
                $transactionDate,
                "9999",
                NO_COUNT,
                NO_COUNT,
                $this -> getColumnData($deposit, "flat3TransId")
            ];
            
            $this -> accessModify("INSERT_DEPOSIT_FLAT3", $transStack, false);
            $this -> sendCompleteMail($deposit);
            
            $this -> logDetails("Completed: {$this -> readableTransactionData($transStack)}", VAL_INT_3);
        }
        
        echo "OK";
    }
}