<?php

namespace Sibs\Sibs\Model\Api;

use Exception;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\App\Response\Http as ResponseHttp;
use Magento\Framework\App\ResponseInterface;
use Sibs\Sibs\Api\SibsSBOInterface;
use Sibs\Sibs\Model\SibsConstants;
use Sibs\Sibs\Utils\SibsException;
use Sibs\Sibs\Utils\SibsLog;

/**
 * Class SibsLogWebHook.
 */
class SibsSBO implements SibsSBOInterface
{
    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 \Magento\Framework\App\RequestInterface
     */
    protected $request;

    /**
     * @var \Magento\Framework\App\ResponseInterface
     */
    protected $response;

    protected $date;

    protected $content;

    protected $serverMode;

    protected $deviceID;

    protected $token;

    /**
     * CustomerAddress constructor.
     * @param \Magento\Framework\App\RequestInterface $request
     * @param \Magento\Framework\App\ResponseInterface $response
     */
    public function __construct(
        RequestInterface $request,
        ResponseInterface $response
    ) {
        $this->request      = $request;
        $this->response     = $response;
        $this->date         = date('Y-m-d\TH:i:s.v\Z');
        $this->content      = json_decode($this->request->getContent(), true);
        $this->serverMode   = $this->content['serverMode'] ?? null;
        $this->deviceID     = $this->content['deviceID'] ?? null;
        $this->token        = [
            'Usrnm'     => null,
            'TknId'     => null,
            'AccsTkn'   => null,
        ];

        SibsLog::setNameFile(SibsConstants::SIBS_LOG_SBO);
    }

    /**
     * loginSBO.
     *
     * @return \Magento\Framework\App\ResponseInterface
     */
    public function loginSBO()
    {
        SibsLog::info('loginSBO -> init');

        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 SibsException("haven't received a json from curl result");
            }

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

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

            SibsLog::info('loginSBO -> end');

