<?php

namespace Sibs2;

defined('ABSPATH') || exit;

/**
 * Class SibsSBO.
 */
class SibsSBO
{
    const URL = [
        'DEV'  => 'https://stargate-dev.qly.sibs.pt',
        'TST'  => 'https://stargate-tst.qly.sibs.pt',
        'CER'  => 'https://stargate-cer.qly.sibs.pt',
        'QLY'  => 'https://stargate.qly.sibs.pt',
        'LIVE' => 'https://www.sibsgateway.com',
    ];

    /**
     * @var array
     */
    protected $date;

    /**
     * @var array
     */
    protected $headers;

    /**
     * @var string
     */
    protected $content;

    /**
     * @var string
     */
    protected $serverMode;

    /**
     * @var string
     */
    protected $deviceID;

    /**
     * @var string
     */
    protected $token;

    /**
     * Constructor.
     *
     * @param array $headers
     * @param array $content
     */
    public function __construct(array $headers, array $content)
    {
        $this->date         = sibsConfig::getStoreCurrentTime('Y-m-d\TH:i:s.v\Z');
        $this->headers      = $headers;
        $this->content      = $content;
        $this->serverMode   = $this->content['serverMode'] ?? null;
        $this->deviceID     = $this->content['deviceID'] ?? null;
        $this->token        = [
            'Usrnm'     => null,
            'TknId'     => null,
            'AccsTkn'   => null,
        ];
    }

    /**
     * loginSBO.
     *
     * @return string|false
     */
    public function loginSBO()
    {
        sibsLogger::debug('loginSBO -> init', sibsConstants::SIBS_API_LOG);

        try {
            $url          = self::URL[$this->serverMode] . '/api/1/management/user/login';
            $index        = 'LgnUsrReq';
            $endpointData = [
                'LgnUsr' => [
                    'UsrCrdntls' => [
                        'Usrnm' => $this->content['login'] ?? null,
                        'Pwd'   => $this->content['pass'] ?? null,
                    ],
                ],
            ];

            $postData   = $this->getPostData($url, $index, $endpointData);
            $resultCurl = $this->sibsCallCurl($url, $postData);

            if (empty($resultCurl)) {
                throw new \Exception("haven't received a json from curl result");
            }

            $endpointResponse = $resultCurl['LgnUsrRspn']['LgnUsr'] ?? null;

            if (! (! empty($endpointResponse['Rslt']['RsltStts']) && $endpointResponse['Rslt']['RsltStts'] == 'APPR')) {
                throw new \Exception('Login not approved on SBO');
            }

            sibsLogger::debug('loginSBO -> end', sibsConstants::SIBS_API_LOG);

            return $this->responseSuccess([
                'token'     => $endpointResponse['TknData']['AccsTkn'] ?? null,
                'NxtFlow'   => $resultCurl['LgnUsrRspn']['Hdr']['NxtFlow'] ?? null,
            ]);
        } catch(\Exception $e) {
            return $this->responseError([], $e->getMessage(), 400);
        }
    }

    /**
     * activateSMSCode.
     *
     * @return string|false
     */
    public function activateSMSCode()
    {
        sibsLogger::debug('activateSMSCode -> init', sibsConstants::SIBS_API_LOG);

        try {
            $url          = self::URL[$this->serverMode] . '/api/1/management/app/activation';
            $index        = 'AppActReq';
            $endpointData = [
                'AppAct' => [
                    'Act' => [
                        'ActCd' => $this->content['code'] ?? null,
                    ],
                ],
            ];
            $auth = $this->headers['Authorization'];

            $postData   = $this->getPostData($url, $index, $endpointData);
            $resultCurl = $this->sibsCallCurl($url, $postData, $auth);

            if (empty($resultCurl)) {
                throw new \Exception("haven't received a json from curl result");
            }

            $endpointResponse = $resultCurl['AppActRspn']['AppAct'] ?? null;

            if (! (! empty($endpointResponse['Rslt']['RsltStts']) && $endpointResponse['Rslt']['RsltStts'] == 'APPR')) {
                throw new \Exception('Activate SMS not approved on SBO');
            }

            sibsLogger::debug('activateSMSCode -> end', sibsConstants::SIBS_API_LOG);

            return $this->responseSuccess('Request approved on SBO');
        } catch(\Exception $e) {
            return $this->responseError([], $e->getMessage(), 400);
        }
    }

