<?php
namespace WPO\WC\PDF_Invoices_Pro\Cloud\Dropbox;

use WPO\WC\PDF_Invoices_Pro\Cloud\Abstracts\Cloud_API;
use WPO\WC\PDF_Invoices_Pro\Vendor\Spatie\Dropbox\Client as Dropbox_Client;
use WPO\WC\PDF_Invoices_Pro\Vendor\GuzzleHttp\Client as Guzzle_Client;
use WPO\WC\PDF_Invoices_Pro\Vendor\GuzzleHttp\RequestOptions as Guzzle_RequestOptions;
use WPO\WC\PDF_Invoices_Pro\Vendor\Composer\CaBundle\CaBundle;


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

if ( ! class_exists( '\\WPO\\WC\\PDF_Invoices_Pro\\Cloud\\Dropbox\\Dropbox_API' ) ) :

/**
 * Dropbox API Class
 * 
 * @class  \WPO\WC\PDF_Invoices_Pro\Cloud\Dropbox\Dropbox_API
 */

class Dropbox_API extends Cloud_API {

	/**
	 * @var bool|string
	 */
	public $slug;

	/**
	 * @var string
	 */
	public $name;
	
	/**
	 * @var string
	 */
	public $authorization_url;
	
	/**
	 * @var string
	 */
	public $refresh_token_url;

	/**
	 * @var bool|string
	 */
	public $access_token;

	/**
	 * @var string
	 */
	private $access_type;

	/**
	 * @var Dropbox_Client
	 */
	protected $dropbox_client;

	/**
	 * Construct
	 * 
	 * @return void
	 */
	public function __construct() {
		// Parent constructor
		parent::__construct();

		// Check if we are dealing with this service API
		if ( 'dropbox' !== parent::$service_slug ) {
			return;
		}

		$this->slug              = parent::$service_slug;
		$this->name              = parent::$service_name;
		$this->access_token      = parent::$service_access_token;
		$this->access_type       = parent::$cloud_storage_settings['dropbox_access_type'] ?? 'app_folder';
		$this->authorization_url = parent::$service_authorization_url . '&access=' . $this->access_type;
		$this->refresh_token_url = parent::$service_refresh_token_url . '&access=' . $this->access_type;

		add_action( 'admin_notices', array( $this, 'auth_success' ) );
		add_action( 'admin_notices', array( $this, 'revoke_necessary' ) );
		add_action( 'admin_notices', array( $this, 'app_folder_changed' ) );
		add_action( 'wpo_wcpdf_before_settings_page', array( $this, 'api_auth_message' ), 10, 2 );
		add_action( 'wp_ajax_wpo_wcpdf_cloud_refresh_token', array( $this, 'ajax_refresh_token' ) );
		
		$this->display_authorization_success_notice();
	}
	
	/**
	 * Gets an instance of the Guzzle HttpClient with preset path to the CA bundle
	 * 
	 * @return object Guzzle_Client
	 */
	private function get_http_client() {
		return new Guzzle_Client( array(
			Guzzle_RequestOptions::VERIFY => apply_filters( 'wpo_wcpdf_dropbox_cabundle_path', CaBundle::getSystemCaRootBundlePath() ),
		) );
	}

	/**
	 * Gets the Dropbox API provider
	 * 
	 * @return Dropbox_Client|false
	 */
	private function get_dropbox_client() {
		if ( empty( $this->access_token ) ) {
			$access_token = $this->get_access_token();
			
			if ( empty( $access_token ) ) {
				self::unlink_service_settings();
				return false;
			}
		}
		
		if ( empty( $this->dropbox_client ) ) {
			$this->dropbox_client = new Dropbox_Client( $this->access_token, $this->get_http_client() );
		}

		return $this->dropbox_client;
	}

	/**
	 * Gets the Dropbox user account informations (name and email)
	 * 
	 * @return string
	 */
	public function get_account_info(): string {
		$dropbox_client = $this->get_dropbox_client();
		
		if ( ! $dropbox_client ) {
			return '';
		}

		try {
			$account = $dropbox_client->getAccountInfo();
			$name    = $account['name']['display_name'];
			$email   = $account['email'];

			return "{$name} [{$email}]";
		} catch ( \Exception $e ) {
			self::log( 'error', "fetching {$this->slug} account info failed: get_account_info()" );
			return '';
		}
	}

