<?php
include_once('../system/lib/config.php');

class MoneyRequest extends System {
    
    /**
     * 
     * @var string
     */
    private $pnum                       = NO_STRING;
    
    /**
     * 
     * @var string
     */
    private $amount                     = NO_COUNT;
    
    /**
     * 
     * @var string
     */
    private $debitAmount                = NO_COUNT;
    
    /**
     * 
     * @var string
     */
    private $fromAccount                = NO_STRING;
    
    /**
     * 
     * @var string
     */
    private $toAccount                  = NO_STRING;
    
    /**
     * 
     * @var string
     */
    private $debitCurrency              = NO_STRING;
    
    /**
     * 
     * @var string
     */
    private $currency                   = NO_STRING;
    
    /**
     * 
     * @var string
     */
    private $signature                  = NO_STRING;
    
    /**
     * 
     * @var string
     */
    private $message                    = NO_STRING;
    
    // header-filled params
    /**
     * 
     * @var string
     */
    private $ip                         = NO_STRING;
    
    /**
     * 
     * @var string
     */
    private $referer                    = NO_STRING;
    // end-of-header-filled-params
    
    /**
     * 
     * @var boolean
     */
    private $proceedToRemittanceRequest = false;
    
    /**
     * 
     * @var array
     */
    private $result                     = [];
    
    /**
     * 
     * @var array
     */
    private $ctransfer                  = [];
    
    // Case-to-case basis variables
    /**
     * 
     * @var string
     */
    private $transactionNum             = NO_STRING;
    
    /**
     * 
     * @var string
     */
    private $toFee                      = NO_COUNT;
    // end-of-case-to-case-basis-variables
    
    /**
     * 
     * @var string
     */
    private $accessLogDir               = NO_STRING;
    
    /**
     * 
     * 
     * @var boolean
     */
    private $forTest                    = false;
    
    /**
     * 
     * @var integer
     */
    private $responseCode               = 200;
    
    public function __construct(){
        parent::__construct();
        
        $this -> setParameter();
        $this -> validate();
    }
    