    /**
     * resendSMSCode.
     *
     * @return string|false
     */
    public function resendSMSCode()
    {
        sibsLogger::debug('resendSMSCode -> init', sibsConstants::SIBS_API_LOG);

        try {
            $url          = self::URL[$this->serverMode] . '/api/1/management/app/resendappactivationcode';
            $index        = 'RsndAppActCdReq';
            $endpointData = [];
            $auth         = $this->headers['Authorization'];

            $postData   = $this->getPostData($url, $index, $endpointData);
            $resultCurl = $this->sibsCallCurl($url, $postData, $auth);

            if (empty($resultCurl)) {
                throw new \Exception("haven't received a json from curl result");
            }

            $endpointResponse = $resultCurl['RsndAppActCdRspn']['RsndAppActCd'] ?? null;

            if (! (! empty($endpointResponse['Rslt']['RsltStts']) && $endpointResponse['Rslt']['RsltStts'] == 'APPR')) {
                throw new \Exception('Resend SMS Code not approved on SBO');
            }

            sibsLogger::debug('resendSMSCode -> end', sibsConstants::SIBS_API_LOG);

            return $this->responseSuccess('Request approved on SBO');
        } catch(\Exception $e) {
            return $this->responseError([], $e->getMessage(), 400);
        }
    }

    /**
     * getTerminalList.
     *
     * @return string|false
     */
    public function getTerminalList()
    {
        sibsLogger::debug('getTerminalList -> init', sibsConstants::SIBS_API_LOG);

        try {
            $url          = self::URL[$this->serverMode] . '/api/1/management/user/terminallist';
            $index        = 'UsrTermnlLstReq';
            $endpointData = [
                'UsrTermnlLstReq' => [
                    'UsrTermnlFltr' => [
                        'MrchntCd' => null,
                    ],
                    'OrgnFltr' => 'MRPRTL',
                ],
            ];
            $auth = $this->headers['Authorization'];

            $postData   = $this->getPostData($url, $index, $endpointData);
            $resultCurl = $this->sibsCallCurl($url, $postData, $auth);

            if (empty($resultCurl)) {
                throw new \Exception("haven't received a json from curl result");
            }

            $endpointResponse = $resultCurl['UsrTermnlLstRspn']['UsrTermnlLst'] ?? null;

            if (! (! empty($endpointResponse['Rslt']['RsltStts']) && $endpointResponse['Rslt']['RsltStts'] == 'APPR')) {
                throw new \Exception('Get Terminal List not approved on SBO');
            }

            if (! (! empty($endpointResponse['UsrTermnlLstRspnItm']) && is_array($endpointResponse['UsrTermnlLstRspnItm']))) {
                throw new \Exception('None terminal was found for this user');
            }

            $terminalList = [];

            foreach ($endpointResponse['UsrTermnlLstRspnItm'] as $list) {
                $terminalList[] = [
                    'terminal'    => $list['TermnlCd'],
                    'AccptrCd'    => $list['AccptrCd'],
                    'MrchntCd'    => $list['MrchntCd'],
                    'optionData'  => $list['TermnlCd'] . '-' . $list['AccptrCd'] . '-' . $list['MrchntCd'],
                    'description' => (
                        'Terminal: ' . $list['TermnlCd'] . ' - ' . $list['MrchntDsc'] .
                        ' - MrchntCode: ' . $list['MrchntCd']
                    ),
                ];
            }

            sibsLogger::debug('getTerminalList -> end', sibsConstants::SIBS_API_LOG);

            return $this->responseSuccess(['terminalList' => $terminalList]);
        } catch(\Exception $e) {
            return $this->responseError([], $e->getMessage(), 400);
        }
    }

