<?php
/**
 * API Base Class
 *
 * @package     deliciousbrains
 * @subpackage  api/base
 * @copyright   Copyright (c) 2015, Delicious Brains
 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
 * @since       0.1
 */

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

/**
 * Delicious_Brains_API_Base Class
 *
 * This class handles communication with the Delicious Brains WooCommerce API
 *
 * @since 0.1
 */
class Delicious_Brains_API_Base extends Delicious_Brains_API {

	/**
	 * @var Delicious_Brains_API_Plugin
	 */
	public $plugin;

	/**
	 * @var string
	 */
	public $home_url;

	/**
	 * @param Delicious_Brains_API_Plugin $plugin
	 */
	function __construct( Delicious_Brains_API_Plugin $plugin ) {
		$this->plugin   = $plugin;
		$this->home_url = $this->get_home_url();

		parent::__construct();
	}

	/**
	 * Get home URL.
	 *
	 * @return string
	 */
	private function get_home_url() {
		$home_url = get_option( 'home' );

		if ( is_multisite() && $this->plugin->is_network_activated ) {
			// Make sure always use the network URL in API communication
			$current_site = get_current_site();
			$home_url     = 'http://' . $current_site->domain;
		}

		return untrailingslashit( set_url_scheme( $home_url, 'http' ) );
	}

	/**
	 * Generate the API URL
	 *
	 * @param string $request
	 * @param array  $args
	 *
	 * @return string
	 */
	function get_url( $request, $args = array() ) {
		$url                       = $this->api_url;
		$args['request']           = $request;
		$args['product']           = $this->plugin->slug;
		$args['version']           = $this->plugin->version;
		$args['locale']            = urlencode( get_locale() );
		$args['php_version']       = urlencode( phpversion() );
		$args['wordpress_version'] = urlencode( get_bloginfo( 'version' ) );

		$args = apply_filters( $this->plugin->prefix . '_' . $request . '_request_args', $args );

		if ( false !== get_site_transient( 'dbrains_temporarily_disable_ssl' ) && 0 === strpos( $this->api_url, 'https://' ) ) {
			$url = substr_replace( $url, 'http', 0, 5 );
		}

		$url = add_query_arg( $args, $url );

		return esc_url_raw( $url );
	}

	/**
	 * Main function for communicating with the Delicious Brains API.
	 *
	 * @param string $request
	 * @param array  $args
	 *
	 * @return mixed
	 */
	function api_request( $request, $args = array() ) {
		if ( ( $check = $this->check_api_down() ) ) {
			return $check;
		}

		$url      = $this->get_url( $request, $args );
		$response = $this->get( $url );

		if ( is_wp_error( $response ) || (int) $response['response']['code'] < 200 || (int) $response['response']['code'] > 399 ) {
			// Couldn't connect successfully to the API
			$this->log_error( $response );

			if ( true === $this->is_api_down() ) {
				// API is down
				return $this->check_api_down();
			}

			return $this->connection_failed_response();
		}

		return $response['body'];
	}

	/**
	 * Return a warning notice if the API is down as a response to an API request
	 *
	 * @return bool|mixed|string|void
	 */
	function check_api_down() {
		$trans = get_site_transient( 'dbrains_api_down' );

		if ( false !== $trans ) {
			$api_down_message = sprintf( '<div class="dbrains-api-down updated warning inline-message">%s</div>', $trans );

			return json_encode( array( 'dbrains_api_down' => $api_down_message ) );
		}

		return false;
	}

