<?php
require_once "../lib/config.php";

/**
 * 
 * @author i-Wallet
 *
 */
class SuspendAndDeduct extends System {
    
    /**
     * 
     * @var integer
     */
    private $forTest;
    
    /**
     * 
     * @var string
     */
    private $payload;
    
    /**
     * 
     * @var string
     */
    private $contentType;
    
    /**
     * 
     * @var string
     */
    private $requestMethod;
    
    /**
     * 
     * @var string
     */
    private $ipAddress;
    
    /**
     * 
     * @var array
     */
    private $reportObject   = [];
    
    /**
     * 
     * コンストラクタ
     * 
     */
    public function __construct(){
        parent::__construct();
        header("Content-type: text/plain");
        
        $this -> setParameter();
        $this -> validation();
    }
    
    /**
     * 
     * パラメータの設定
     * 
     */
    private function setParameter(){
        $this -> payload            = file_get_contents("php://input");
        $this -> ipAddress          = $this -> getColumnData($_SERVER, "REMOTE_ADDR");
        $this -> contentType        = $this -> getColumnData($_SERVER, "CONTENT_TYPE");
        $this -> requestMethod      = $this -> getColumnData($_SERVER, "REQUEST_METHOD");
    }
    
    /**
     * 
     * データを確認
     * 
     */
    private function validation(){
        $validation = [];
        
        $chargeAPIConf = $this -> getSettingConfiguration("charge_api_conf");
        
        $ipAddresses = explode(",", $chargeAPIConf -> ip_address);
        
        if(!$this -> checkJSONString($this -> payload)){
            $validation[] = "Invalid JSON String format found on the request data";
        }
        
        if(strtoupper($this -> requestMethod) != "POST"){
            $validation[] = "Invalid Request Method: {$this -> requestMethod}";
        }
        
        if(!in_array($this -> ipAddress, $ipAddresses)){
            $validation[] = "Invalid IP Address: {$this -> ipAddress}";
        }
        
        if($this -> contentType != "application/json"){
            $validation[] = "Invalid Content-type: {$this -> contentType}";
        }
        
        if($this -> isLoopData($validation)){
            header("HTTP/1.1 403 Forbidden due to validation results");
            die(json_encode($validation));
        }
        
        $this -> payload    = json_decode($this -> payload, true);
        $this -> forTest    = $this -> payload["test"];
    }
    
    /**
     * 
     * @param unknown $later
     * @param unknown $earlier
     * @param unknown $period
     * @return boolean
     */
    private function isWithinPeriod($later, $earlier, $period){
        $dateDiff = $this -> computeDateDiff($later, $earlier);
        
        //if dateDiff is <= $period days
        return $dateDiff <= $period;
    }
    /**
     * 
     * @param unknown $subtrahend
     * @param unknown $subtractor
     * @return number
     */
    private function computeDateDiff($subtrahend, $subtractor){
        $dateDiff = strtotime($subtrahend) - strtotime($subtractor);
        
        //divide to get the number of days
        $dateDiff = floor($dateDiff / 86400); 
        
        return $dateDiff;
    }
    
    private function appendSuspendedReport($userAccount, $suspensionStatus){
        if(!isset($this -> reportObject["suspended"]))
            $this -> reportObject["suspended"] = [];
            
        $userAccount["suspensionStatus"] = $suspensionStatus;
        $userAccount["dateOfSuspension"] = date("Y/m/d");
        $this -> reportObject["suspended"][] = $userAccount;
    }
    
    private function appendDeductionReport($userAccount, $transactionNumber, $currency, $dormancyFee){
        if(!isset($this -> reportObject["deducted"]))
            $this -> reportObject["deducted"] = [];
            
        $userAccount["transactionNumber"]   = $transactionNumber;
        $userAccount["dormancyFee"]         = $dormancyFee;
        $userAccount["currency"]            = $currency;
        $userAccount["dateOfDeduction"]     = date("Y/m/d");
        
        $this -> reportObject["deducted"][] = $userAccount;
    }
    
    private function suspendAccount($userAccount, $status){        
        //suspend account
        if($this -> forTest){
            $this -> rawSQL("INSERT INTO t_job_test_suspended_account (user_account, status, suspension_date) ".
                "VALUES ('{$userAccount[PARAM_USER_ACCOUNT]}', '{$status}', CONVERT_TZ(NOW(),@@session.time_zone,'+09:00'))");
        }else{
            $this -> accessModify('UPDATE_USER_STATUS_INACTIVE',
                [$this -> getColumnData($userAccount, PARAM_USER_ACCOUNT), $status],
                false);
        }
    }
    
