<?php
namespace WP_science\Engine\CDN\scienceCDN;

use WP_Error;

/**
 * Class to Interact with the scienceCDN API
 */
class APIClient {
	const scienceCDN_API = 'https://sciencecdn.me/api/';

	/**
	 * Gets current scienceCDN subscription data from cache if it exists
	 *
	 * Else do a request to the API to get fresh data
	 *
	 * @since 3.5
	 *
	 * @return array
	 */
	public function get_subscription_data() {
		$status = get_transient( 'sciencecdn_status' );

		if ( false !== $status ) {
			return $status;
		}

		return $this->get_remote_subscription_data();
	}

	/**
	 * Gets fresh scienceCDN subscription data from the API
	 *
	 * @since 3.5
	 *
	 * @return array
	 */
	private function get_remote_subscription_data() {
		$default = [
			'id'                            => 0,
			'is_active'                     => false,
			'cdn_url'                       => '',
			'subscription_next_date_update' => 0,
			'subscription_status'           => 'cancelled',
		];

		$token = get_option( 'sciencecdn_user_token' );

		if ( empty( $token ) ) {
			return $default;
		}

		$args = [
			'headers' => [
				'Authorization' => 'Token ' . $token,
			],
		];

		$response = wp_remote_get(
			self::scienceCDN_API . 'website/search/?url=' . home_url(),
			$args
		);

		if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
			$this->set_status_transient( $default, 3 * MINUTE_IN_SECONDS );

			return $default;
		}

		$data = wp_remote_retrieve_body( $response );

		if ( empty( $data ) ) {
			$this->set_status_transient( $default, 3 * MINUTE_IN_SECONDS );

			return $default;
		}

		$data = json_decode( $data, true );
		$data = array_intersect_key( (array) $data, $default );
		$data = array_merge( $default, $data );

		$this->set_status_transient( $data, WEEK_IN_SECONDS );

		return $data;
	}

	/**
	 * Sets the scienceCDN status transient with the provided value
	 *
	 * @since 3.5
	 *
	 * @param array $value Transient value.
	 * @param int   $duration Transient duration.
	 * @return void
	 */
	private function set_status_transient( $value, $duration ) {
		set_transient( 'sciencecdn_status', $value, $duration );
	}

	/**
	 * Gets pricing & promotion data for scienceCDN from cache if it exists
	 *
	 * Else do a request to the API to get fresh data
	 *
	 * @since 3.5
	 *
	 * @return array|WP_Error
	 */
	public function get_pricing_data() {
		$pricing = get_transient( 'sciencecdn_pricing' );

		if ( false !== $pricing ) {
			return $pricing;
		}

		return $this->get_remote_pricing_data();
	}

	/**
	 * Gets fresh pricing & promotion data for scienceCDN
	 *
	 * @since 3.5
	 *
	 * @return array|WP_Error
	 */
	private function get_remote_pricing_data() {
		$response = wp_remote_get( self::scienceCDN_API . 'pricing' );

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

		if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
			return $this->get_wp_error( __( 'We could not fetch the current price because scienceCDN API returned an unexpected error code.', 'science' ) );
		}

		$data = wp_remote_retrieve_body( $response );

		if ( empty( $data ) ) {
			return $this->get_wp_error( __( 'scienceCDN is not available at the moment. Please retry later.', 'science' ) );
		}

		$data = json_decode( $data, true );

		set_transient( 'sciencecdn_pricing', $data, 6 * HOUR_IN_SECONDS );

		return $data;
	}

	/**
	 * Gets a new WP_Error instance
	 *
	 * @since 3.5
	 *
	 * @param string $message Error message.
	 *
	 * @return WP_Error
	 */
	private function get_wp_error( string $message ) {
		return new WP_Error( 'sciencecdn_error', $message );
	}

	/**
	 * Sends a request to the API to purge the CDN cache
	 *
	 * @since 3.5
	 *
	 * @return array
	 */
	public function purge_cache_request() {
		$subscription = $this->get_subscription_data();
		$status       = 'error';

		if ( ! isset( $subscription['id'] ) || 0 === $subscription['id'] ) {
			return [
				'status'  => $status,
				'message' => __( 'scienceCDN cache purge failed: Missing identifier parameter.', 'science' ),
			];
		}

		$token = get_option( 'sciencecdn_user_token' );

		if ( empty( $token ) ) {
			return [
				'status'  => $status,
				'message' => __( 'scienceCDN cache purge failed: Missing user token.', 'science' ),
			];
		}

		$args = [
			'method'  => 'DELETE',
			'headers' => [
				'Authorization' => 'Token ' . $token,
			],
		];

		$response = wp_remote_request(
			self::scienceCDN_API . 'website/' . $subscription['id'] . '/purge/',
			$args
		);

		if ( is_wp_error( $response ) ) {
			return [
				'status'  => $status,
				'message' => $response->get_error_message(),
			];
		}

		if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
			return [
				'status'  => $status,
				'message' => __( 'scienceCDN cache purge failed: The API returned an unexpected response code.', 'science' ),
			];
		}

		$data = wp_remote_retrieve_body( $response );

		if ( empty( $data ) ) {
			return [
				'status'  => $status,
				'message' => __( 'scienceCDN cache purge failed: The API returned an empty response.', 'science' ),
			];
		}

		$data = json_decode( $data );

		if ( ! isset( $data->success ) ) {
			return [
				'status'  => $status,
				'message' => __( 'scienceCDN cache purge failed: The API returned an unexpected response.', 'science' ),
			];
		}

		if ( ! $data->success ) {
			return [
				'status'  => $status,
				'message' => sprintf(
					// translators: %s = message returned by the API.
					__( 'scienceCDN cache purge failed: %s.', 'science' ),
					isset( $data->message ) ? $data->message : ''
				),
			];
		}

		return [
			'status'  => 'success',
			'message' => __( 'scienceCDN cache purge successful.', 'science' ),
		];
	}

	/**
	 * Filter the arguments used in an HTTP request, to make sure our user token has not been overwritten
	 * by some other plugin.
	 *
	 * @since  3.5
	 *
	 * @param  array  $args An array of HTTP request arguments.
	 * @param  string $url  The request URL.
	 * @return array
	 */
	public function preserve_authorization_token( $args, $url ) {
		if ( strpos( $url, self::scienceCDN_API ) === false ) {
			return $args;
		}

		if ( empty( $args['headers']['Authorization'] ) && self::scienceCDN_API . 'pricing' === $url ) {
			return $args;
		}

		$token = get_option( 'sciencecdn_user_token' );

		if ( empty( $token ) ) {
			return $args;
		}

		$value = 'token ' . $token;

		if ( isset( $args['headers']['Authorization'] ) && $value === $args['headers']['Authorization'] ) {
			return $args;
		}

		$args['headers']['Authorization'] = $value;

		return $args;
	}
}