    /**
     * getWebhookList.
     *
     * @return string|false
     */
    public function getWebhookList()
    {
        sibsLogger::debug('getWebhookList -> init', sibsConstants::SIBS_API_LOG);

        try {
            $mrchntCd = $this->content['MrchntCd'] ?? null;

            $url          = self::URL[$this->serverMode] . '/api/1/management/spg/webhooks/list';
            $index        = 'SPGWbHksLstReq';
            $endpointData = [
                'SPGWbHksLst' => [
                    'Ntty' => [
                        'MrchntCd' => $mrchntCd,
                        'AccptrCd' => $this->content['AccptrCd'] ?? null,
                        'TermnlCd' => $this->content['TermnlCd'] ?? null,
                    ],
                ],
            ];
            $auth = $this->headers['Authorization'];

            $postData   = $this->getPostData($url, $index, $endpointData, $mrchntCd);
            $resultCurl = $this->sibsCallCurl($url, $postData, $auth);

            $endpointResponse = $resultCurl['SPGWbHksLstRspn']['SPGWbHksLstRspn'] ?? null;

            if (! (! empty($endpointResponse['Rslt']['RsltStts']) && $endpointResponse['Rslt']['RsltStts'] == 'APPR')) {
                throw new \Exception('Get Terminal List not approved on SBO');
            }

            if (! (! empty($endpointResponse['PmtsNtfctn']) && is_array($endpointResponse['PmtsNtfctn']))) {
                throw new \Exception('None Webhook was found for this user');
            }

            $webhookList = [];

            $clientID     = $this->getClientId();
            $bearerToken  = $this->getToken();

            foreach ($endpointResponse['PmtsNtfctn'] as $list) {
                $terminalCode           = $list['Ntty']['TermnlCd'] ?? null;
                $firstChannelFromList   = $list['ChnnlstpNtfctn'][0] ?? [];
                $webhookSecret          = $firstChannelFromList['Scty']['Key'] ?? null;

                if (! empty($clientID) || ! empty($terminalCode) || ! empty($webhookSecret)) {
                    $webhookList[] = [
                        'clientID'      => $clientID,
                        'terminalCode'  => $terminalCode,
                        'webhookSecret' => $webhookSecret,
                        'bearerToken'   => $bearerToken,
                        'description'   => $firstChannelFromList['Vl'] ?? (
                            'Client ID: ' . $clientID . ' Webhook Secret: ' . $webhookSecret
                        ),
                    ];
                }
            }

            sibsLogger::debug('getWebhookList -> end', sibsConstants::SIBS_API_LOG);

            return $this->responseSuccess(['webhookList' => $webhookList]);
        } catch(\Exception $e) {
            return $this->responseError([], $e->getMessage(), 400);
        }
    }

    /**
     * getClientIdList.
     *
     * @return string|false
     */
    public function getClientId()
    {
        sibsLogger::debug('getClientIdList -> init', sibsConstants::SIBS_API_LOG);

        $mrchntCd = $this->content['MrchntCd'] ?? null;

        $url          = self::URL[$this->serverMode] . '/api/1/management/onboarding/id/get';
        $index        = 'OnBrdgClntIdReq';
        $endpointData = [
            'OnBrdgClntIdData' => [
                'Pwd' => $this->content['pass'] ?? null,
            ],
        ];
        $auth = $this->headers['Authorization'];

        $postData   = $this->getPostData($url, $index, $endpointData, $mrchntCd);
        $resultCurl = $this->sibsCallCurl($url, $postData, $auth);

        $endpointResponse = $resultCurl['OnBrdgClntIdRspn'] ?? null;

        if (! (! empty($endpointResponse['Rslt']['RsltStts']) && $endpointResponse['Rslt']['RsltStts'] == 'APPR')) {
            throw new \Exception('Get Client ID not approved on SBO');
        }

        sibsLogger::debug('getClientIdList -> end', sibsConstants::SIBS_API_LOG);

        return $endpointResponse['UsrCrtClntIdRspnBdy']['ClntId'] ?? null;
    }

    /**
     * getTokenList.
     *
     * @return array
     */
    public function getTokenList()
    {
        sibsLogger::debug('getTokenList -> init', sibsConstants::SIBS_API_LOG);

        $mrchntCd = $this->content['MrchntCd'];

        $url          = self::URL[$this->serverMode] . '/api/1/management/spg/user/list';
        $index        = 'SPGUsrLstReq';
        $endpointData = [];
        $auth         = $this->headers['Authorization'];

        $postData   = $this->getPostData($url, $index, $endpointData, $mrchntCd);
        $resultCurl = $this->sibsCallCurl($url, $postData, $auth);

        $endpointResponse = $resultCurl['SPGUsrLstRspn']['SPGUsrLst'] ?? null;

        if (! (! empty($endpointResponse['Rslt']['RsltStts']) && $endpointResponse['Rslt']['RsltStts'] == 'APPR')) {
            throw new \Exception('Get Token List not approved on SBO');
        }

        if (! (! empty($endpointResponse['SPGUsrLstRpnsItms']) && is_array($endpointResponse['SPGUsrLstRpnsItms']))) {
            throw new \Exception('None Token was found for this user');
        }

        sibsLogger::debug('getTokenList -> end', sibsConstants::SIBS_API_LOG);

        return $endpointResponse['SPGUsrLstRpnsItms'] ?? [];
    }