    private function setParameter(){
        $this -> pnum           = $this -> getDataPost("p_num");
        $this -> amount			= $this -> getDataPost('amount');
        $this -> fromAccount	= $this -> getDataPost('from_account');
        $this -> toAccount		= $this -> getDataPost('to_account');
        $this -> debitCurrency	= $this -> getDataPost('debit_currency');
        $this -> currency		= $this -> getDataPost('currency');
        $this -> message		= $this -> getDataPost('message');
        $this -> signature		= $this -> getDataPost('signature');
        $this -> forTest        = $this -> getDataPost("test");
        
        $this -> ip             = $this -> getColumnData($_SERVER, "REMOTE_ADDR");
        $this -> referer        = $this -> getColumnData($_SERVER, "HTTP_REFERER");
    }
    
    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 -> accessLogDir;
                break;
        }
        
        error_log(
            "{$logTimestamp}\r\n{$content}\r\n", VAL_INT_3,
            "{$logFile}/Log_{$logDate}.log");
    }
    
    public function validate(){
        $this -> accessLogDir = "Logs/remittance/access";
        
        if(!@file_exists($this -> accessLogDir)){
            mkdir($this -> accessLogDir, 0777, true);
        }
        
        $charvalidity = [
            $this -> fromAccount,
            $this -> toAccount
        ];
        
        foreach($charvalidity as $checkaccnum){
            if(!is_numeric($checkaccnum)){
                $this -> result[PARAM_RESULT] = sprintf("%02d", VAL_INT_6);
                $this -> result[PARAM_STATUS] = "CHARACTER_RESTRICTION";
                $this -> result["desc"] = "account number must be numeric";
                
                goto ends;
            }
            
            if(strlen($checkaccnum) > VAL_INT_8){
                $this -> result[PARAM_RESULT] = sprintf("%02d", VAL_INT_6);
                $this -> result[PARAM_STATUS] = "CHARACTER_LIMIT";
                $this -> result["desc"] = "an account number must only have 8 digits";
                
                goto ends;
            }
        }
        
        $requestDetails = [
            "IP_Address" => $this -> getColumnData($_SERVER, "REMOTE_ADDR"),
            "Program_Code" => $this -> pnum
        ];
        
        $requestDetails = print_r(array_merge($requestDetails, apache_request_headers()), true);
        $this -> logDetails($requestDetails, VAL_INT_1);
        
        $userAgent  = $this -> getColumnData($_SERVER, "HTTP_USER_AGENT");
        $referer    = $this -> getColumnData($_SERVER, "HTTP_REFERER");
        
        $whitelisitingValidationPassed = true;
        
        // first layer of validation (Whitelisted IP Address)
        $isIpInRemittanceDisabled = false;
        
        // second layer of validation (Whitelisted Domains)
        $isDomainInRemittanceDisabled = false;
        
        if($userAgent && $referer){
            // this condition will be considered if this was triggered in browser
            if(!$this -> checkSettlementIpAddress($this -> pnum, "allow_ip_in_remittance", $isIpInRemittanceDisabled)){
                if(!$isIpInRemittanceDisabled)
                    $whitelisitingValidationPassed = false;
            }
            
            if(!$this -> checkSettlementDomain($this -> pnum, "allow_domain_in_remittance", $isDomainInRemittanceDisabled)){
                if(!$isDomainInRemittanceDisabled)
                    $whitelisitingValidationPassed = false;
            }
        }else{
            // to insist the mandatory checking of IP in case it's triggered using server-to-server processing.
            if(!$this -> checkSettlementIpAddress($this -> pnum, "mandatory", $isIpInRemittanceDisabled)){
                if(!$isIpInRemittanceDisabled)
                    $whitelisitingValidationPassed = false;
            }
        }
        
        if($whitelisitingValidationPassed){
            if(($this -> fromAccount != NO_STRING && $this -> toAccount != NO_STRING) 
                    && ($this -> fromAccount == $this -> toAccount)){
                $this -> result[PARAM_RESULT] = sprintf('%02d', VAL_INT_93);
                $this -> result[PARAM_STATUS] = "REJECTED";
                
                $this -> responseCode = 406;
            }else{
                // APIチェック用データ
                // シグネチャ生成用のデータを取得する
                $row			= $this -> getRowData($this -> accessSelect("SELECT_API_SIGNATURE_BY_A_ID", array($this -> pnum)));
                $apiPassword	= $this -> getColumnData($row, COLUMN_PASSWORD);
                $apiUserAccount	= $this -> getColumnData($row, COLUMN_USER_ACCOUNT);
                
                $checkSignature = $apiUserAccount
                .$apiPassword
                .$this -> pnum
                .$this -> amount;
                
                $sha = hash("sha256", $checkSignature);
                                
                if($apiUserAccount !== $this -> fromAccount){
                    $this -> result[PARAM_RESULT] = sprintf("%02d", VAL_INT_4);
                    $this -> result[PARAM_STATUS] = "SENDER_ERROR";
                    
                    goto ends;   
                }
                
                if($this -> signature != $sha){
                    $this -> result["combination"] = [
                        "real_raw" => $checkSignature,
                        "real_signature" => $sha
                    ];
                    
                    $this -> result[PARAM_RESULT] = sprintf("%02d", VAL_INT_90);
                    $this -> result[PARAM_STATUS] = "REJECTED";
                    
                    $this -> responseCode = 406;
                }else{                    
                    $this -> randomSecondsDelay();
                    
                    $this -> ctransfer = $this -> checkTransfer(
                        $this -> fromAccount,
                        $this -> toAccount,
                        $this -> currency,
                        $this -> amount,
                        $this -> debitCurrency);
                    
                    if($this -> ctransfer[PARAM_RESULT] == NO_COUNT){
                        $this -> proceedToRemittanceRequest = true;
                    }else{
                        $format = sprintf("%02d", $this -> ctransfer[PARAM_RESULT]);
                        
                        $this -> result[PARAM_RESULT] = $format;
                        $this -> result[PARAM_STATUS] = "ERROR(CODE:{$format})";
                        
                        $this -> responseCode = 406;
                    }
                }
            }
        }else{
            $this -> result[PARAM_RESULT] = sprintf("%02d", VAL_INT_92);
            $this -> result[PARAM_STATUS] = "REJECTED";
            
            $this -> responseCode = 406;
        }
        
        ends:
        $this -> logPost($this -> pnum);
    }
    
    private function randomSecondsDelay(){
        $processDelaySettings = $this -> getSettingConfiguration("remittance_api_random_process_sleeper");
        
        if($processDelaySettings){
            $delayTime = mt_rand(
                $processDelaySettings -> min, 
                $processDelaySettings -> max);
            
            $this -> result["delayTime"] = "{$delayTime} second(s).";
            
            sleep($delayTime);
        }
    }
    
    private function getTransactionalParams(){
        $this -> debitAmount = $this -> getColumnData($this -> ctransfer, PARAM_AMOUNT);
        
        // 取引番号の生成
        $this -> transactionNum = !$this -> forTest ? 
            $this -> getTransactionNumberCommon(VAR_TRANSACTION_TRANSFER_DEPOSIT) : 
            "T01234567";
        
        //to fee
        $this -> toFee = $this -> currencyToInt($this -> getColumnData($this -> ctransfer, PARAM_TO_FEE), $this -> currency);
        
        $params = [];
        
        // 送金実行
        $params[] = $this -> fromAccount;																			// 送金元口座番号
        $params[] = $this -> fromAccount;																			// 送金元口座番号
        $params[] = $this -> debitCurrency;																			// 送金通貨
        $params[] = $this -> currencyToInt($this -> getColumnData($this -> ctransfer, PARAM_FROM_FEE), $this -> debitCurrency);	// 口座元手数料
        $params[] = $this -> toAccount;																				// 送金先口座番号
        $params[] = $this -> currency;																				// 送金元通貨
        $params[] = $this -> fee;			// 送金先手数料
        $params[] = $this -> amount;																				// 入力金額
        $params[] = NO_COUNT;																				// 送金方法
        $params[] = $this -> message;																				// メッセージ
        $params[] = $this -> debitCurrency;																			// 出金通貨
        $params[] = $this -> currencyToInt($this -> debitAmount, $this -> debitCurrency);									// 出金金額
        $params[] = $this -> currency;																				// 送金通貨
        $params[] = $this -> currencyToInt($this -> amount, $this -> currency);												// 送金金額
        $params[] = $this -> getColumnData($this -> ctransfer, PARAM_RATE);												// レート
        $params[] = NO_COUNT;																				// 処理種別
        $params[] = NO_STRING;																				// 管理者ID
        $params[] = VAL_INT_2;																				// 処理ステータス
        $params[] = $this -> transactionNum;																		// 取引番号
        $params[] = date("Y-m-d H:i:s");
        
        return $params;
    }
    
    private function getFullNameString(Array $rowData){
        if($this -> isLoopData($rowData)){
            return 
                $this -> getColumnData($rowData, COLUMN_FIRST_NAME)
                .DELIMIT_SPACE
                .$this -> getColumnData($rowData, COLUMN_LAST_NAME);
        }
        
        return NO_STRING;
    }
    
    private function getSendDataParams() {        
        $rtn	= [];
        
        $row1	= $this -> getRowData($this -> getAccountCommon($this -> fromAccount));
        $row2	= $this -> getRowData($this -> getAccountCommon($this -> toAccount));
        
        $rtn[] = $this -> fromAccount;															// ユーザ口座番号
        $rtn[] = $this -> getFullNameString($row2);			                                  // ユーザ名
        $rtn[] = $this -> getUserData(PARAM_USER_ACCOUNT);										// 送金先口座番号
        $rtn[] = $this -> getFullNameString($row1);                                              // ユーザ名（送金元）
        $rtn[] = $this -> getCurrentTimeCommon();												// 現在時刻
        $rtn[] = $this -> currency;																// 通貨
        $rtn[] = $this -> formatCurrency($this -> amount, $this -> currency);					// 金額
        $rtn[] = $this -> debitCurrency;														// 手数料通貨
        $rtn[] = $this -> formatCurrency($this -> toFee, $this -> currency);					// 手数料
        $rtn[] = $this -> message;																	// メッセージ
        $rtn[] = $this -> transactionNum;													// 取引番号
        $rtn[] = $this -> getColumnData($row2, COLUMN_MAIL);									// メールアドレス
        
        return $rtn;
    }
    
    private function printCallbackResult(){
        header("HTTP/1.1 {$this -> responseCode}");
        
        $this -> accessModifyCommon('INSERT_LOG_POST',
            [
                $this -> ip,
                $this -> pnum,
                $this -> referer,
                '[REMITTANCE_CALLBACK_RESULT]'.json_encode($this -> result)
            ]);
       
        $toUnset = ["combination", "delayTime"];
        
        foreach($toUnset as $uns){
            if(isset($this -> result[$uns]))
                unset($this -> result[$uns]);
        }
        
        echo json_encode($this -> result);
    }
    
    private function actualProcessing($params){
        $insertNewTransfer = $this -> accessModify('INSERT_TRANSFER', $params, false);
        
        if($insertNewTransfer){
            $encodedParams = json_encode($params);
            
            $this -> accessModifyCommon('INSERT_LOG_POST', [
                $this -> ip,
                $this -> pnum,
                $this -> referer,
                "[INSERT_DB]{$encodedParams}"
                ]);
            
            $transactionNum = $params[VAL_INT_18];
            
            // send email to receiver
            $emailParams    = $this -> getSendDataParams();
            
            $toAccountData  = $this -> getAccountCommon($this -> toAccount);
            $language       = $this -> getColumnData($this -> getRowData($toAccountData), COLUMN_USE_LANGUAGE);
            
            $this -> sendMailByTmp($language.DIRECTORY_SEPARATOR.'transfer_receive_user.xml'
                , $emailParams
                , $emailParams[VAL_INT_11]
                , VAR_CS_MAIL_ADDRESS);
            
            // 通貨が違う場合は、為替コミッションの発生
            if($this -> debitCurrency != $this -> currency) {
                
                // 着金額指定の場合
                $this -> registCommisionExchange($this -> fromAccount
                    , $this -> currency
                    , $this -> debitCurrency
                    , $this -> currencyToInt($this -> debitAmount, $this -> debitCurrency));
            }
            
            // コミッションの集計（送金元）
            $this -> registCommision(VAR_TRANSFER
                , null
                , $this -> fromAccount
                , $this -> debitCurrency
                , $this -> getColumnData($this -> ctransfer, PARAM_FROM_FEE));
            
            // コミッションの集計（送金先）
            $this -> registCommision(VAR_TRANSFER
                , null
                , $this -> toAccount
                , $this -> currency
                , $this -> getColumnData($this -> ctransfer, PARAM_TO_FEE)
                , PARAM_TO);
            
            $this -> result[PARAM_RESULT]				  = sprintf("%02d", NO_COUNT);
            $this -> result[PARAM_STATUS]				  = "OK";
            $this -> result[PARAM_TRANSACTION_NUMBER]	  = $transactionNum;
            $this -> result[PARAM_CURRENCY]			      = $this -> debitCurrency;
            $this -> result[PARAM_AMOUNT]				  = $this -> debitAmount;
            $this -> result[PARAM_FEE]					  = $this -> getColumnData($this -> ctransfer, PARAM_FROM_FEE);
            
            // this brings back memories - anton (2021)
            $this -> result[PARAM_FROM_ACCOUNT]		      = $this -> fromAccount;
            $this -> result[PARAM_TO_ACCOUNT]			  = $this -> toAccount;
        }else{
            $this -> result[PARAM_RESULT] = sprintf("%02d", VAL_INT_99);
            $this -> result[PARAM_STATUS] = "REJECTED";
            
            $this -> responseCode = 406;
            
            $this -> accessModifyCommon("INSERT_LOG_POST",
                [
                    $this -> ip,
                    $this -> pnum,
                    $this -> referer,
                    '[INSERT_DB_ERR]'.json_encode($params)
                ]);
        }
    }
    
    private function emulateProcessing($params){
        $encodedParams = json_encode($params);
        
        $this -> accessModifyCommon('INSERT_LOG_POST', [
            $this -> ip,
            $this -> pnum,
            $this -> referer,
            "[TEST_INSERT_DB]{$encodedParams}"
            ]);
        
        $this -> result[PARAM_RESULT]				  = sprintf("%02d", NO_COUNT);
        $this -> result[PARAM_STATUS]				  = "OK";
        $this -> result[PARAM_TRANSACTION_NUMBER]	  = $params[VAL_INT_18];
        $this -> result[PARAM_CURRENCY]			      = $this -> debitCurrency;
        $this -> result[PARAM_AMOUNT]				  = $this -> debitAmount;
        $this -> result[PARAM_FEE]					  = $this -> getColumnData($this -> ctransfer, PARAM_FROM_FEE);
        
        // this brings back memories - anton (2021)
        $this -> result[PARAM_FROM_ACCOUNT]		      = $this -> fromAccount;
        $this -> result[PARAM_TO_ACCOUNT]			  = $this -> toAccount;
    }
    
    public function listen(){
        if($this -> proceedToRemittanceRequest){
            $params = $this -> getTransactionalParams();
            
            if($this -> forTest)
                $this -> emulateProcessing($params); 
            else
                $this -> actualProcessing($params);
        }
        
        $this -> printCallbackResult();
    }
}

$request = new MoneyRequest();
$request -> listen();