<?php

namespace Transferito\Models\Core;

use Transferito\Models\Core\Config;

if (!defined('ABSPATH')) exit;

class Api {

	private $savedAPI;
	private $planDetail;
	private $statusUrl;
	private $createMigrationFreeURL;
	private $createMigrationPaidURL;
	private $startMigrationFreeURL;
	private $startMigrationPaidURL;
	private $directDownloadCheckURL;
	private $envCheckURL;
	private $completeUploadURL;
	private $sslCheckURL;
	private $errorReportingURL;
	private $serverCheckURL;
	private $cPanelAuthURL;

	private $dbConnectionTestURL;
	private $hostingGuideRequestURL;
	private $telemetryURL;

    private $serverVerificationURL;
    private $updateBackupStatus;
    private $updateUploadStatus;

	/**
	 * Keys
	 */
	private $publicKey;
	private $secretKey;

	private $freeUser;
	private $maxSizeExceeded;

	public function __construct()
	{
		$settings = get_option('transferito_settings_option');
		$this->savedAPI = Config::getEndpoint('information');
		$this->planDetail = Config::getEndpoint('detail');
		$this->statusUrl = Config::getEndpoint('status');
		$this->createMigrationFreeURL = Config::getEndpoint('free-migration/create');
		$this->createMigrationPaidURL = Config::getEndpoint('paid-migration/create');
		$this->startMigrationFreeURL = Config::getEndpoint('free-migration/start');
		$this->startMigrationPaidURL = Config::getEndpoint('paid-migration/start');
		$this->envCheckURL = Config::getEndpoint('environment-check');

		$this->completeUploadURL = Config::getEndpoint('upload/complete');
		$this->sslCheckURL = Config::getEndpoint('ssl/check');
		$this->errorReportingURL = Config::getEndpoint('error/reporting');
		$this->cPanelAuthURL = Config::getEndpoint('domain/check');
        $this->serverVerificationURL = Config::getEndpoint('verification');

		$this->hostingGuideRequestURL = Config::getEndpoint('request/hosting-guide');
		$this->telemetryURL = Config::getEndpoint('telemetry');


        // v2 Checks
        $this->dbConnectionTestURL = Config::getEndpoint('check/database');
        $this->directDownloadCheckURL = Config::getEndpoint('check/download');
        $this->serverCheckURL = Config::getEndpoint('check/server');

        $this->updateBackupStatus = Config::getEndpoint('update/status/backup-completed');
        $this->updateUploadStatus = Config::getEndpoint('update/status/upload-started');

		/**
		 * API Keys
		 */
		$publicKey = isset($settings['public_transferito_key']) ? $settings['public_transferito_key'] : '';
		$secretKey = isset($settings['secret_transferito_key']) ? $settings['secret_transferito_key'] : '';
		$this->publicKey = sanitize_text_field($publicKey);
		$this->secretKey = sanitize_text_field($secretKey);

		/**
		 * If user doesn't have API keys set as a free user
		 */
		$this->freeUser = !($this->publicKey && $this->secretKey);
	}

	public function setFreeUser()
	{
		$this->freeUser = true;
	}

	public function setMaxSizeExceeded($maxSizeExceeded)
	{
		$this->maxSizeExceeded = $maxSizeExceeded;
	}

	public function createMigration(array $transferDetail)
	{
		$migrationUrl = $this->freeUser ? $this->createMigrationFreeURL : $this->createMigrationPaidURL;
		return $this->post($migrationUrl, $transferDetail);
	}

	public function startMigration(array $transferDetail)
	{
		$migrationUrl = $this->freeUser ? $this->startMigrationFreeURL : $this->startMigrationPaidURL;
		return $this->post($migrationUrl, $transferDetail);
	}

	public function getStatus($token)
	{
		$transferStatus = $this->statusUrl . '/' . $token;
		return $this->get($transferStatus);
	}

    public function getMaxExceeded()
    {
        return $this->maxSizeExceeded;
    }

	public function planInformation($apiKeys)
	{
		return $this->post(
            $this->planDetail,
            [
                'publicKey' => $apiKeys['publicTransferito_APIKey'],
                'secretKey' => $apiKeys['secretTransferito_APIKey'],
            ],
            true,
            true
        );
	}

	public function canFindSite()
	{
		return $this->post($this->envCheckURL, [ 'url' => TRANSFERITO_UPLOAD_URL ], true);
	}

    public function directDownloadCheck(array $migrationInfo)
    {
        return $this->post($this->directDownloadCheckURL, $migrationInfo, true);
    }

	public function sslCheck()
	{
		return $this->get($this->sslCheckURL, true);
	}

	public function failedMigration($reason)
	{
		$errorInformation = buildErrorReporting($reason);
		return $this->post($this->errorReportingURL, $errorInformation, true);
	}

	public function checkDestinationServerRequirements($migrationInfo)
	{
		return $this->post($this->serverCheckURL, $migrationInfo, true);
	}

	public function cPanelAuth(array $cPanelAuthInfo)
	{
		return $this->post($this->cPanelAuthURL, $cPanelAuthInfo, true);
	}

	public function databaseValidation(array $databaseDetails)
	{
		return $this->post($this->dbConnectionTestURL, $databaseDetails);
	}

	public function completeUpload(array $uploadInfo)
	{
		return $this->post($this->completeUploadURL, $uploadInfo, true);
	}