            $this->responseSuccess([
                'token'     => $endpointResponse['TknData']['AccsTkn'] ?? null,
                'NxtFlow'   => $resultCurl['LgnUsrRspn']['Hdr']['NxtFlow'] ?? null,
            ]);
        } catch(SibsException $e) {
            $this->responseError([], $e->getErrorMessage(), ResponseHttp::STATUS_CODE_400);
        } catch(Exception $e) {
            $this->responseError([], $e->getMessage(), ResponseHttp::STATUS_CODE_500);
        }
    }

    /**
     * activateSMSCode.
     *
     * @return \Magento\Framework\App\ResponseInterface
     */
    public function activateSMSCode()
    {
        SibsLog::info('activateSMSCode -> init');

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

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

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

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

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

            SibsLog::info('activateSMSCode -> end');

            $this->responseSuccess('Request approved on SBO');
        } catch(SibsException $e) {
            $this->responseError([], $e->getErrorMessage(), ResponseHttp::STATUS_CODE_400);
        } catch(Exception $e) {
            $this->responseError([], $e->getMessage(), ResponseHttp::STATUS_CODE_500);
        }
    }

    /**
     * resendSMSCode.
     *
     * @return \Magento\Framework\App\ResponseInterface
     */
    public function resendSMSCode()
    {
        SibsLog::info('resendSMSCode -> init');

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

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

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

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

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

            SibsLog::info('resendSMSCode -> end');

            $this->responseSuccess('Request approved on SBO');
        } catch(SibsException $e) {
            $this->responseError([], $e->getErrorMessage(), ResponseHttp::STATUS_CODE_400);
        } catch(Exception $e) {
            $this->responseError([], $e->getMessage(), ResponseHttp::STATUS_CODE_500);
        }
    }

    /**
     * getTerminalList.
     *
     * @return \Magento\Framework\App\ResponseInterface
     */
    public function getTerminalList()
    {
        SibsLog::info('getTerminalList -> init');

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

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

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

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

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

            if (! (! empty($endpointResponse['UsrTermnlLstRspnItm']) && is_array($endpointResponse['UsrTermnlLstRspnItm']))) {
                throw new SibsException('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']
                    ),
                ];
            }

            SibsLog::info('getTerminalList -> end');

            $this->responseSuccess(['terminalList' => $terminalList]);
        } catch(SibsException $e) {
            $this->responseError([], $e->getErrorMessage(), ResponseHttp::STATUS_CODE_400);
        } catch(Exception $e) {
            $this->responseError([], $e->getMessage(), ResponseHttp::STATUS_CODE_500);
        }
    }

    /**
     * getWebhookList.
     *
     * @return \Magento\Framework\App\ResponseInterface
     */
    public function getWebhookList()
    {
        SibsLog::info('getWebhookList -> init');

        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->request->getHeader('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 SibsException('Get Terminal List not approved on SBO');
            }

            if (! (! empty($endpointResponse['PmtsNtfctn']) && is_array($endpointResponse['PmtsNtfctn']))) {
                throw new SibsException('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
                        ),
                    ];
                }
            }

            SibsLog::info('getWebhookList -> end');

            $this->responseSuccess(['webhookList' => $webhookList]);
        } catch(SibsException $e) {
            $this->responseError([], $e->getErrorMessage(), ResponseHttp::STATUS_CODE_400);
        } catch(Exception $e) {
            $this->responseError([], $e->getMessage(), ResponseHttp::STATUS_CODE_500);
        }
    }

    /**
     * getClientIdList.
     */
    public function getClientId()
    {
        SibsLog::info('getClientIdList -> init');

        try {
            $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->request->getHeader('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 SibsException('Get Client ID not approved on SBO');
            }

            SibsLog::info('getClientIdList -> end');

            return $endpointResponse['UsrCrtClntIdRspnBdy']['ClntId'] ?? null;
        } catch(SibsException $e) {
            $this->responseError([], $e->getErrorMessage(), ResponseHttp::STATUS_CODE_400);
        } catch(Exception $e) {
            $this->responseError([], $e->getMessage(), ResponseHttp::STATUS_CODE_500);
        }
    }

    /**
     * getTokenList.
     */
    public function getTokenList()
    {
        SibsLog::info('getTokenList -> init');

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

            $url          = self::URL[$this->serverMode] . '/api/1/management/spg/user/list';
            $index        = 'SPGUsrLstReq';
            $endpointData = [];
            $auth         = $this->request->getHeader('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 SibsException('Get Token List not approved on SBO');
            }

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

            SibsLog::info('getTokenList -> end');

            return $endpointResponse['SPGUsrLstRpnsItms'] ?? [];
        } catch(SibsException $e) {
            $this->responseError([], $e->getErrorMessage(), ResponseHttp::STATUS_CODE_400);
        } catch(Exception $e) {
            $this->responseError([], $e->getMessage(), ResponseHttp::STATUS_CODE_500);
        }
    }

    /**
     * getTokenDetail.
     */
    public function getTokenDetail()
    {
        SibsLog::info('getTokenDetail -> init');

        try {
            $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->request->getHeader('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 SibsException('Get Token List not approved on SBO');
            }

            SibsLog::info('getTokenDetail -> end');

            return $endpointResponse['TknData'] ?? [];
        } catch (SibsException $e) {
            $this->responseError([], $e->getErrorMessage(), ResponseHttp::STATUS_CODE_400);
        } catch (Exception $e) {
            $this->responseError([], $e->getMessage(), ResponseHttp::STATUS_CODE_500);
        }
    }

    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'];
    }

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

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

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

    public function sibsCallCurl($url, $body = [], $authorization = null)
    {
        SibsLog::info('curl URL: ' . $url);

        $headers = [
            'Accept: application/json',
            'Content-Type: application/json',
        ];
        SibsLog::info('curl HEADER: ' . SibsLog::prettify($headers));

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

        $parsedBody = $this->hideLoginPass($body);
        SibsLog::info('curl BODY: ' . SibsLog::prettify($parsedBody));

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Accept: application/json',
        ]);
        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);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

        $result = curl_exec($ch);

        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        SibsLog::warn('curl HTTP: ' . $http_code, true);

        if (curl_errno($ch)) {
            SibsLog::err('curl ERROR: ' . curl_error($ch));

            return false;
        }

        curl_close($ch);

        $response = json_decode($result, true);

        SibsLog::info('curl RESPONSE: ' . SibsLog::prettify($response));

        return $response;
    }

    public function responseSuccess($data = [], $message = 'Success')
    {
        $statusCode = ResponseHttp::STATUS_CODE_200;

        $this->response->getHeaders()
            ->addHeaderLine('Content-Type', 'application/json');

        $this->response
            ->setStatusCode($statusCode)
            ->setContent(json_encode([
                'message'  => $message,
                'data'     => $data,
            ]));

        $this->response->send();

        die();
    }

    public function responseError($data = [], $message = 'Error', $statusCode = ResponseHttp::STATUS_CODE_400)
    {
        $this->response->getHeaders()
            ->addHeaderLine('Content-Type', 'application/json');

        $this->response
            ->setStatusCode($statusCode)
            ->setContent(json_encode([
                'code'      => $statusCode,
                'message'   => $message,
                'data'      => $data,
            ]));

        $this->response->send();

        die();
    }
}