    private function executeWithdraw($userAccount, $currency, $amount){
        $transactionNumber = NO_STRING;
        
        if($this -> forTest){
            $transactionNumber = $this -> createUId();
            
            $this -> rawSQL("INSERT INTO t_job_test_transactions (transaction_number, user_account, amount, currency, note, status, create_time) ".
                "VALUES ('{$transactionNumber}', '{$userAccount[PARAM_USER_ACCOUNT]}', '{$amount}', '{$currency}', 'Dormancy Test', 2, CONVERT_TZ(NOW(),@@session.time_zone,'+09:00'))");
        }else{
            $transactionNumber = $this -> registInactiveFeeCommon($userAccount[PARAM_USER_ACCOUNT],
                $currency,
                $this -> intToCurrency($amount, $currency),
                VAR_TRANSACTION_DOMANT_FEE,
                'Inactive Account Maintenance Fee',
                VAL_INT_2
            );
        }
        
        return $transactionNumber;
    }
    
    private function deductInactiveFee($userAccount, $currency, $amount){        
        //return string for deduction details
        $returnString = NO_STRING;
        
        $transactionNumber = $this -> executeWithdraw($userAccount, $currency, $amount);
        
        $amount = $this -> intToCurrency($amount, $currency);
        
        //if JPY or PHP or IDR, remove decimal
        if(in_array($currency, $this -> getZeroDecimalCurrenciesCommon())){
            $amount = floor($amount);
        }
        
        $returnString = "• {$currency} {$amount} ({$transactionNumber}) \n";
        
        return $returnString;
    }
    
    private function processAccountBalance($userAccount){
        //temporary string holder
        $tempString = NO_STRING;
        
        //get all balances
        $balances = array();
        $balances = $this -> getBalanceListCommon($userAccount[PARAM_USER_ACCOUNT]);
        
        //fee
        $fee = 20;
        $feeInt = $this -> currencyToInt($fee, USD);
        
        //check if there is a balance in USD
        if(isset($balances['USD'])){
            //check if USD is enough
            if(($balances['USD'] - $feeInt) >= 0){
                //deduct
                $tempString = $this -> deductInactiveFee($userAccount, 'USD', $feeInt);
                
                //parse $deductionDetails to get the transaction number
                $transactionNumber = $this -> getTransactionNumber($tempString);
                
                $currency = USD;
                $this -> appendDeductionReport($userAccount, $transactionNumber, $currency, $this -> intToCurrency($feeInt, $currency));      
                return;
            }
        }
        
        //define converted (to USD) currency array
        $convertedCurrencyBalances = array();
        
        //convert other currencies
        foreach ($balances as $key => $value) {
            if($key != 'USD'){
                $exchange = $this -> getExchangeCommon($value, $key, 'USD', true, false);
                $zeroDecimalCurrencies = $this -> getZeroDecimalCurrenciesCommon();
                
                if(in_array($key, $zeroDecimalCurrencies)){
                    $convertedCurrencyBalances[$key] = floor($this -> getColumnData($exchange, PARAM_ORIGINAL_AMOUNT) * 100);
                } else {
                    $convertedCurrencyBalances[$key] = floor($this -> getColumnData($exchange, PARAM_ORIGINAL_AMOUNT));
                }
            } else
                $convertedCurrencyBalances[$key] = $value;
        }
        
        //check if all the converted amount is >= $feeInt
        if(array_sum($convertedCurrencyBalances) >= $feeInt){
            //check if there is USD currency in the balance
            if(isset($convertedCurrencyBalances['USD'])){
                //check first if there is remaining USD
                if($convertedCurrencyBalances['USD'] > 0){
                    //deduct from remaining USD
                    $feeInt -= $balances['USD'];
                    
                    $tempString = $this -> deductInactiveFee($userAccount, 'USD', $balances['USD']);
                    
                    //parse $deductionDetails to get the transaction number
                    $transactionNumber = $this -> getTransactionNumber($tempString);
                    
                    $currency = USD;
                    
                    $this -> appendDeductionReport($userAccount, $transactionNumber, $currency, 
                        $this -> intToCurrency($balances[$currency], $currency));
                }
                
                //unset USD
                unset($convertedCurrencyBalances['USD']);
            }
            
            //sort $convertedCurrencyBalances in desc order
            arsort($convertedCurrencyBalances);
            
            //after sorting deduct
            foreach($convertedCurrencyBalances as $key => $value){
                //check if there is still remaining fee
                if($feeInt > 0){
                    if(($value - $feeInt) >= 0){
                        //convert fee currency
                        $exchange	= $this -> getExchangeCommon($feeInt, 'USD', $key, true, false);
                        $zeroDecimalCurrencies = $this -> getZeroDecimalCurrenciesCommon();
                        
                        if(in_array($key,$zeroDecimalCurrencies)){
                            $deductAmount = floor($this -> getColumnData($exchange, PARAM_ORIGINAL_AMOUNT) / 100);
                        } else {
                            $deductAmount = floor($this -> getColumnData($exchange, PARAM_ORIGINAL_AMOUNT));
                        }
                        
                        //deduct
                        $tempString = $this -> deductInactiveFee($userAccount, $key, $deductAmount);
                        
                        //parse $deductionDetails to get the transaction number
                        $transactionNumber = $this -> getTransactionNumber($tempString);
                        
                        $this ->appendDeductionReport($userAccount, $transactionNumber, $key, $this -> intToCurrency($deductAmount, $key));
                        break;
                    } else{
                        //get remaining fee
                        $feeInt -= $value;
                        //deduct
                        $tempString = $this -> deductInactiveFee($userAccount, $key, $balances[$key]);
                        
                        $transactionNumber = $this -> getTransactionNumber($tempString);
                        
                        //put report data
                        $this ->appendDeductionReport($userAccount, $transactionNumber, $key, $this -> intToCurrency($deductAmount, $key));
                    }
                } else
                    break;
            }
        } else{
            //suspend
            $suspensionStatus = ($userAccount['status'] == VAL_INT_3) ? VAL_INT_7 : VAL_INT_6;
            $this -> suspendAccount($userAccount, $suspensionStatus);
            
            //put report data
            $this -> appendSuspendedReport($userAccount, $suspensionStatus);
        }
    }
    
