<?php

namespace SiteGround_Migrator\Api_Service;

use SiteGround_Migrator\Helper\Log_Service_Trait;
use SiteGround_Migrator\Helper\Helper;
use SiteGround_Migrator\Database_Service\Database_Service;
use SiteGround_Migrator\Directory_Service\Directory_Service;
/**
 * Handle all request to SiteGround API.
 *
 * This class defines all code necessary to make requests to SiteGround API.
 * It also provides information about the current installtion and authenticaion.
 */
class Api_Service {
	use Log_Service_Trait;

	/**
	 * The Siteground API endpoint.
	 *
	 * @var 1.0.0
	 */
	const API_URL = 'https://wp-transfer-api.sgvps.net/wp-api-v0';

	/**
	 * The max allowed difference between the current
	 * timestamp and when the request was made.
	 *
	 * @var 1.0.0
	 */
	const MAX_TIME_DIFF = 172800;

	/**
	 * Provide information about the current WordPress installation.
	 * The information includes the following:
	 *      - Server IP address
	 *      - PHP Version
	 *      - WordPress version
	 *      - site_url
	 *      - home_url
	 *      - database size
	 *      - the size of wp-content directory
	 *      - files treemap
	 *
	 * @since  1.0.0
	 *
	 * @return array Array containg the information above.
	 */
	public function get_installation_info() {
		global $wp_version;
		global $wpdb;

		if (
			function_exists( 'posix_getpwuid' ) &&
			function_exists( 'posix_geteuid' )
		) {
			$username = posix_getpwuid( posix_geteuid() )['name'];
		} else {
			$username = getenv( 'USERNAME' );
		}

		if ( empty( $username ) ) {
			$username = 'UNKNOWN';
		}

		$uploads_dir = wp_upload_dir();

		$data = array(
			'ip_address'        => $this->get_ip_address(),
			'php_version'       => phpversion(),
			'wordpress_version' => $wp_version,
			'site_url'          => is_multisite() ? network_site_url() : get_site_url(),
			'home_url'          => is_multisite() ? network_home_url() : get_home_url(),
			'database_size'     => Database_Service::get_database_size(),
			'wp_size'           => Directory_Service::get_wordpress_size(),
			'key'               => get_option( 'siteground_migrator_encryption_key' ),
			'base_ident'        => get_option( 'siteground_migrator_temp_directory' ),
			'table_prefix'      => $wpdb->prefix,
			'wp_content_dir'    => WP_CONTENT_DIR,
			'wp_uploads_dir'    => Helper::is_flyweel() ? str_replace( '/www/', '', $uploads_dir['basedir'] ) : str_replace( ABSPATH, '', $uploads_dir['basedir'] ),
			'wp_content_folder' => Helper::is_flyweel() ? str_replace( '/www/', '', WP_CONTENT_DIR ) : str_replace( ABSPATH, '', WP_CONTENT_DIR ),
			'src_hostname'      => gethostname(),
			'sg_host'           => Helper::is_siteground(),
			'src_user'          => $username,
			'src_os'            => PHP_OS,
		);

		if ( function_exists( 'php_uname' ) ) {
			$data['src_uname'] = php_uname();
		}

		return $data;
	}