	/**
	 * Return a connection failed notice as a response to an API request
	 *
	 * @return string
	 */
	protected function connection_failed_response() {
		$connection_failed_message = __( '<strong>Could not connect to deliciousbrains.com</strong> &mdash; You will not receive update notifications or be able to activate your license until this is fixed.' );
		$connection_failed_message .= '</p><p>';

		if ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) && WP_HTTP_BLOCK_EXTERNAL ) {
			$url_parts = parse_url( $this->api_base );
			$host      = $url_parts['host'];
			if ( ! defined( 'WP_ACCESSIBLE_HOSTS' ) || strpos( WP_ACCESSIBLE_HOSTS, $host ) === false ) {
				$connection_failed_message .= sprintf( __( 'We\'ve detected that <code>WP_HTTP_BLOCK_EXTERNAL</code> is enabled and the host <strong>%1$s</strong> has not been added to <code>WP_ACCESSIBLE_HOSTS</code>. Please disable <code>WP_HTTP_BLOCK_EXTERNAL</code> or add <strong>%1$s</strong> to <code>WP_ACCESSIBLE_HOSTS</code> to continue. <a href="%2$s" target="_blank">More information</a>' ), esc_attr( $host ), 'https://deliciousbrains.com/wp-migrate-db-pro/doc/wp_http_block_external/' );
			}
		} else {
			$disable_ssl_url           = $this->admin_url( $this->plugin->settings_url_path . '&nonce=' . wp_create_nonce( $this->plugin->prefix . '-disable-ssl' ) . '&' . $this->plugin->prefix . '-disable-ssl=1' );
			$connection_failed_message .= sprintf( __( 'This issue is often caused by an improperly configured SSL server (https). We recommend <a href="%1$s" target="_blank" class="help">fixing the SSL configuration on your server</a>, but if you need a quick fix you can %2$s' ), 'https://deliciousbrains.com/wp-migrate-db-pro/doc/could-not-connect-deliciousbrains-com/', sprintf( '<a href="%1$s" class="temporarily-disable-ssl">%2$s</a>', $disable_ssl_url, __( 'temporarily disable SSL for connections to deliciousbrains.com.' ) ) );
		}

		return json_encode( array( 'errors' => array( 'connection_failed' => $connection_failed_message ) ) );
	}

	/**
	 * Is the Delicious Brains API down?
	 *
	 * If not available then a 'dbrains_api_down' transient will be set with an appropriate message.
	 *
	 * @return bool
	 */
	protected function is_api_down() {
		if ( false !== get_site_transient( 'dbrains_api_down' ) ) {
			return true;
		}

		$response = $this->get( $this->api_status_url );

		// Can't get to api status url so fall back to normal failure handling.
		if ( is_wp_error( $response ) || 200 != (int) $response['response']['code'] || empty( $response['body'] ) ) {
			return false;
		}

		$json = json_decode( $response['body'], true );

		// Can't decode json so fall back to normal failure handling.
		if ( ! $json ) {
			return false;
		}

		// Decoded JSON data doesn't seem to be the format we expect or is not down, so fall back to normal failure handling.
		if ( ! isset( $json['api']['status'] ) || 'down' != $json['api']['status'] ) {
			return false;
		}

		$message = $this->get_down_message( $json['api'] );

		set_site_transient( 'dbrains_api_down', $message, $this->transient_retry_timeout );

		return true;
	}

	/**
	 * Form the error message about the API being down
	 *
	 * @param array $response
	 *
	 * @return string
	 */
	protected function get_down_message( $response ) {
		$message = __( "<strong>Delicious Brains API is Down — </strong>Unfortunately we're experiencing some problems with our server." );

		if ( ! empty( $response['updated'] ) ) {
			$updated     = $response['updated'];
			$updated_ago = sprintf( _x( '%s ago', 'ex. 2 hours ago' ), human_time_diff( strtotime( $updated ) ) );
		}

		if ( ! empty( $response['message'] ) ) {
			$message .= '<br />';
			$message .= __( "Here's the most recent update on its status" );
			if ( ! empty( $updated_ago ) ) {
				$message .= ' (' . $updated_ago . ')';
			}
			$message .= ': <em>' . $response['message'] . '</em>';
		}

		return $message;
	}

	/**
	 * Default request arguments passed to an HTTP request
	 *
	 * @return array
	 *
	 * @see wp_remote_request() For more information on the available arguments.
	 */
	protected function get_default_request_args() {
		return array(
			'timeout'   => 10,
			'blocking'  => true,
			'sslverify' => $this->verify_ssl(),
		);
	}

	/**
	 * Retrieve the url to the admin area for the site.
	 * Handles if the plugin is a network activated plugin.
	 *
	 * @param string $path Optional path relative to the admin url
	 *
	 * @return string|void
	 */
	public function admin_url( $path ) {
		if ( $this->plugin->is_network_activated ) {
			$url = network_admin_url( $path );
		} else {
			$url = admin_url( $path );
		}

		return $url;
	}

	/**
	 * Error log method
	 *
	 * phpcs:disable WordPress.PHP.DevelopmentFunctions
	 *
	 * @param mixed $error
	 * @param bool  $additional_error_var
	 */
	function log_error( $error, $additional_error_var = false ) {
		error_log( print_r( $error, true ) );
	}

	/**
	 * Use SSL verification for requests
	 *
	 * @return bool
	 */
	function verify_ssl() {
		return false;
	}
}
