<?php

class WPCF7_Integration
{
    private static $instance;
    private $services = array();
    private $categories = array();
    private function __construct()
    {
    }
    public static function get_instance()
    {
        if (empty(self::$instance)) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    public function add_service($name, WPCF7_Service $service)
    {
        $name = sanitize_key($name);
        if (empty($name) or isset($this->services[$name])) {
            return false;
        }
        $this->services[$name] = $service;
    }
    public function add_category($name, $title)
    {
        $name = sanitize_key($name);
        if (empty($name) or isset($this->categories[$name])) {
            return false;
        }
        $this->categories[$name] = $title;
    }
    public function service_exists($name = '')
    {
        if ('' == $name) {
            return (bool) count($this->services);
        } else {
            return isset($this->services[$name]);
        }
    }
    public function get_service($name)
    {
        if ($this->service_exists($name)) {
            return $this->services[$name];
        } else {
            return false;
        }
    }
    public function list_services($args = '')
    {
        $args = wp_parse_args($args, array('include' => array()));
        $singular = false;
        $services = (array) $this->services;
        if (!empty($args['include'])) {
            $services = array_intersect_key($services, array_flip((array) $args['include']));
            if (1 == count($services)) {
                $singular = true;
            }
        }
        if (empty($services)) {
            return;
        }
        $action = wpcf7_current_action();
        foreach ($services as $name => $service) {
            $cats = array_intersect_key($this->categories, array_flip($service->get_categories()));
            ?>
<div class="card<?php 
            echo $service->is_active() ? ' active' : '';
            ?>" id="<?php 
            echo esc_attr($name);
            ?>">
<?php 
            $service->icon();
            ?>
<h2 class="title"><?php 
            echo esc_html($service->get_title());
            ?></h2>
<div class="infobox">
<?php 
            echo esc_html(implode(', ', $cats));
            ?>
</div>
<br class="clear" />

<div class="inside">
<?php 
            if ($singular) {
                $service->display($action);
            } else {
                $service->display();
            }
            ?>
</div>
</div>
<?php 
        }
    }
}
abstract class WPCF7_Service
{
    public abstract function get_title();
    public abstract function is_active();
    public function get_categories()
    {
        return array();
    }
    public function icon()
    {
        return '';
    }
    public function link()
    {
        return '';
    }
    public function load($action = '')
    {
    }
    public function display($action = '')
    {
    }
    public function admin_notice($message = '')
    {
    }
}
class WPCF7_Service_OAuth2 extends WPCF7_Service
{
    protected $client_id = '';
    protected $client_secret = '';
    protected $access_token = '';
    protected $refresh_token = '';
    protected $authorization_endpoint = 'https://example.com/authorization';
    protected $token_endpoint = 'https://example.com/token';
    public function get_title()
    {
        return '';
    }
    public function is_active()
    {
        return !empty($this->refresh_token);
    }
    protected function save_data()
    {
    }
    protected function reset_data()
    {
    }
    protected function get_redirect_uri()
    {
        return admin_url();
    }
    protected function menu_page_url($args = '')
    {
        return menu_page_url('wpcf7-integration', false);
    }
    public function load($action = '')
    {
        if ('auth_redirect' == $action) {
            $code = isset($_GET['code']) ? $_GET['code'] : '';
            if ($code) {
                $this->request_token($code);
            }
            if (!empty($this->access_token)) {
                $message = 'success';
            } else {
                $message = 'failed';
            }
            wp_safe_redirect($this->menu_page_url(array('action' => 'setup', 'message' => $message)));
            exit;
        }
    }
    protected function authorize($scope = '')
    {
        $endpoint = add_query_arg(array('response_type' => 'code', 'client_id' => $this->client_id, 'redirect_uri' => urlencode($this->get_redirect_uri()), 'scope' => $scope), $this->authorization_endpoint);
        if (wp_redirect(esc_url_raw($endpoint))) {
            exit;
        }
    }
    protected function get_http_authorization_header($scheme = 'basic')
    {
        $scheme = strtolower(trim($scheme));
        switch ($scheme) {
            case 'bearer':
                return sprintf('Bearer %s', $this->access_token);
            case 'basic':
            default:
                return sprintf('Basic %s', base64_encode($this->client_id . ':' . $this->client_secret));
        }
    }
    protected function request_token($authorization_code)
    {
        $endpoint = add_query_arg(array('code' => $authorization_code, 'redirect_uri' => urlencode($this->get_redirect_uri()), 'grant_type' => 'authorization_code'), $this->token_endpoint);
        $request = array('headers' => array('Authorization' => $this->get_http_authorization_header('basic')));
        $response = wp_remote_post(esc_url_raw($endpoint), $request);
        $response_code = (int) wp_remote_retrieve_response_code($response);
        $response_body = wp_remote_retrieve_body($response);
        $response_body = json_decode($response_body, true);
        if (WP_DEBUG and 400 <= $response_code) {
            $this->log($endpoint, $request, $response);
        }
        if (401 == $response_code) {
            $this->access_token = null;
            $this->refresh_token = null;
        } else {
            if (isset($response_body['access_token'])) {
                $this->access_token = $response_body['access_token'];
            } else {
                $this->access_token = null;
            }
            if (isset($response_body['refresh_token'])) {
                $this->refresh_token = $response_body['refresh_token'];
            } else {
                $this->refresh_token = null;
            }
        }
        $this->save_data();
        return $response;
    }
    protected function refresh_token()
    {
        $endpoint = add_query_arg(array('refresh_token' => $this->refresh_token, 'grant_type' => 'refresh_token'), $this->token_endpoint);
        $request = array('headers' => array('Authorization' => $this->get_http_authorization_header('basic')));
        $response = wp_remote_post(esc_url_raw($endpoint), $request);
        $response_code = (int) wp_remote_retrieve_response_code($response);
        $response_body = wp_remote_retrieve_body($response);
        $response_body = json_decode($response_body, true);
        if (WP_DEBUG and 400 <= $response_code) {
            $this->log($endpoint, $request, $response);
        }
        if (401 == $response_code) {
            $this->access_token = null;
            $this->refresh_token = null;
        } else {
            if (isset($response_body['access_token'])) {
                $this->access_token = $response_body['access_token'];
            } else {
                $this->access_token = null;
            }
            if (isset($response_body['refresh_token'])) {
                $this->refresh_token = $response_body['refresh_token'];
            }
        }
        $this->save_data();
        return $response;
    }
    protected function remote_request($url, $request = array())
    {
        static $refreshed = false;
        $request = wp_parse_args($request, array());
        $request['headers'] = array_merge($request['headers'], array('Authorization' => $this->get_http_authorization_header('bearer')));
        $response = wp_remote_request(esc_url_raw($url), $request);
        if (401 === wp_remote_retrieve_response_code($response) and !$refreshed) {
            $this->refresh_token();
            $refreshed = true;
            $response = $this->remote_request($url, $request);
        }
        return $response;
    }
    protected function log($url, $request, $response)
    {
        wpcf7_log_remote_request($url, $request, $response);
    }
}