<?php

/**
 * All Execute Call Class related functions.
 *
 * @package  Inventory
 */
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class CMBIRD_API_Handler_Zoho {

	/**
	 * Configuration array.
	 *
	 * @var array | array[]
	 */
	private array $config;

	/**
	 * WordPress Filesystem object.
	 *
	 * @var WP_Filesystem_Base|null
	 */
	private $filesystem;

	/**
	 * CMBIRD_API_Handler_Zoho constructor.
	 *
	 * Sets up the object properties.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->config = array(
			'ExecutecallZI'   => array(
				'OID'         => get_option( 'cmbird_zoho_inventory_oid' ),
				'ATOKEN'      => get_option( 'cmbird_zoho_inventory_access_token' ),
				'RTOKEN'      => get_option( 'cmbird_zoho_inventory_refresh_token' ),
				'EXPIRESTIME' => get_option( 'cmbird_zoho_inventory_timestamp' ),
			),
			'ExecutecallZCRM' => array(
				'ATOKEN'      => get_option( 'cmbird_zoho_crm_access_token' ),
				'RTOKEN'      => get_option( 'cmbird_zoho_crm_refresh_token' ),
				'EXPIRESTIME' => get_option( 'cmbird_zoho_crm_timestamp' ),
			),
		);
		if ( null === $this->filesystem ) {
			// Ensure FS_METHOD is set to 'direct' if not already defined.
			if ( ! defined( 'FS_METHOD' ) ) {
				define( 'FS_METHOD', 'direct' );
			}
			global $wp_filesystem;
			if ( empty( $wp_filesystem ) ) {
				require_once ABSPATH . '/wp-admin/includes/file.php';
				WP_Filesystem();
			}
			$this->filesystem = $wp_filesystem;
		}
	}

	/**
	 * Get Call to Zoho
	 *
	 * @param string $url - URL to make the GET request to.
	 * @return mixed | WP_Error - Returns the decoded JSON response or a WP_Error object on failure.
	 */
	public function execute_curl_call_get( $url ) {
		// Sleep for .5 sec for each api calls.
		usleep( 500000 );
		$handlefunction = new CMBIRD_Auth_Zoho();

		// if $url contains 'inventory' use zoho_access_token, refresh token and timestamp.
		if ( strpos( $url, 'inventory' ) !== false ) {
			$app_name             = 'zoho_inventory';
			$zoho_access_token    = $this->config['ExecutecallZI']['ATOKEN'];
			$zoho_refresh_token   = $this->config['ExecutecallZI']['RTOKEN'];
			$zoho_timestamp       = $this->config['ExecutecallZI']['EXPIRESTIME'];
			$authorization_header = 'Bearer';
		} else {
			$app_name             = 'zoho_crm';
			$zoho_access_token    = $this->config['ExecutecallZCRM']['ATOKEN'];
			$zoho_refresh_token   = $this->config['ExecutecallZCRM']['RTOKEN'];
			$zoho_timestamp       = $this->config['ExecutecallZCRM']['EXPIRESTIME'];
			$authorization_header = 'Zoho-oauthtoken';
		}

		// Early return if no access token or refresh token is available (not connected).
		if ( empty( $zoho_access_token ) || empty( $zoho_refresh_token ) ) {
			return new WP_Error( 401, 'Zoho not connected. Please connect to Zoho first.' );
		}
		$current_time = strtotime( gmdate( 'Y-m-d H:i:s' ) );
		if ( $zoho_timestamp < $current_time ) {

			$respo_at_js = $handlefunction->get_zoho_refresh_token( $zoho_refresh_token, $app_name );
			if ( is_wp_error( $respo_at_js ) || ! is_array( $respo_at_js ) || ! array_key_exists( 'access_token', $respo_at_js ) ) {
				return new WP_Error( 403, 'Access denied!' );
			}
			$zoho_access_token = $respo_at_js['access_token'];
			if ( 'zoho_inventory' === $app_name ) {
				update_option( 'cmbird_zoho_inventory_access_token', $respo_at_js['access_token'] );
				update_option( 'cmbird_zoho_inventory_timestamp', strtotime( gmdate( 'Y-m-d H:i:s' ) ) + $respo_at_js['expires_in'] );
			} else {
				update_option( 'cmbird_zoho_crm_access_token', $respo_at_js['access_token'] );
				update_option( 'cmbird_zoho_crm_timestamp', strtotime( gmdate( 'Y-m-d H:i:s' ) ) + $respo_at_js['expires_in'] );
			}
		}

		$args = array(
			'headers' => array(
				'Authorization' => "$authorization_header $zoho_access_token",
			),
		);

		$response = wp_remote_get( $url, $args );

		// get the status code of the response.
		$status_code = wp_remote_retrieve_response_code( $response );
		// if code is 429, update the option "zoho_rate_limit_exceeded" to true.
		if ( 429 === $status_code ) {
			update_option( 'cmbird_zoho_rate_limit_exceeded', true );
			update_option( 'cmbird_zoho_rate_limit_time', time() );
		} else {
			update_option( 'cmbird_zoho_rate_limit_exceeded', false );
			delete_option( 'cmbird_zoho_rate_limit_time' );
		}

		// Check if the request was successful.
		if ( ! is_wp_error( $response ) ) {
			// If successful, get the body of the response.
			$body = wp_remote_retrieve_body( $response );

			// Decode JSON response.
			return json_decode( $body );
		} else {
			return $response;
		}
	}

	/**
	 * Post Call to Zoho
	 *
	 * @param string $url - URL to make the POST request to.
	 * @param array  $data - Data to be sent in the POST request.
	 * @return mixed
	 */
	public function execute_curl_call_post( $url, $data ) {
		$handlefunction = new CMBIRD_Auth_Zoho();

		// if $url contains 'inventory' use zoho__access_token, refresh token and timestamp.
		if ( strpos( $url, 'inventory' ) !== false ) {
			$app_name             = 'zoho_inventory';
			$zoho_access_token    = $this->config['ExecutecallZI']['ATOKEN'];
			$zoho_refresh_token   = $this->config['ExecutecallZI']['RTOKEN'];
			$zoho_timestamp       = $this->config['ExecutecallZI']['EXPIRESTIME'];
			$authorization_header = 'Bearer';
		} else {
			$app_name             = 'zoho_crm';
			$zoho_access_token    = $this->config['ExecutecallZCRM']['ATOKEN'];
			$zoho_refresh_token   = $this->config['ExecutecallZCRM']['RTOKEN'];
			$zoho_timestamp       = $this->config['ExecutecallZCRM']['EXPIRESTIME'];
			$authorization_header = 'Zoho-oauthtoken';
		}

		// Early return if no access token or refresh token is available (not connected).
		if ( empty( $zoho_access_token ) || empty( $zoho_refresh_token ) ) {
			return new WP_Error( 401, 'Zoho not connected. Please connect to Zoho first.' );
		}

		$current_time = strtotime( gmdate( 'Y-m-d H:i:s' ) );

		if ( $zoho_timestamp < $current_time ) {

			$respo_at_js = $handlefunction->get_zoho_refresh_token( $zoho_refresh_token, $app_name );
			if ( is_wp_error( $respo_at_js ) || ! is_array( $respo_at_js ) || ! array_key_exists( 'access_token', $respo_at_js ) ) {
				return new WP_Error( 403, 'Access denied!' );
			}

			$zoho_access_token = $respo_at_js['access_token'];
			if ( 'zoho_inventory' === $app_name ) {
				update_option( 'cmbird_zoho_inventory_access_token', $respo_at_js['access_token'] );
				update_option( 'cmbird_zoho_inventory_timestamp', strtotime( gmdate( 'Y-m-d H:i:s' ) ) + $respo_at_js['expires_in'] );
			} else {
				update_option( 'cmbird_zoho_crm_access_token', $respo_at_js['access_token'] );
				update_option( 'cmbird_zoho_crm_timestamp', strtotime( gmdate( 'Y-m-d H:i:s' ) ) + $respo_at_js['expires_in'] );
			}
		}

		$args     = array(
			'body'    => $data,
			'headers' => array(
				'Authorization' => "$authorization_header $zoho_access_token",
			),
		);
		$response = wp_remote_post( $url, $args );

		// get the status code of the response.
		$status_code = wp_remote_retrieve_response_code( $response );
		// if code is 429, update the option "zoho_rate_limit_exceeded" to true.
		if ( 429 === $status_code ) {
			update_option( 'cmbird_zoho_rate_limit_exceeded', true );
			update_option( 'cmbird_zoho_rate_limit_time', time() );
		} else {
			update_option( 'cmbird_zoho_rate_limit_exceeded', false );
			delete_option( 'cmbird_zoho_rate_limit_time' );
		}

		// Check if the request was successful.
		if ( ! is_wp_error( $response ) ) {
			// If successful, get the body of the response.
			$body = wp_remote_retrieve_body( $response );
			// Decode JSON response.
			return json_decode( $body );
		} else {
			// If there was an error, return the entire WP Error object.
			return $response;
		}
	}

	/**
	 * Put Call Zoho
	 *
	 * @param string $url - URL to make the PUT request to.
	 * @param array  $data - Data to be sent in the PUT request.
	 * @return mixed
	 */
	public function execute_curl_call_put( $url, $data ) {

		$handlefunction = new CMBIRD_Auth_Zoho();

		// if $url contains 'inventory' use zoho_inventory_access_token, refresh token and timestamp.
		if ( strpos( $url, 'inventory' ) !== false ) {
			$app_name                     = 'zoho_inventory';
			$zoho_inventory_access_token  = $this->config['ExecutecallZI']['ATOKEN'];
			$zoho_inventory_refresh_token = $this->config['ExecutecallZI']['RTOKEN'];
			$zoho_inventory_timestamp     = $this->config['ExecutecallZI']['EXPIRESTIME'];
			$authorization_header         = 'Bearer';
		} else {
			$app_name                     = 'zoho_crm';
			$zoho_inventory_access_token  = $this->config['ExecutecallZCRM']['ATOKEN'];
			$zoho_inventory_refresh_token = $this->config['ExecutecallZCRM']['RTOKEN'];
			$zoho_inventory_timestamp     = $this->config['ExecutecallZCRM']['EXPIRESTIME'];
			$authorization_header         = 'Zoho-oauthtoken';
		}

		// Early return if no access token or refresh token is available (not connected).
		if ( empty( $zoho_inventory_access_token ) || empty( $zoho_inventory_refresh_token ) ) {
			return new WP_Error( 401, 'Zoho not connected. Please connect to Zoho first.' );
		}

		$current_time = strtotime( gmdate( 'Y-m-d H:i:s' ) );

		if ( $zoho_inventory_timestamp < $current_time ) {

			$respo_at_js = $handlefunction->get_zoho_refresh_token( $zoho_inventory_refresh_token, $app_name );
			if ( is_wp_error( $respo_at_js ) || ! is_array( $respo_at_js ) || ! array_key_exists( 'access_token', $respo_at_js ) ) {
				return new WP_Error( 403, 'Access denied!' );
			}
			$zoho_inventory_access_token = $respo_at_js['access_token'];
			if ( 'zoho_inventory' === $app_name ) {
				update_option( 'cmbird_zoho_inventory_access_token', $respo_at_js['access_token'] );
				update_option( 'cmbird_zoho_inventory_timestamp', strtotime( gmdate( 'Y-m-d H:i:s' ) ) + $respo_at_js['expires_in'] );
			} else {
				update_option( 'cmbird_zoho_crm_access_token', $respo_at_js['access_token'] );
				update_option( 'cmbird_zoho_crm_timestamp', strtotime( gmdate( 'Y-m-d H:i:s' ) ) + $respo_at_js['expires_in'] );
			}
		}

		$args     = array(
			'body'    => $data,
			'headers' => array(
				'Authorization' => "$authorization_header $zoho_inventory_access_token",
			),
			'method'  => 'PUT',
		);
		$response = wp_remote_request( $url, $args );

		// get the status code of the response.
		$status_code = wp_remote_retrieve_response_code( $response );
		// if code is 429, update the option "zoho_rate_limit_exceeded" to true.
		if ( 429 === $status_code ) {
			update_option( 'cmbird_zoho_rate_limit_exceeded', true );
		} else {
			update_option( 'cmbird_zoho_rate_limit_exceeded', false );
		}

		// Check if the request was successful.
		if ( ! is_wp_error( $response ) ) {
			// If successful, get the body of the response.
			$body = wp_remote_retrieve_body( $response );
			// Decode JSON response.
			return json_decode( $body );
		} else {
			return $response;
		}
	}

	/**
	 * Delete Call to Zoho
	 *
	 * @param string $url - URL to make the DELETE request to.
	 * @return mixed
	 */
	public function execute_curl_call_delete( $url ) {
		$handlefunction = new CMBIRD_Auth_Zoho();

		// if $url contains 'inventory' use zoho_inventory_access_token, refresh token and timestamp.
		if ( strpos( $url, 'inventory' ) !== false ) {
			$app_name                     = 'zoho_inventory';
			$zoho_inventory_access_token  = $this->config['ExecutecallZI']['ATOKEN'];
			$zoho_inventory_refresh_token = $this->config['ExecutecallZI']['RTOKEN'];
			$zoho_inventory_timestamp     = $this->config['ExecutecallZI']['EXPIRESTIME'];
			$authorization_header         = 'Bearer';
		} else {
			$app_name                     = 'zoho_crm';
			$zoho_inventory_access_token  = $this->config['ExecutecallZCRM']['ATOKEN'];
			$zoho_inventory_refresh_token = $this->config['ExecutecallZCRM']['RTOKEN'];
			$zoho_inventory_timestamp     = $this->config['ExecutecallZCRM']['EXPIRESTIME'];
			$authorization_header         = 'Zoho-oauthtoken';
		}

		$current_time = strtotime( gmdate( 'Y-m-d H:i:s' ) );

		if ( $zoho_inventory_timestamp < $current_time ) {

			$respo_at_js                 = $handlefunction->get_zoho_refresh_token( $zoho_inventory_refresh_token, $app_name );
			$zoho_inventory_access_token = $respo_at_js['access_token'];
			if ( 'zoho_inventory' === $app_name ) {
				update_option( 'cmbird_zoho_inventory_access_token', $respo_at_js['access_token'] );
				update_option( 'cmbird_zoho_inventory_timestamp', strtotime( gmdate( 'Y-m-d H:i:s' ) ) + $respo_at_js['expires_in'] );
			} else {
				update_option( 'cmbird_zoho_crm_access_token', $respo_at_js['access_token'] );
				update_option( 'cmbird_zoho_crm_timestamp', strtotime( gmdate( 'Y-m-d H:i:s' ) ) + $respo_at_js['expires_in'] );
			}
		}

		$args     = array(
			'headers' => array(
				'Authorization' => "$authorization_header $zoho_inventory_access_token",
			),
			'method'  => 'DELETE',
		);
		$response = wp_remote_request( $url, $args );

		// get the status code of the response.
		$status_code = wp_remote_retrieve_response_code( $response );
		// if code is 429, update the option "zoho_rate_limit_exceeded" to true.
		if ( 429 === $status_code ) {
			update_option( 'cmbird_zoho_rate_limit_exceeded', true );
		} else {
			update_option( 'cmbird_zoho_rate_limit_exceeded', false );
		}

		// Check if the request was successful.
		if ( ! is_wp_error( $response ) ) {
			// If successful, get the body of the response.
			$body = wp_remote_retrieve_body( $response );
			// Decode JSON response.
			return json_decode( $body );
		} else {
			return $response;
		}
	}

	/**
	 *
	 * Get Call Zoho Image
	 *
	 * @param mixed $url - URL of the image.
	 * @param mixed $image_name - Name of the image.
	 * @return WP_Error|string - Returns a WP_Error object on failure, or the URL of the saved image on success.
	 * @throws Exception - Throws exception on failure.
	 */
	public function execute_curl_call_image_get( $url, $image_name ) {
		// $fd = fopen( __DIR__ . '/execute_curl_call_image_get.txt', 'a+' );
		global $wp_filesystem;

		$handlefunction               = new CMBIRD_Auth_Zoho();
		$zoho_inventory_access_token  = $this->config['ExecutecallZI']['ATOKEN'];
		$zoho_inventory_refresh_token = $this->config['ExecutecallZI']['RTOKEN'];
		$zoho_inventory_timestamp     = $this->config['ExecutecallZI']['EXPIRESTIME'];

		$current_time = strtotime( gmdate( 'Y-m-d H:i:s' ) );

		if ( $zoho_inventory_timestamp < $current_time ) {

			$respo_at_js = $handlefunction->get_zoho_refresh_token( $zoho_inventory_refresh_token, 'zoho_inventory' );

			$zoho_inventory_access_token = $respo_at_js['access_token'];
			update_option( 'cmbird_zoho_inventory_access_token', $respo_at_js['access_token'] );
			update_option( 'cmbird_zoho_inventory_timestamp', strtotime( gmdate( 'Y-m-d H:i:s' ) ) + $respo_at_js['expires_in'] );

		}
		$args     = array(
			'headers' => array(
				'Authorization' => "Bearer $zoho_inventory_access_token",
			),
		);
		$response = wp_remote_get( $url, $args );
		// fwrite( $fd, PHP_EOL . 'Response : ' . print_r( $response, true ) );
		// fclose( $fd );

		// Check if the request was successful.
		if ( ! is_wp_error( $response ) ) {
			// If successful, get the body of the response.
			$body = wp_remote_retrieve_body( $response );
			// fwrite( $fd, PHP_EOL . 'Body : ' . print_r( $body, true ) );
			// Set up the upload directory.
			$upload               = wp_upload_dir();
			$absolute_upload_path = $upload['basedir'] . '/zoho_image/';
			$url_upload_path      = $upload['baseurl'] . '/zoho_image/';

			// Generate a unique image name.
			$upload_dir = "$absolute_upload_path/$image_name";
			// remove the file if it exists.
			if ( file_exists( $upload_dir ) ) {
				wp_delete_file( $upload_dir );
			}
			// Create the directory if it doesn't exist/.
			if ( ! is_dir( $absolute_upload_path ) ) {
				wp_mkdir_p( $absolute_upload_path );
			}
			// Save the image file.
			$success = $wp_filesystem->put_contents( $upload_dir, $body );
			if ( ! $success ) {
					wp_delete_file( $upload_dir );
					return '';
			}
			// Use trailingslashit to make sure the URL ends with a single slash.
			return trailingslashit( $url_upload_path ) . $image_name;
		} else {
			// If there was an error, return it as a WP_Error object.
			return $response;
		}
	}

	/**
	 * Downloads a Zoho file response to a temporary uploads directory.
	 *
	 * @param string $url       URL of the file.
	 * @param string $file_name File name to write.
	 * @param string $directory Uploads subdirectory name.
	 * @return WP_Error|string
	 */
	public function execute_curl_call_file_get( $url, $file_name, $directory = 'zoho_files' ) {
		global $wp_filesystem;

		$handlefunction               = new CMBIRD_Auth_Zoho();
		$zoho_inventory_access_token  = $this->config['ExecutecallZI']['ATOKEN'];
		$zoho_inventory_refresh_token = $this->config['ExecutecallZI']['RTOKEN'];
		$zoho_inventory_timestamp     = $this->config['ExecutecallZI']['EXPIRESTIME'];

		$current_time = strtotime( gmdate( 'Y-m-d H:i:s' ) );

		if ( $zoho_inventory_timestamp < $current_time ) {
			$respo_at_js = $handlefunction->get_zoho_refresh_token( $zoho_inventory_refresh_token, 'zoho_inventory' );

			if ( is_wp_error( $respo_at_js ) || ! is_array( $respo_at_js ) || ! isset( $respo_at_js['access_token'], $respo_at_js['expires_in'] ) ) {
				return new WP_Error( 'cmbird_zoho_refresh_failed', 'Unable to refresh Zoho Inventory access token.' );
			}

			$zoho_inventory_access_token = $respo_at_js['access_token'];
			update_option( 'cmbird_zoho_inventory_access_token', $respo_at_js['access_token'] );
			update_option( 'cmbird_zoho_inventory_timestamp', strtotime( gmdate( 'Y-m-d H:i:s' ) ) + $respo_at_js['expires_in'] );
		}

		$args = array(
			'headers' => array(
				'Authorization' => "Bearer $zoho_inventory_access_token",
			),
		);

		$response = wp_remote_get( $url, $args );
		if ( is_wp_error( $response ) ) {
			return $response;
		}

		$status_code = wp_remote_retrieve_response_code( $response );
		if ( 200 > $status_code || 300 <= $status_code ) {
			return new WP_Error( 'cmbird_zoho_file_request_failed', 'Zoho file request did not return a successful response.' );
		}

		$body = wp_remote_retrieve_body( $response );
		if ( '' === $body ) {
			return new WP_Error( 'cmbird_empty_file_response', 'Zoho returned an empty file response.' );
		}

		$upload               = wp_upload_dir();
		$absolute_upload_path = trailingslashit( $upload['basedir'] ) . trim( $directory, '/\\' );
		$sanitized_file_name  = sanitize_file_name( $file_name );
		if ( '' === pathinfo( $sanitized_file_name, PATHINFO_EXTENSION ) ) {
			$header_file_name = $this->get_download_file_name_from_response( $response );
			if ( '' !== $header_file_name ) {
				$sanitized_file_name = $header_file_name;
			}
		}

		if ( '' === $sanitized_file_name ) {
			$sanitized_file_name = uniqid( 'cmbird-file-', false );
		}

		$file_path = trailingslashit( $absolute_upload_path ) . $sanitized_file_name;

		if ( file_exists( $file_path ) ) {
			wp_delete_file( $file_path );
		}

		if ( ! is_dir( $absolute_upload_path ) ) {
			wp_mkdir_p( $absolute_upload_path );
		}

		$success = $wp_filesystem->put_contents( $file_path, $body );
		if ( ! $success ) {
			if ( file_exists( $file_path ) ) {
				wp_delete_file( $file_path );
			}
			return new WP_Error( 'cmbird_file_write_failed', 'Unable to write Zoho file to the uploads directory.' );
		}

		return $file_path;
	}

	/**
	 * Extracts a file name from a download response when available.
	 *
	 * @param array $response WordPress HTTP response array.
	 * @return string
	 */
	private function get_download_file_name_from_response( $response ) {
		$headers = wp_remote_retrieve_headers( $response );
		if ( ! $headers || ! isset( $headers['content-disposition'] ) ) {
			return '';
		}

		$content_disposition = (string) $headers['content-disposition'];
		if ( ! preg_match( '/filename\*?=(?:UTF-8\'\')?"?([^";]+)"?/i', $content_disposition, $matches ) ) {
			return '';
		}

		return sanitize_file_name( rawurldecode( trim( $matches[1] ) ) );
	}
}