    /**
     * getTokenDetail.
     *
     * @return string|false
     */
    public function getTokenDetail()
    {
        sibsLogger::debug('getTokenDetail -> init', sibsConstants::SIBS_API_LOG);

        $mrchntCd = $this->content['MrchntCd'];

        $url          = self::URL[$this->serverMode] . '/api/1/management/spg/user/token/detail';
        $index        = 'SPGUsrTknDtlReq';
        $endpointData = [
            'SPGUsrTknDtl' => [
                'Mrchnt'     => $this->content['MrchntCd'] ?? null,
                'Termnl'     => $this->content['TermnlCd'] ?? null,
                'TknId'      => $this->token['TknId'] ?? null,
                'UsrCrdntls' => [
                    'Usrnm' => $this->content['login'] ?? null,
                    'Pwd'   => $this->content['pass'] ?? null,
                ],
            ],
        ];
        $auth = $this->headers['Authorization'];

        $postData   = $this->getPostData($url, $index, $endpointData, $mrchntCd);
        $resultCurl = $this->sibsCallCurl($url, $postData, $auth);

        $endpointResponse = $resultCurl['SPGUsrTknDtlRspn']['SPGUsrTknDtl'] ?? null;

        if (! (! empty($endpointResponse['Rslt']['RsltStts']) && $endpointResponse['Rslt']['RsltStts'] == 'APPR')) {
            throw new \Exception('Get Token Detail not approved on SBO');
        }

        sibsLogger::debug('getTokenDetail -> end', sibsConstants::SIBS_API_LOG);

        return $endpointResponse['TknData'] ?? [];
    }

    /**
     * getToken.
     *
     * @return string
     */
    public function getToken()
    {
        $tokenList = $this->getTokenList();

        if (count($tokenList) > 0) {
            $this->token['Usrnm'] = $tokenList[0]['Usrnm'] ?? null;
            $this->token['TknId'] = $tokenList[0]['Usrs']['TknId'] ?? null;

            foreach ($tokenList as $token) {
                if ($token['Usrs']['Usrnm'] == $this->content['TermnlCd']) {
                    $this->token['Usrnm'] = $token['Usrs']['Usrnm'] ?? null;
                    $this->token['TknId'] = $token['Usrs']['TknId'] ?? null;
                }
            }
        }

        $tokenDetail            = $this->getTokenDetail();
        $this->token['AccsTkn'] = $tokenDetail['AccsTkn'] ?? null;

        return $this->token['AccsTkn'];
    }

    /**
     * getPostData.
     *
     * @param string $url
     * @param string $index
     * @param array $endpointData
     * @param string $mrchntCd
     * @return array
     */
    public function getPostData($url, $index, $endpointData = [], $mrchntCd = null)
    {
        $postData = [
            $index => [
                'Hdr' => [
                    'XchgId'        => '10E6BDBF45994956B6432820F81613DA',
                    'DtTm'          => $this->date,
                    'AppLabl'       => 'WEBMRCHPRT',
                    'Vrsn'          => '1.0.0',
                    'CrrntMrchntCd' => "$mrchntCd",
                ],
                'Trlr' => [
                    'TchnclTrlrData' => [
                        'Device' => [
                            'DeviceID' => $this->deviceID,
                        ],
                        'Sys' => [
                            'SysFmly'     => 'Windows',
                            'SysArchtctr' => 'x64',
                            'SysVrsn'     => '10',
                        ],
                        'Brwsr' => [
                            'BrwsrNm'        => 'Chrome',
                            'BrwsrVrsn'      => '116.0.0.0',
                            'BrwsrFgrPrtcol' => $this->deviceID,
                        ],
                        'Lctn' => [
                            'Lt'          => 0,
                            'Ln'          => 0,
                            'LctnDataSrc' => 'BROWSER',
                        ],
                    ],
                ],
            ],
            'Trlr' => [
                'XchgId'     => '0FA51E12D6544602A0312696E1F12FE7',
                'Tmstmp'     => $this->date,
                'AppLabl'    => 'WEBMRCHPRT',
                'PltfrmData' => [
                    'DeviceId' => $this->deviceID,
                    'Device'   => 'Chrome',
                    'OS'       => 'Windows',
                    'OSVrsn'   => '10',
                    'Lctn'     => [
                        'Lt'          => 0,
                        'Ln'          => 0,
                        'LctnDataSrc' => 'BROWSER',
                    ],
                    'URL' => $url,
                ],
            ],
        ];

        if (! empty($endpointData) && is_array($endpointData)) {
            $postData = array_merge_recursive($postData, [$index => $endpointData]);
        }

        return $postData;
    }