   /**
    * 
    * @param unknown $transactionList
    * @param unknown $userAccount
    * @return boolean
    */
    private function isTransactionWithinPeriod($transactionList, $userAccount){
        //check if there is last lift date
        if($userAccount['last_lift_date'] != null){
            $hasTransactionWithin180 = false;
            $hasTransactionAfterLift = false;
            
            //if there is last lift date, check if it not is between now and now-180
            if(!$this -> isWithinPeriod(date("Y-m-d H:i:s"), $userAccount['last_lift_date'], 180)){
                //check for any transactions within now and now - 180
                foreach($transactionList as $transaction){
                    if($this -> isWithinPeriod(date("Y-m-d H:i:s"), $transaction['transaction_time'], 180)){
                        $hasTransactionWithin180 = true;
                        break;
                    }
                }
                
                //check for any transactions within 180 days after last lift date
                foreach($transactionList as $transaction){
                    if($this -> isWithinPeriod($transaction['transaction_time'], $userAccount['last_lift_date'], 180)){
                        $hasTransactionAfterLift = true;
                        break;
                    }
                }
                
                //check if both has passed
                if($hasTransactionWithin180 && $hasTransactionAfterLift)
                    return true;
            } else
                return true;
            
        } else{
            //check for any transactions within now and now - 180
            foreach($transactionList as $transaction){
                if($this -> isWithinPeriod(date("Y-m-d H:i:s"), $transaction['transaction_time'], 180))
                    return true;
            }
        }
        
        return false;
    }
    
    private function getTransactionNumber($string){
        $transactionNumber = NO_STRING;
        
        if(strlen($string) > 0) {
            $arrayContainer = explode("(", $string);
            $arrayContainer = explode(")", $arrayContainer[1]);
            $transactionNumber = $arrayContainer[0];
        }
        
        return $transactionNumber;
    }
    
    /**
     * 
     * @param array $userAccounts
     */
    private function applySuspendAndDeduction(Array $userAccounts){
        foreach($userAccounts as $userAccount){
            $transactionList = $this -> accessSelect("LIST_USER_TRANSACTION_FOR_SUSPENSION_CHECKING",
                [$this -> getColumnData($userAccount, PARAM_USER_ACCOUNT), NO_STRING]);
            
            $dataCount = count($transactionList);
                                    
            if($dataCount < VAL_INT_1){
                $lastLiftDate = $userAccount["last_lift_date"];
                
                if($lastLiftDate != null && !$this -> isWithinPeriod(date("Y-m-d H:i:s"), $lastLiftDate, 180)){
                    //suspend
                    $suspensionStatus = ($userAccount['status'] == VAL_INT_3) ? VAL_INT_7 : VAL_INT_6;
                    $this -> suspendAccount($userAccount, $suspensionStatus);
                    $this -> appendSuspendedReport($userAccount, $suspensionStatus);
                    
                }else{
                    //suspend
                    $suspensionStatus = ($userAccount['status'] == VAL_INT_3) ? VAL_INT_7 : VAL_INT_6;
                    $this -> suspendAccount($userAccount, $suspensionStatus);
                    $this -> appendSuspendedReport($userAccount, $suspensionStatus);
                }
            }else{
                //check if there is a valid transaction
                $hasTransaction = $this -> isTransactionWithinPeriod($transactionList, $userAccount);
                
                //if there is no transaction
                if(!$hasTransaction)
                    //deduct or suspend
                    $this -> processAccountBalance($userAccount);
            }
        }
    }
    
    /**
     * 
     * 主な機能
     * 
     */
    public function listen(){
        if(is_array($this -> payload)){
            $start = microtime(true);
            
            $this -> applySuspendAndDeduction($this -> payload["accounts"]);
                        
            $this -> reportObject["elapsed_time"]   = sprintf("%.5f", microtime(true) - $start);
            $this -> reportObject["test_only"]      = $this -> forTest;
            
            echo json_encode($this -> reportObject);
        }
    }
}

$suspendAndDeduct = new SuspendAndDeduct();
$suspendAndDeduct -> listen();