<?php

namespace FluentFormPro\Integrations\Salesforce;

if (!defined('ABSPATH')) {
    exit; // Exit if accessed directly.
}

class API
{
    protected $clientId = null;

    protected $clientSecret = null;

    protected $instance_url = null;

    protected $accessToken = null;

    protected $callBackUrl = null;

    protected $settings = [];

    public function __construct($settings)
    {
        $this->clientId = $settings['client_id'];
        $this->clientSecret = $settings['client_secret'];
        $this->instance_url = rtrim($settings['instance_url'], '/');
        $this->accessToken = $settings['access_token'];
        $this->callBackUrl = admin_url('?ff_salesforce_auth=true');
        $this->settings = $settings;

        // SECURITY (PRO-38): Salesforce instance URLs are always on salesforce.com / force.com.
        // A settings_manager could otherwise point instance_url at an internal or attacker host and
        // the OAuth Bearer token attached to every request would be exfiltrated there (SSRF +
        // credential exposure). Fail closed by emptying an out-of-policy host, so the derived URLs
        // become relative and wp_remote_* rejects them before any token leaves.
        if ($this->instance_url && !self::isAllowedSalesforceHost($this->instance_url)) {
            $this->instance_url = '';
        }
    }

    protected static function isAllowedSalesforceHost($url)
    {
        $host = strtolower((string) wp_parse_url($url, PHP_URL_HOST));
        $scheme = strtolower((string) wp_parse_url($url, PHP_URL_SCHEME));
        if ('https' !== $scheme || !$host) {
            return false;
        }
        // COMPAT: filterable so an operator can add a legitimate Salesforce host this list does not
        // anticipate (a new TLD/region) without patching, keeping the default-deny posture.
        $suffixes = apply_filters('fluentform/salesforce_allowed_host_suffixes', ['salesforce.com', 'force.com']);
        foreach ((array) $suffixes as $suffix) {
            $suffix = strtolower((string) $suffix);
            if ($host === $suffix || substr($host, -strlen('.' . $suffix)) === '.' . $suffix) {
                return true;
            }
        }
        return false;
    }

    public function getRedirectServerURL()
    {
        // SECURITY (PRO-17): bind the OAuth response to the initiating admin session so a
        // forged callback (attacker's code) cannot graft the attacker's account onto the site.
        return $this->instance_url .
            '/services/oauth2/authorize?response_type=code&client_id=' .
            $this->clientId .
            '&state=' . rawurlencode(wp_create_nonce('ff_salesforce_oauth')) .
            '&redirect_uri=' .
            $this->callBackUrl;
    }

    public function generateAccessToken($code, $settings)
    {
        $url = $settings['is_sandbox'] == 'true' ? 'https://test.salesforce.com/services/oauth2/token' : 'https://login.salesforce.com/services/oauth2/token';

        $response = wp_remote_post($url, [
            'body' => [
                'client_id'     => $this->clientId,
                'client_secret' => $this->clientSecret,
                'grant_type'    => 'authorization_code',
                'redirect_uri'  => $this->callBackUrl,
                'code'          => $code
            ]
        ]);

        if (is_wp_error($response)) {
            return $response;
        }

        $body = wp_remote_retrieve_body($response);
        $body = \json_decode($body, true);

        if (isset($body['error_description'])) {
            return new \WP_Error('invalid_client', $body['error_description']);
        }

        $settings['access_token'] = $body['access_token'];
        $settings['refresh_token'] = $body['refresh_token'];

        return $settings;
    }

    protected function getApiSettings()
    {
        $this->maybeRefreshToken();

        if(!$this->settings['status']) {
            return new \WP_Error('invalid', __('API key is invalid', 'fluentformpro'));
        }

        return [
            'client_id'         => $this->clientId,
            'client_secret'     => $this->clientSecret,
            'callback'          => $this->callBackUrl,
            'access_token'      => $this->settings['access_token'],
            'refresh_token'     => $this->settings['refresh_token']
        ];
    }

    protected function maybeRefreshToken()
    {
        $url = $this->settings['is_sandbox'] == 'true' ? 'https://test.salesforce.com/services/oauth2/token' : 'https://login.salesforce.com/services/oauth2/token';

        $response = wp_remote_post($url, [
            'body' => [
                'client_id' => $this->clientId,
                'client_secret' => $this->clientSecret,
                'grant_type' => 'refresh_token',
                'refresh_token' => $this->settings['refresh_token']
            ]
        ]);

        if (is_wp_error($response)) {
            return new \WP_Error('error', $response[0]['errorCode'] . ': ' .$response[0]['message']);
        }

        $body = wp_remote_retrieve_body($response);
        $body = \json_decode($body, true);

        if (isset($body['error_description'])) {
            return new \WP_Error('invalid_client', $body['error_description']);
        }

        $this->settings['access_token'] = $body['access_token'];
    }

    public function makeRequest($url, $bodyArgs, $type = 'GET')
    {
        $apiSettings = $this->getApiSettings();
        $this->accessToken = $apiSettings['access_token'];

        $request = [];
        if ($type == 'GET') {
            $request = wp_remote_get($url, [
                'headers' => [
                    'Authorization' => " Bearer ". $this->accessToken,
                ]
            ]);
        }

        if ($type == 'POST') {
            $request = wp_remote_post($url, [
                'headers' => [
                    'Authorization' => " Bearer ". $this->accessToken,
                    'Content-Type' => 'application/json'
                ],
                'body' => $bodyArgs
            ]);
        }

        if (is_wp_error($request)) {
            $message = $request->get_error_message();
            return new \WP_Error($request->get_error_code(), $message);
        } elseif ($request['response']['code'] >= 200 && $request['response']['code'] <= 299) {
            return json_decode($request['body'], true);
        }

        $body = wp_remote_retrieve_body($request);
        $body = \json_decode($body, true)[0];

        $error = 'Unknown Error';

        if (!empty($body['errorCode'])) {
            if (isset($body['message'])) {
                $error = $body['message'];
            }
        }

        return new \WP_Error($request['response']['code'], $error);
    }

    public function subscribe($subscriber)
    {
        $url = $this->instance_url . '/services/data/v53.0/sobjects/' . $subscriber['list_id'];
        $post = \json_encode($subscriber['attributes'], JSON_NUMERIC_CHECK);

        $response = $this->makeRequest($url, $post, 'POST');

        return $response;
    }
}
