<?php 
namespace PortalPeople\netsuite;

use yii\httpclient\Client;
use yii\authclient\OAuth2;
use yii\base\Exception;
use Yii;
use yii\helpers\Json;
use Firebase\JWT\JWT;


class NetSuiteApiAuth extends OAuth2
{
    public $authUrl  = 'https://4465021.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token';
    public $tokenUrl  = 'https://4465021.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token';
    public $scope = 'rest_webservices';
    public $baseUrl = 'https://4465021.suitetalk.api.netsuite.com';

    protected function initUserAttributes()
    {
        return $this->api('userinfo', 'GET');
    }
    
    /**
     * Authenticates user directly using JSON Web Token (JWT).
     * @see https://tools.ietf.org/html/rfc7515
     * @param string $username
     * @param \yii\authclient\signature\BaseMethod|array $signature signature method or its array configuration.
     * If empty - [[signatureMethod]] will be used.
     * @param array $options additional options. Valid options are:
     *
     * - header: array, additional JWS header parameters.
     * - payload: array, additional JWS payload (message or claim-set) parameters.
     * - signatureKey: string, signature key to be used, if not set - [[clientSecret]] will be used.
     *
     * @param array $params additional request params.
     * @return OAuthToken access token.
     * @since 2.1.3
     */
    public function authenticateUserJwt($username, $signature = null, $options = [], $params = [])
    {
        if (empty($signature)) {
            $signatureMethod = $this->getSignatureMethod();
        } elseif (is_object($signature)) {
            $signatureMethod = $signature;
        } else {
            $signatureMethod = $this->createSignatureMethod($signature);
        }
        $header = isset($options['header']) ? $options['header'] : [];
        $payload = isset($options['payload']) ? $options['payload'] : [];
        
        $header = array_merge([
            'typ' => 'JWT'
        ], $header);

        if (!isset($header['alg'])) {
            $signatureName = $signatureMethod->getName();
            if (preg_match('/^([a-z])[a-z]*\-([a-z])[a-z]*([0-9]+)$/is', $signatureName, $matches)) {
                // convert 'RSA-SHA256' to 'RS256' :
                $signatureName = $matches[1] . $matches[2] . $matches[3];
            }
            $header['alg'] = $signatureName;
        }
        $payload = array_merge([
            'iss' => $username,
            'scope' => $this->scope,
            'aud' => $this->tokenUrl,
            'iat' => time(),
        ], $payload);

        if (!isset($payload['exp'])) {
            $payload['exp'] = $payload['iat'] + 3600;
        }

        $signatureBaseString = JWT::urlsafeB64Encode(Json::encode($header));
        $signatureKey = isset($options['signatureKey']) ? $options['signatureKey'] : $this->clientSecret;
        $assertion = JWT::encode($payload, $signatureKey, $header['alg'], $header['kid'], $header);
        
        $request = $this->createRequest()
            ->setMethod('POST')
            ->setUrl($this->tokenUrl)
            ->setData(array_merge([
                'grant_type' => 'client_credentials',
                'client_id' => $username,
                'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
                'client_assertion' => $assertion,
            ], $params));
            
        $response = $this->sendRequest($request);

        if ($response === null || (is_object($response) && method_exists($response, 'getIsOk') && !$response->getIsOk())) {
            $errorMsg = 'NetSuite API authentication failed.';
            if (is_object($response) && method_exists($response, 'getContent')) {
                $errorMsg .= ' Response: ' . $response->getContent();
            }
            throw new Exception($errorMsg);
        }

        $token = $this->createToken(['params' => $response]);
        $this->setAccessToken($token);

        return $token;
    }   
}
