<?php
use Firebase\JWT\JWT;
use Defuse\Crypto\Crypto;
use function GuzzleHttp\json_encode;

/**
 *
 * 目的： PaymentAPI
 * 開発者：　Anthony
 *
 */
class PaymentAPI extends System {
    
    /**
     *
     * シングルトンの一般的な変数
     *
     * @var object
     */
    private static $singleton;
    
    /**
     * 
     * @var integer
     */
    private $remarks    = NO_COUNT;
    
    /**
     * 
     * @var payload
     */
    private $data;
    
    /**
     * 
     * @var unknown
     */
    private $paymentAPIconf;
    
    /**
     * 
     * @var unknown
     */
    private $midconf;
    
    private const INVALID_JSON_STRING = 1;
    private const NO_PROGRAM_CODE = 2;
    
    /**
     *
     * コンストラクタ
     *
     */
    public function __construct(){
        parent::__construct();
        $this -> initSettings();
    }
    
    private function initSettings(){
        $this -> paymentAPIconf = $this -> getConfiguration("payment_api_conf");
        $this -> midconf        = $this -> getConfiguration("middleware_conf");
        
        if(isset($this -> paymentAPIconf -> middleware_sig_passphrase)){
            $replacements = [
                "{curdate_int}" => strtotime(date("Ymd")),
                "{curdate_str}" => date("Ymd")
            ];
            
            foreach($replacements as $search => $replace){
                $this -> paymentAPIconf -> middleware_sig_passphrase = str_replace(
                    $search,
                    $replace,
                    $this -> paymentAPIconf -> middleware_sig_passphrase);
            }
        }
    }
    
    /**
     * シングルトンの機能
     *
     * @return PaymentAPI
     */
    private static function getInstance() : PaymentAPI {
        if(!isset(self::$singleton))
            self::$singleton = new PaymentAPI();
            
        return self::$singleton;
    }
    
    /**
     *
     * 目的の出力
     */
    public static function action(){
        $purpose = self::getInstance();
        
        $purpose -> redirectToForm();
    }
    
    /**
     *
     * 公開的な確認
     *
     * @param string $data
     * @param array $verdict
     */
    public static function validation($data, &$verdict){
        $purpose = self::getInstance();
        
        $purpose -> setData($data);
        $purpose -> checkSettlementPayload();
        $purpose -> setVerdict($verdict);
    }
    
    /**
     * 
     * 支払ペイロードの確認
     * 
     */
    private function checkSettlementPayload(){
        if($this -> checkJSONString($this -> data)){
            $settleData = json_decode($this -> data, true);
            
            $apiSignature = $this -> getRowData(
                $this -> getAPISignatureCommon($this -> getColumnData($settleData, PARAM_P_NUM)));
            
            if(!$this -> isLoopData($apiSignature)){
                $this -> invalid[] = self::NO_PROGRAM_CODE;
            }
        }else
            $this -> remarks = self::INVALID_JSON_STRING;
    }
    
    /**
     * 
     * 
     * ペイロードデータの設定
     * 
     * @param unknown $data
     */
    private function setData($data){
        $this -> data = $data;
    }
    
    /**
     *
     * 開発者の設定
     *
     * @return boolean|mixed
     */
    private function getConfiguration($settingId){
        $devSetting = $this -> getRowData($this -> accessSelect("SELECT_DEV_SETTING", [$settingId]));
        
        if($this -> isLoopData($devSetting)){                
            if($this -> checkJSONString($this -> getColumnData($devSetting, "dev_setting_value"))){
                $settingsValue = json_decode($this -> getColumnData($devSetting, "dev_setting_value"));
                return $settingsValue;
            }
        }else
            return false;
    }
    
    /**
     *
     * 確認の結果設定
     *
     * @param array $verdict
     */
    private function setVerdict(&$verdict){
        switch($this -> remarks){
            case NO_COUNT:
                $verdict = [
                    "validation" => true,
                    "description" => NO_STRING
                ];
                break;
            case self::INVALID_JSON_STRING:
                $verdict = [
                    "validation" => false,
                    "description" => "Syntax Error on Payload JSON String found"
                ];
                break;
            case self::NO_PROGRAM_CODE:
                $verdict = [
                    "validation" => false,
                    "description" => "Program Code doesn't exist"
                ];
                break;
        }
    }
    
    /**
     * 
     * 支払いフォームIDの作成者
     * 
     * @param array $registData
     * @return boolean|unknown
     */
    private function createSettlementFormId(Array $registData){
        return $this -> accessModify("INSERT_SETTLEMENT_FORM", 
            array_merge(
                $registData, 
                [base64_encode($this -> data)]
                ), false);
    }
    
    /**
     * 
     * 
     * 支払フォームへのリダイレクト
     * 
     */
    private function redirectToForm(){
        $payloadData    = json_decode($this -> data);
        $uniqueKey      = $this -> createUId();
        
        $this -> createSettlementFormId([
            $uniqueKey,
            $payloadData -> p_num
        ]);
        
        $midsignature = Crypto::encryptWithPassword(
            json_encode([
                "name" => "middleware_sig_M".strtotime("YmdHis"),
                "uniqueKey" => $uniqueKey,
                "creation" => date("Y-m-d H:i:s"),
                "expiration" => date("Y-m-d H:i:s", strtotime($this -> paymentAPIconf -> form_duration)),
                "signature" => $payloadData -> signature,
                "p_num" => $payloadData -> p_num
            ]), 
            $this -> paymentAPIconf -> middleware_sig_passphrase);
        
        unset($payloadData -> signature);
        unset($payloadData -> p_num);
        
        $formlinkPayload = [
            "midsign" => $midsignature,
            "data" => base64_encode(json_encode($payloadData))
        ];
        
        $formToken = JWT::encode(
            $formlinkPayload, 
            $this -> midconf -> token_secretkey.strtotime(date("Ymd")),
            $this -> midconf -> algorithm);
        
        $baseUrl = SITE_PROTOCOL."://".SITE_DOMAIN_FULL."/{$payloadData -> language}/settlement";
        $params = "type=".TYPE_SETTLEMENT_EXPRESS_API."&ptoken={$formToken}";
        
        $redirection = "{$baseUrl}?{$params}";
        
        header("Location: {$redirection}");
    }
    
    /**
     *
     * 詳細をログ
     *
     * @param unknown $content
     */
    private function logDetails($content){
        $timestamp = date("Y-m-d H:i:s");
        
        $logDir = SITE_ROOT."api/Logs/settlement/curl";
        
        if(!@file_exists($logDir))
            mkdir($logDir, 0777, true);
            
        $logFile = "{$logDir}/Log_".date("Y-m-d").".log";
        error_log("[{$timestamp}]\n\n{$content}\n\n", VAL_INT_3, $logFile);
    }
}