	/**
	 * Retrieve the server ip address.
	 *
	 * @since  1.0.0
	 *
	 * @return string $ip_address The server IP address.
	 */
	private function get_ip_address() {
		if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
			$ip_address = $_SERVER['HTTP_CLIENT_IP']; // WPCS: sanitization ok.
		} elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
			$ip_address = $_SERVER['HTTP_X_FORWARDED_FOR']; // WPCS: sanitization ok.
		} elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED'] ) ) {
			$ip_address = $_SERVER['HTTP_X_FORWARDED']; // WPCS: sanitization ok.
		} elseif ( ! empty( $_SERVER['HTTP_FORWARDED_FOR'] ) ) {
			$ip_address = $_SERVER['HTTP_FORWARDED_FOR']; // WPCS: sanitization ok.
		} elseif ( ! empty( $_SERVER['HTTP_FORWARDED'] ) ) {
			$ip_address = $_SERVER['HTTP_FORWARDED']; // WPCS: sanitization ok.
		} elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) {
			$ip_address = $_SERVER['REMOTE_ADDR']; // WPCS: sanitization ok.
		} else {
			$ip_address = 'UNKNOWN';
		}

		return sanitize_text_field( wp_unslash( $ip_address ) );
	}

	/**
	 * Sort all request params and build the query string.
	 *
	 * @since  1.0.0
	 *
	 * @param  string $data json encoded representation of the data.
	 *
	 * @return string $api_query Query string containing all data params.
	 */
	private function prepare_verify_request( $data ) {
		$api_query = '';
		// Sort the data keys.
		ksort( $data );

		// Build the query.
		foreach ( $data as $key => $value ) {
			$api_query .= "$key=$value|";
		}

		// Finally return the query.
		return $api_query;
	}

	/**
	 * Make request to SG endpoint.
	 *
	 * @since  1.0.0
	 *
	 * @param  string $api_endpoint API ednpoint.
	 * @param  array  $data         Request body.
	 *
	 * @return array  Array containing the response code and response message.
	 */
	public function do_request( $api_endpoint, $data = array() ) {
		global $wp_version;

		$transfer_id  = get_option( 'siteground_migrator_transfer_id' );
		$transfer_psk = get_option( 'siteground_migrator_transfer_psk' );

		// Add the endpoint command to data.
		$data['cmd'] = $api_endpoint . $transfer_id;

		// Create the authentication hash.
		$auth = sha1( $this->prepare_verify_request( $data ) . $transfer_psk );

		// Prepare the json encoded data for the request.
		$json_data = json_encode(
			array(
				'data' => $data,
			)
		);

		// Send request to SG api.
		$response = wp_remote_post(
		// Add the auth parameter to endpoint.
			add_query_arg( 'auth', $auth, self::API_URL . $api_endpoint . $transfer_id ),
			array(
				'method'    => 'POST',
				'headers'   => array(
					// Get the content length of encoded data.
					'Content-Length' => strlen( $json_data ),
					// Add the content type.
					'Content-type'   => 'application/json',
				),
				'body'      => $json_data,
				'timeout'   => 30,
				'sslverify' => ( 1 === version_compare( 5.3, $wp_version ) ) ? false : true,
			)
		);

		// Return the response containing the status code and the response message.
		return $this->prepare_response_message( $response );
	}

	/**
	 * Prepare response message using the response from the api.
	 *
	 * @since  1.0.0
	 *
	 * @param  array $response The response from the server.
	 *
	 * @return array Array containing the error message and status code.
	 */
	private function prepare_response_message( $response ) {
		// Check for wp errors.
		if ( is_wp_error( $response ) ) {
			return array(
				// The status code.
				'status_code' => 404,
				// The response message.
				'message'     => $response->get_error_message(),
			);
		}

		// Get the status code.
		$status_code = wp_remote_retrieve_response_code( $response );

		// Retrieve the response body.
		$response_body = json_decode( wp_remote_retrieve_body( $response ) );

		// Get the responce body from the old Migrator API transfer_info.
		$transfer_info = ! empty( $response_body->transfer_info ) ? $response_body->transfer_info : '';

		// Get the responce body from new Migrator API data->transfer_info array.
		if ( isset( $response_body->data->transfer_info ) ) {
			$transfer_info = $response_body->data->transfer_info;
		}

		// Return the response.
		return array(
			// The status code.
			'status_code'   => (int) $response_body->status,
			// The response message.
			'message'       => $response_body->message,
			// Transfer info if there is such.
			'transfer_info' => $transfer_info,
		);
	}

	/**
	 * Parse the transfer token to `transfer_id` & `transfer_psk`
	 *
	 * @since  1.0.0
	 */
	public function parse_transfer_token() {
		// Get the transfer token.
		$transfer_token = get_option( 'siteground_migrator_transfer_token' );

		// Parse the token and retrieve the `transfer_id` and `transfer_psk`.
		$parse_result = preg_match( '~(\d{10}-\w{16})-(\w{16})~', $transfer_token, $matches );

		// Bail if there are no matches.
		if ( empty( $parse_result ) ) {
			$this->log_error( 'Error parsing transfer token. Please, make sure it\'s valid!' );
			return false;
		}

		// Set transfer id.
		if ( ! empty( $matches[1] ) ) {
			$this->log_info( 'Updating transfer id.' );
			update_option( 'siteground_migrator_transfer_id', $matches[1] );
		}

		// Set transfer psk.
		if ( ! empty( $matches[2] ) ) {
			$this->log_info( 'Updating transfer psk.' );
			update_option( 'siteground_migrator_transfer_psk', $matches[2] );
		}

		return true;
	}

	/**
	 * Verify the the request is made from SiteGroud
	 * server and has all required params.
	 *
	 * @param string $key Authentication key.
	 *
	 * @return bool True on success.
	 *
	 * @since  1.0.0
	 */
	public function authenticate( $key ) {
		// Bail if any of required parameters is missing.
		if (
			empty( $_GET['transfer_id'] ) ||
			empty( $_GET['ts'] ) ||
			empty( $_GET['auth'] )
		) {
			$this->log_die( '`transfer_id`, `ts` & `auth` parameters are required.' );
		}

		// Get the time diff between current timestamp and `ts` param.
		$time_diff = time() - sanitize_text_field( wp_unslash( $_GET['ts'] ) );

		// Bail if the transfer timestamp is not valid.
		if (
			! is_int( $time_diff ) ||
			$time_diff < -60 ||
			$time_diff > self::MAX_TIME_DIFF
		) {
			$this->log_die( 'Transfer ts is invalid.' );
		}

		// Get `transfer_id`.
		$transfer_id = get_option( 'siteground_migrator_transfer_id' );

		// Bail if the transfer id is not valid.
		if ( $transfer_id !== $_GET['transfer_id'] ) {
			$this->log_die( 'Transfer id is invalid.' );
		}

		// Generate authentication token.
		$auth = sha1( $transfer_id . '-' . $key . '-' . get_option( 'siteground_migrator_transfer_psk' ) . '-' . $_GET['ts'] ); // input var ok; sanitization ok.

		// Bail if the auth param doens't exists or if the auth is not valid.
		if ( $auth !== $_GET['auth'] ) {
			$this->log_die( 'Authentication doesn\'t match.' );
		}

		return true;
	}

	/**
	 * Send json success which means the plugin is installed.
	 *
	 * @since  1.0.0.
	 */
	public function is_plugin_installed() {
		wp_send_json_success( array( 'siteground_migrator' => true ) );
	}

}
