<?php

namespace Sibs\Stargate\Sbo;

use Sibs\Stargate\Config\SibsConfig;
use Sibs\Stargate\Constants\Environments;
use Sibs\Stargate\Constants\Logs;
use Sibs\Stargate\Loggers\SibsLogger;

defined('ABSPATH') || exit;

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

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

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

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

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

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

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

    /**
     * Constructor.
     */
    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 false|string
     */
    public function loginSBO()
    {
        SibsLogger::debug('loginSBO -> init', [
            'source' => Logs::API,
        ]);

        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']) && 'APPR' == $endpointResponse['Rslt']['RsltStts'])) {
                throw new \Exception('Login not approved on SBO');
            }

            SibsLogger::debug('loginSBO -> end', [
                'source' => Logs::API,
            ]);

            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 false|string
     */
    public function activateSMSCode()
    {
        SibsLogger::debug('activateSMSCode -> init', [
            'source' => Logs::API,
        ]);

        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']) && 'APPR' == $endpointResponse['Rslt']['RsltStts'])) {
                throw new \Exception('Activate SMS not approved on SBO');
            }

            SibsLogger::debug('activateSMSCode -> end', [
                'source' => Logs::API,
            ]);

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

    /**
     * resendSMSCode.
     *
     * @return false|string
     */
    public function resendSMSCode()
    {
        SibsLogger::debug('resendSMSCode -> init', [
            'source' => Logs::API,
        ]);

        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']) && 'APPR' == $endpointResponse['Rslt']['RsltStts'])) {
                throw new \Exception('Resend SMS Code not approved on SBO');
            }

            SibsLogger::debug('resendSMSCode -> end', [
                'source' => Logs::API,
            ]);

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

    /**
     * getTerminalList.
     *
     * @return false|string
     */
    public function getTerminalList()
    {
        SibsLogger::debug('getTerminalList -> init', [
            'source' => Logs::API,
        ]);

        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']) && 'APPR' == $endpointResponse['Rslt']['RsltStts'])) {
                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', [
                'source' => Logs::API,
            ]);

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

    /**
     * getWebhookList.
     *
     * @return false|string
     */
    public function getWebhookList()
    {
        SibsLogger::debug('getWebhookList -> init', [
            'source' => Logs::API,
        ]);

        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']) && 'APPR' == $endpointResponse['Rslt']['RsltStts'])) {
                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', [
                'source' => Logs::API,
            ]);

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

    /**
     * getClientIdList.
     *
     * @return false|string
     */
    public function getClientId()
    {
        SibsLogger::debug('getClientIdList -> init', [
            'source' => Logs::API,
        ]);

        $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']) && 'APPR' == $endpointResponse['Rslt']['RsltStts'])) {
            throw new \Exception('Get Client ID not approved on SBO');
        }

        SibsLogger::debug('getClientIdList -> end', [
            'source' => Logs::API,
        ]);

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

    /**
     * getTokenList.
     *
     * @return array
     */
    public function getTokenList()
    {
        SibsLogger::debug('getTokenList -> init', [
            'source' => Logs::API,
        ]);

        $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']) && 'APPR' == $endpointResponse['Rslt']['RsltStts'])) {
            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', [
            'source' => Logs::API,
        ]);

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

    /**
     * getTokenDetail.
     *
     * @return false|string
     */
    public function getTokenDetail()
    {
        SibsLogger::debug('getTokenDetail -> init', [
            'source' => Logs::API,
        ]);

        $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']) && 'APPR' == $endpointResponse['Rslt']['RsltStts'])) {
            throw new \Exception('Get Token Detail not approved on SBO');
        }

        SibsLogger::debug('getTokenDetail -> end', [
            'source' => Logs::API,
        ]);

        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);
        };

        return $this->cloneAndCensorArray($originalArray, $censorCriteria);
    }

    /**
     * sibsCallCurl.
     *
     * @param string $url
     * @param array  $body
     * @param string $authorization
     *
     * @return mixed
     */
    public function sibsCallCurl($url, $body = [], $authorization = null)
    {
        SibsLogger::debug('curl URL: '.$url, [
            'source' => Logs::API,
        ]);

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

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

        SibsLogger::debug('curl HEADER', [
            'source' => Logs::API,
            'headers' => $headers,
        ]);

        $parsedBody = $this->hideLoginPass($body);
        SibsLogger::debug('curl BODY', [
            'source' => Logs::API,
            'body' => $parsedBody,
        ]);

        $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, [
            'source' => Logs::API,
        ]);

        if (curl_errno($ch)) {
            SibsLogger::error('curl ERROR: '.curl_error($ch), [
                'source' => Logs::API,
            ]);

            return false;
        }

        curl_close($ch);

        $response = json_decode($result, true);

        SibsLogger::debug('curl RESPONSE', [
            'source' => Logs::API,
            'response' => $response,
        ]);

        return $response;
    }

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

        SibsLogger::debug('returnResponse (code: '. 200 .') message: '.$message, [
            'source' => Logs::API,
        ]);

        return json_encode($msgreturn);
    }

    /**
     * responseError.
     *
     * @param array  $data
     * @param string $message
     * @param int    $statusCode
     *
     * @return false|string
     */
    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, [
            'source' => Logs::API,
        ]);

        http_response_code($statusCode);

        return json_encode($msgreturn);
    }
}