	/**
	 * Upload process
	 * 
	 * @param string $filepath
	 * @param \WC_Abstract_Order|null $order
	 * @param string|null $document_type
	 * 
	 * @return array
	 */
	public function upload( string $filepath, ?\WC_Abstract_Order $order = null, ?string $document_type = null ): array {
		if ( empty( $filepath ) ) {
			self::log( 'error', 'empty file path: upload()' );
			return array( 'error' => __( 'File path is empty', 'wpo_wcpdf_pro' ) );
		}
		
		// check if token is expired
		if ( $this->is_token_expired() ) {
			self::log( 'error', 'token expired, refreshing for a new token: upload()' );
			$this->refresh_token();
		}
		
		// check if credentials are set
		if ( ! self::are_account_credentials_set() ) {
			self::log( 'error', 'invalid credentials: upload()' );
			return array( 'error' => __( 'Cloud service credentials not set', 'wpo_wcpdf_pro' ) );
		}

		// get service api client
		$dropbox_client = $this->get_dropbox_client();
		if ( ! $dropbox_client ) {
			self::log( 'error', 'empty service api client: upload()' );
			return array( 'error' => __( 'Could not get service api client', 'wpo_wcpdf_pro' ) );
		}

		$destination_folder = $this->get_destination_folder( $filepath, $order, $document_type );
		$service_name       = $this->name;

		try {
			$filename      = basename( $filepath );
			$file_contents = fopen( $filepath, 'r' );
			$uploaded_file = $dropbox_client->upload( "{$destination_folder}{$filename}", $file_contents, 'overwrite' );

			self::log( 'info', "successfully uploaded {$filename} to {$service_name}: upload()" );

			return array( 'success' => $uploaded_file );
		} catch ( \Exception $e ) {
			$error_response = $e->getMessage();
			$error_message  = "trying to upload to {$service_name}: " . $error_response;
			self::log( 'error', $error_message );

			$decoded_response = $this->maybe_json_decode( $error_response );
			// check for JSON
			if ( $decoded_response ) {
				if ( isset( $decoded_response['error'] ) ) {
					$error     = $decoded_response['error'];
					$unlink_on = array( 'invalid_access_token' );
					
					if ( in_array( $error['.tag'], $unlink_on ) ) {
						update_option( parent::$service_api_settings_option, array() );
					}
				}
			}

			return array( 'error' => $error_message );
		}
	}
	
	/**
	 * Displays a one-time notice informing the user that, due to the new
	 * authorization implementation, the App folder in Dropbox has been renamed.
	 * 
	 * The folder name has changed from:
	 * 'WooCommerce PDF Invoices & Packing Slips'
	 * to:
	 * 'PDF Invoices & Packing Slips for WooCommerce'
	 * 
	 * Going forward, all PDF documents will be stored in the new folder.
	 *
	 * @return void
	 */
	public function app_folder_changed(): void {
		if ( 'app_folder' !== $this->access_type ) {
			return;
		}

		$option_name  = 'wpo_wcpdf_dropbox_app_folder_notice_dismissed';
		$is_dismissed = get_option( $option_name, false );
		$hide_arg     = 'wpo_wcpdf_hide_dropbox_app_folder_notice';
		$nonce_action = 'wcpdf_hide_dropbox_app_folder_notice';

		// Handle dismissal
		if ( isset( $_GET[ $hide_arg ] ) && isset( $_GET['_wpnonce'] ) ) {
			$nonce = sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) );
			if ( wp_verify_nonce( $nonce, $nonce_action ) ) {
				update_option( $option_name, true );
			} else {
				wcpdf_log_error( 'Invalid nonce while hiding Dropbox app folder notice.' );
			}

			wp_redirect( esc_url_raw( admin_url( 'admin.php?page=wpo_wcpdf_options_page&tab=cloud_storage' ) ) );
			exit;
		}

		// Don't show the notice if dismissed
		if ( $is_dismissed ) {
			return;
		}

		// Display the notice
		?>
		<div class="notice notice-info">
			<p><strong><?php echo WPO_WCPDF_Pro()->plugin_name; ?></strong></p>
			<p>
				<?php echo esc_html__( 'Due to a recent change in the Dropbox authorization process, the App folder used by the plugin has been renamed.', 'wpo_wcpdf_pro' ); ?>
				<br>
				<?php
				printf(
					/* translators: 1. old folder name, 2. new folder name */
					esc_html__( 'Your Dropbox folder has changed from %1$s to %2$s.', 'wpo_wcpdf_pro' ),
					'<code>WooCommerce PDF Invoices & Packing Slips</code>',
					'<code>PDF Invoices & Packing Slips for WooCommerce</code>'
				);
				?>
				<br>
				<?php echo esc_html__( 'From now on, all PDF documents will be stored in the new Dropbox folder.', 'wpo_wcpdf_pro' ); ?>
			</p>
			<p>
				<a class="button" href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( $hide_arg => true ) ), $nonce_action ) ); ?>">
					<?php esc_html_e( 'Dismiss this notice', 'wpo_wcpdf_pro' ); ?>
				</a>
			</p>
		</div>
		<?php
	}

}

endif; // class_exists

return new Dropbox_API();