<?php

namespace PortalPeople\netsuite;

use yii\httpclient\Client;
use yii\base\Component;
use yii\base\Exception;
use PortalPeople\netsuite\NetSuiteApiAuth;
use Yii;

/**
 * NetSuite API Client using OAuth 2.0 JWT Authentication
 * 
 * This class provides a client for interacting with NetSuite's RESTlet API
 * It uses the NetSuiteApiAuth component for authentication
 * 
 * @package PortalPeople\netsuite
 */
class NetSuiteClient extends Component {
    /**
     * @var string NetSuite account ID
     */
    public $accountId;
    
    /**
     * @var string Client ID from NetSuite integration record (consumer_key)
     */
    public $clientId;
    
    /**
     * @var string Client secret from NetSuite integration record (consumer_secret)
     */
    public $clientSecret;
    
    /**
     * @var string Certificate ID (Key ID) from NetSuite
     */
    public $certificateId;
    
    /**
     * @var string Path to private key file for JWT signing
     */
    public $privateKeyPath;
    
    /**
     * @var string NetSuite REST API base URL
     */
    public $baseUrl;
    
    /**
     * @var \common\components\NetSuiteApiAuth OAuth client instance
     */
    private $_oauthClient;
    
    /**
     * @var Client HTTP client instance
     */
    private $_httpClient;

    /**
     * @inheritdoc
     */
    public function init()
    {
        parent::init();
        
        if (empty($this->accountId)) {
            throw new Exception('NetSuite account ID is required');
        }
        
        if (empty($this->clientId) || empty($this->clientSecret)) {
            throw new Exception('NetSuite OAuth 2.0 client ID and secret are required');
        }
        
        if (empty($this->certificateId)) {
            throw new Exception('NetSuite certificate ID (Key ID) is required');
        }
        
        if (empty($this->privateKeyPath)) {
            throw new Exception('Private key path is required for JWT authentication');
        }
        
        if (empty($this->baseUrl)) {
            $this->baseUrl = "https://{$this->accountId}.suitetalk.api.netsuite.com/services/rest";
        }
        
        // Initialize OAuth client
        $this->_oauthClient =Yii::$app->authClientCollection->getClient('NetSuite');
        
        // Initialize HTTP client
        $this->_httpClient = new Client(['baseUrl' => $this->baseUrl]);
    }
    
    /**
     * Check if authentication is successful
     * @return bool
     */
    public function isAuthenticated()
    {
        try {
            $token = $this->getAccessToken();
            return !empty($token);
        } catch (\Exception $e) {
            return false;
        }
    }
    
    /**
     * Get access token using JWT assertion
     * 
     * @return string Access token
     * @throws Exception
     */
    public function getAccessToken()
    {
        // Check if we already have a valid token
        $token = $this->_oauthClient->getAccessToken();
        
        if (!$token || empty($token->token)) {
            // Authenticate using JWT
            $this->authenticate();
            $token = $this->_oauthClient->getAccessToken();
        }
        
        return $token ? $token->token : null;
    }
    
    /**
     * Authenticate with NetSuite using JWT
     * 
     * @throws Exception
     */
    protected function authenticate()
    {
        // Resolve private key path
        $privateKeyPath = $this->privateKeyPath;
        if (!file_exists($privateKeyPath)) {
            $privateKeyPath = $_SERVER['DOCUMENT_ROOT'] . $privateKeyPath;
        }
        
        if (!file_exists($privateKeyPath)) {
            throw new Exception("Private key file not found: {$this->privateKeyPath}");
        }
        
        // Read private key
        $privateKey = file_get_contents($privateKeyPath);
        if ($privateKey === false) {
            throw new Exception("Could not read private key file: {$privateKeyPath}");
        }
        
        // Prepare JWT parameters matching the working implementation
        $jwtParams = [
            'header' => [
                'alg' => 'ES256',
                'kid' => $this->certificateId,
                'typ' => 'JWT'
            ],
            'signatureKey' => $privateKey,
        ];
        
        // Authenticate using JWT
        $this->_oauthClient->authenticateUserJwt(
            $this->clientId,
            null,
            $jwtParams,
            []
        );
    }
    
    /**
     * Make HTTP request to NetSuite API
     * 
     * @param string $method HTTP method
     * @param string $endpoint API endpoint
     * @param array $data Request data
     * @param array $params Query parameters
     * @return array Response data
     * @throws Exception
     */
    public function request($method, $endpoint, $data = [], $params = [])
    {
        try {
            $accessToken = $this->getAccessToken();
            
            if (!$accessToken) {
                throw new Exception('Failed to obtain access token');
            }
            
            $request = $this->_httpClient->createRequest()
                ->setMethod($method)
                ->setUrl($endpoint)
                ->setHeaders(['Authorization' => 'Bearer ' . $accessToken]);
            
            if (!empty($params)) {
                $request->addData($params);
            }
            
            if (!empty($data) && in_array(strtoupper($method), ['POST', 'PUT', 'PATCH'])) {
                $request->setFormat(Client::FORMAT_JSON);
                $request->setData($data);
            }
            
            $response = $request->send();
            
            if (!$response->isOk) {
                throw new Exception('NetSuite API request failed: ' . $response->content);
            }
            
            return $response->data;
            
        } catch (\Exception $e) {
            throw new Exception('NetSuite API request failed: ' . $e->getMessage(), $e->getCode(), $e);
        }
    }
    
    /**
     * Perform GET request
     * 
     * @param string $endpoint API endpoint
     * @param array $params Query parameters
     * @return array Response data
     */
    public function get($endpoint, $params = [])
    {
        return $this->request('GET', $endpoint, [], $params);
    }
    
    /**
     * Perform POST request
     * 
     * @param string $endpoint API endpoint
     * @param array $data Request data
     * @param array $params Query parameters
     * @return array Response data
     */
    public function post($endpoint, $data = [], $params = [])
    {
        return $this->request('POST', $endpoint, $data, $params);
    }
    
    /**
     * Perform PUT request
     * 
     * @param string $endpoint API endpoint
     * @param array $data Request data
     * @param array $params Query parameters
     * @return array Response data
     */
    public function put($endpoint, $data = [], $params = [])
    {
        return $this->request('PUT', $endpoint, $data, $params);
    }
    
    /**
     * Perform PATCH request
     * 
     * @param string $endpoint API endpoint
     * @param array $data Request data
     * @param array $params Query parameters
     * @return array Response data
     */
    public function patch($endpoint, $data = [], $params = [])
    {
        return $this->request('PATCH', $endpoint, $data, $params);
    }
    
    /**
     * Perform DELETE request
     * 
     * @param string $endpoint API endpoint
     * @param array $params Query parameters
     * @return array Response data
     */
    public function delete($endpoint, $params = [])
    {
        return $this->request('DELETE', $endpoint, [], $params);
    }
}