    /**
     * cloneAndCensorArray.
     *
     * @param array $array
     * @param callable $censorCriteria
     * @return string
     */
    public function cloneAndCensorArray($array, $censorCriteria)
    {
        $result = [];

        foreach ($array as $key => $value) {
            if (is_array($value)) {
                $result[$key] = $this->cloneAndCensorArray($value, $censorCriteria);
            } elseif ($censorCriteria($key, $value)) {
                $result[$key] = is_string($value) ? str_repeat('*', strlen($value)) : $value;
            } else {
                $result[$key] = $value;
            }
        }

        return $result;
    }

    /**
     * hideLoginPass.
     *
     * @param array $originalArray
     * @return string
     */
    public function hideLoginPass($originalArray = [])
    {
        $censorCriteria = function ($key, $value) {
            return in_array($key, ['Usrnm', 'Pwd']) || filter_var($value, FILTER_VALIDATE_EMAIL);
        };

        $censoredArray = $this->cloneAndCensorArray($originalArray, $censorCriteria);

        return $censoredArray;
    }

    /**
     * sibsCallCurl.
     *
     * @param tring $url
     * @param array $body
     * @param string $authorization
     * @return mixed
     */
    public function sibsCallCurl($url, $body = [], $authorization = null)
    {
        sibsLogger::debug('curl URL: ' . $url, sibsConstants::SIBS_API_LOG);

        $headers = [
            'Accept: application/json',
            'Content-Type: application/json',
        ];

        if (! empty($authorization)) {
            $headers[] = 'Authorization: Bearer ' . $authorization;
        }

        sibsLogger::debug('curl HEADER: ' . sibsLogger::prettify($headers), sibsConstants::SIBS_API_LOG);

        $parsedBody = $this->hideLoginPass($body);
        sibsLogger::debug('curl BODY: ' . sibsLogger::prettify($parsedBody), sibsConstants::SIBS_API_LOG);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

        $result = curl_exec($ch);

        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        sibsLogger::debug('curl HTTP: ' . $http_code, sibsConstants::SIBS_API_LOG);

        if (curl_errno($ch)) {
            sibsLogger::error('curl ERROR: ' . curl_error($ch), sibsConstants::SIBS_API_LOG);

            return false;
        }

        curl_close($ch);

        $response = json_decode($result, true);

        sibsLogger::debug('curl RESPONSE: ' . sibsLogger::prettify($response), sibsConstants::SIBS_API_LOG);

        return $response;
    }

    /**
     * responseSuccess.
     *
     * @param array $data
     * @param string $message
     * @return string|false
     */
    public function responseSuccess($data = [], $message = 'Success')
    {
        $msgreturn = [
            'success' => 'true',
            'return'  => $data,
            'message' => $message,
            'date'    => $this->date,
        ];

        sibsLogger::debug('returnResponse (code: ' . 200 . ') message: ' . $message, sibsConstants::SIBS_API_LOG);

        return json_encode($msgreturn);
    }

    /**
     * responseError.
     *
     * @param array $data
     * @param string $message
     * @param int $statusCode
     * @return string|false
     */
    public function responseError($data = [], $message = 'Error', $statusCode = 400)
    {
        $msgreturn = [
            'success' => 'false',
            'return'  => $data,
            'message' => $message,
            'date'    => $this->date,
        ];

        sibsLogger::error('returnResponse (code: ' . $statusCode . ') message: ' . $message, sibsConstants::SIBS_API_LOG);

        http_response_code($statusCode);

        return json_encode($msgreturn);
    }
}