    public function hostingGuideRequest(array $guideRequestInfo) {
        return $this->post($this->hostingGuideRequestURL, $guideRequestInfo);
    }

    public function pushTelemetry(array $telemetry) {
        return $this->post($this->telemetryURL, $telemetry);
    }

	public function getSavedDetail()
	{
		$result = array(
			'savedFTP'  => [],
			'savedDBS'  => []
		);

		/**
		 * Call api - to get saved details
		 */
		$savedDetail = $this->post($this->savedAPI, array());

		/**
		 * If the response is success
		 * Then create the result array
		 */
		if ($savedDetail['code'] === 200) {
			$response = $savedDetail['message'];
			$result = array(
				'savedFTP'  => isset($response->newFTP) ? $response->newFTP : [],
				'savedDBs'  => isset($response->database) ? $response->database : []
			);
		}

		return $result;
	}

    public function validateServerConnection($url, array $connectionDetails)
    {
        return $this->post($url, $connectionDetails);
    }

    public function createVerificationRequest()
    {
        return $this->post($this->serverVerificationURL, []);
    }

    public function updateBackupStatus($payload)
    {
        return $this->post($this->updateBackupStatus, $payload);
    }

    public function updateUploadStarted($payload)
    {
        return $this->post($this->updateUploadStatus, $payload);
    }

	public function cPanelAvailabilityCheck($domain)
	{
        $request = $this->curlRequest($domain);

		return array(
			'message'   => $request['message'],
			'url'       => $domain,
			'code'      => $request['code']
		);
	}

	private function getRequestHeaders()
	{
		return array(
			'pKey'      => $this->publicKey,
			'sKey'      => $this->secretKey,
			'platform'  => 'wp_plugin',
			'token'     => wp_create_nonce('wp_plugin')
		);
	}

	private function stringifyHeaders($additionalHeaders = [])
	{
		$headers = array_merge($this->getRequestHeaders(), $additionalHeaders);
		$updatedHeaders = [];
		foreach ($headers as $key => $headerValue) {
			$updatedHeaders[] = $key . ':' . $headerValue;
		}
		return $updatedHeaders;
	}

	private function post($url, $body, $returnResultProperty = false, $overrideAPIKeys = false)
	{
		$useFallback = get_transient('transferito_request_fallback');

		/**
		 * Default to use PHP cURL
		 */
		if ($useFallback) {
			return $this->curlRequest($url, $returnResultProperty, $body, 'POST');
		} else {
            /**
             * Set the headers as a variable
             */
            $headers = $this->getRequestHeaders();

            /**
             * Override the API Keys
             * The body must have the array elements publicKey & secretKey
             */
            if ($overrideAPIKeys) {
                $headers['pKey'] = $body['publicKey'];
                $headers['sKey'] = $body['secretKey'];
            }

			/**
			 * Call the endpoint
			 */
			$response = wp_remote_post($url, array(
				'method'    => 'POST',
				'timeout'   => 15000,
				'blocking'  => true,
				'body'      => $body,
				'headers'   => $headers
			));
			return $this->handleWPResponse($response, $returnResultProperty);
		}
	}

	private function get($url, $returnResultProperty = false, $rawResult = false)
	{
		$useFallback = get_transient('transferito_request_fallback');

		/**
		 * Default to use PHP cURL
		 */
		if ($useFallback) {
			return $this->curlRequest($url, $returnResultProperty);
		} else {
			$response = wp_remote_get($url, array(
				'headers'   => $this->getRequestHeaders()
			));
			return $this->handleWPResponse($response, $returnResultProperty, $rawResult);
		}
	}

	private function handleWPResponse($response, $returnResultProperty = false, $rawResult = false)
	{
		/**
		 *
		 */
		$httpCode = wp_remote_retrieve_response_code($response);

		/**
		 * Check on the result of the transaction
		 */
		if (is_wp_error($response)) {
			$responseMessage = $response->get_error_message();
		} else {
			$result = wp_remote_retrieve_body($response);
			$jsonResponse = json_decode($result);
			$responseMessage = ($returnResultProperty) ? $jsonResponse->result : $jsonResponse;

			if ($rawResult) {
				$responseMessage = $result;
			}
		}

		// close the session
		return array(
			'message'   => $responseMessage,
			'code'      => $httpCode,
		);
	}

	private function curlRequest($url, $returnResultProperty = false, $body = [], $method = 'GET')
	{

        $requestOptions = array(
            'method' => $method,
            'headers' => $this->stringifyHeaders(),
        );

        if ($method === 'POST') {
            $requestOptions['body'] = $body;
        }

        $request = wp_remote_request($url, $requestOptions);

        /**
         *
         */
        $httpCode = wp_remote_retrieve_response_code($request);

        /**
         *
         */
        $body = wp_remote_retrieve_body($request);

		return $this->handleFallbackResponse($httpCode, $body, $returnResultProperty);
	}

	private function handleFallbackResponse($httpCode, $response, $returnResultProperty = false)
	{
		/**
		 * Check on the result of the transaction
		 */
		$jsonResponse = json_decode($response);
		$responseMessage = ($returnResultProperty) ? $jsonResponse->result : $jsonResponse;

		// Return formatted response
		return array(
			'message'   => $responseMessage,
			'code'      => $httpCode,
		);
	}

}
