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

use WPO\WC\PDF_Invoices_Pro\Cloud\Cloud_API;
use WPO\WC\PDF_Invoices_Pro\Cloud\Dropbox\Helpers\Auth_Helper;
use WPO\WC\PDF_Invoices_Pro\Cloud\Dropbox\Helpers\Token_Helper;
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
 * @version     1.0
 * @category    Class
 * @author      Alexandre Faustino
 */

class Dropbox_API extends Cloud_API {

	private $slug;
	private $name;
	private $access_token;
	private $key;
	private $secret;

	// providers
	protected $auth_helper;
	protected $token_helper;
	protected $dropbox_client;

	/**
	 * Construct
	 * 
	 * @return	void
	 */
	public function __construct()
	{
		// includes
		$this->includes();

		// Parent constructor
		parent::__construct();

		// register service specific settings
		add_filter( 'wpo_wcpdf_settings_fields_cloud_storage', array( $this, 'service_specific_settings' ), 10, 4 );

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

		// set this service slug, name and token
		$this->slug                = parent::$service_slug;
		$this->name                = parent::$service_name;
		$this->access_token        = parent::$service_access_token;

		// Destination folders
		$this->destination_folders = $this->destination_folders();

		// Define key and secret
		$this->access_type = isset( parent::$cloud_storage_settings['access_type'] ) ? parent::$cloud_storage_settings['access_type'] : 'app_folder';
		foreach( $this->destination_folders as $folder_slug => $folder_keys ) {
			if( $this->access_type == $folder_slug ) {
				$this->key    = $folder_keys['key'];
				$this->secret = $folder_keys['secret'];
			}
		}

		// Authorization message
		if ( empty( $this->access_token ) && $this->enabled ) {
			add_action( 'wpo_wcpdf_before_settings_page', array( $this, 'api_auth_message' ), 10, 2 );
		}

		if ( ! empty( $_REQUEST['wpo_wcpdf_'.$this->slug.'_code'] ) ) {
			$this->finish_auth();
		}

		if ( isset($_REQUEST['wpo_wcpdf_'.$this->slug.'_success']) ) {
			add_action( 'wpo_wcpdf_before_settings_page', array( $this, 'auth_success' ), 10, 2 );
		}

		if ( isset($_REQUEST['wpo_wcpdf_'.$this->slug.'_fail']) ) {
			add_action( 'wpo_wcpdf_before_settings_page', array( $this, 'auth_fail' ), 10, 2 );
		}

	}

	public function service_specific_settings( $settings_fields, $page, $option_group, $option_name )
	{
		$service_specific_settings = array(
			array(
				'type'		=> 'setting',
				'id'		=> 'access_type',
				'title'		=> __( 'Destination folder', 'wpo_wcpdf_pro' ),
				'callback'	=> 'select',
				'section'	=> 'cloud_storage_general_settings',
				'args'		=> array(
					'option_name'	=> $option_name,
					'id'			=> 'access_type',
					'options' 		=> array(
						'app_folder'		=> __( 'App folder (restricted access)' , 'wpo_wcpdf_pro' ),
						'root_folder'		=> __( 'Main Dropbox folder' , 'wpo_wcpdf_pro' ),
					),
					'description'	=> __( 'Note: Reauthorization is required after changing this setting!' , 'wpo_wcpdf_pro' ),
					'custom'		=> array(
						'type'			=> 'text_input',
						'custom_option'	=> 'root_folder',
						'args'			=> array(
							'option_name'	=> $option_name,
							'id'			=> 'destination_folder',
							'size'			=> '40',
							'description'	=> __( 'Enter a subfolder to use (optional)', 'wpo_wcpdf_pro' ),
						),
					),
				)
			),
		);

		// register ids for conditional visibility
		$ids = array_column( $service_specific_settings, 'id' );
		add_filter( 'wpo_wcpdf_cloud_service_specific_settings', function( $settings ) use ( $ids ) {
			$settings['dropbox'] = $ids;
			return $settings;
		});

		return $this->append_settings_after_setting_id( $settings_fields, $service_specific_settings, 'cloud_service' );
	}

	/**
	 * Includes another files
	 * 
	 * @return	void
	 */
	private function includes()
	{
		// helpers
		include_once( WPO_WCPDF_Pro()->plugin_path().'/includes/cloud/dropbox/helpers/auth-helper.php' );
		include_once( WPO_WCPDF_Pro()->plugin_path().'/includes/cloud/dropbox/helpers/token-helper.php' );
	}

	/**
	 * Defines the Dropbox destination folders and the key/secret pairs
	 * 
	 * @return	array
	 */
	private function destination_folders()
	{
		return array(
			'app_folder' 	=> array(
				'key'		=> 'p40abi3fysjr6o9',
				'secret'	=> '6abfjn0ddlal3oc'
			),
			'root_folder'	=> array(
				'key'		=> 'wtra5psb2pszzqb',
				'secret'	=> 'ne8j2qo1rtefekr'
			)
		);
	}

	/**
	 * Gets the authorization provider
	 * 
	 * @return	object|bool
	 */
	public function get_auth_helper()
	{
		if( empty( $this->auth_helper ) ) {
			$this->auth_helper = new Auth_Helper(
				[
					'clientId'     => $this->key,
					'clientSecret' => $this->secret,
				],
				[
					'httpClient'   => $this->get_http_client(),
				]
			);
		}

		return $this->auth_helper;
	}

	/**
	 * Gets the token provider
	 * 
	 * @return	object
	 */
	public function get_token_helper()
	{
		if( empty( $this->token_helper ) ) {
			$this->token_helper = new Token_Helper( $this->get_auth_helper(), $this->service_api_settings_option );
		}

		return $this->token_helper;
	}

	/**
	 * Gets the Dropbox API provider
	 * 
	 * @return	object
	 */
	private function get_dropbox_client()
	{
		$this->token_helper = $this->get_token_helper();

		if( empty( $this->dropbox_client ) ) {
			$this->dropbox_client = new Dropbox_Client( $this->token_helper, $this->get_http_client() );
		}

		return $this->dropbox_client;
	}

	/**
	 * Gets an instance of the Guzzle HttpClient with preset path to the CA bundle
	 * 
	 * @return	object Guzzle_Client
	 */
	private function get_http_client()
	{
		$guzzle_client = new Guzzle_Client([
			Guzzle_RequestOptions::VERIFY => apply_filters( 'wpo_wcpdf_dropbox_cabundle_path', CaBundle::getSystemCaRootBundlePath() ),
		]);
		return $guzzle_client;
	}

	/**
	 * Sets the account info in service settings
	 * 
	 * @return	void
	 */
	public function set_account_info( $access_token )
	{
		$service_api_settings = get_option( $this->service_api_settings_option ); // to get the last changes from the TokenProvider

		if ( ! empty( $access_token ) ) {
			$service_api_settings['account_info'] = $this->get_account_info();
		} else {
			unset( $service_api_settings['account_info'] );
		}
		
		update_option( $this->service_api_settings_option, $service_api_settings );
	}

	/**
	 * Gets the Dropbox user account informations (name and email)
	 * 
	 * @return	string|void
	 */
	public function get_account_info()
	{
		try {
			$dropbox_client = $this->get_dropbox_client();
			$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" );
		}
	}

	/**
	 * Generates the Dropbox authorization request URL
	 * 
	 * @return	string
	 */
	public function auth_url()
	{
		$auth_helper = $this->get_auth_helper();
		return esc_url( remove_query_arg( 'redirect_uri', $auth_helper->getAuthorizationUrl() ) );
	}

	/**
	 * Generates the token from the access code provided by the authorization request
	 * 
	 * @return	string
	 */
	public function auth_get_access_token( $auth_code )
	{
		$token_helper = $this->get_token_helper();
		return $token_helper->createToken( $auth_code );
	}

	/**
	 * Finishes the authorization process by saving the token on the Dropbox API settings
	 * 
	 * @return void
	 */
	public function finish_auth()
	{
		$code = sanitize_text_field( $_REQUEST['wpo_wcpdf_'.$this->slug.'_code'] );

		self::log( 'notice', "{$this->slug} authentication code entered: {$code}" );

		// Fetch the AccessToken
		try {
			// get token
			$access_token = $this->auth_get_access_token( $code );

			// set account info
			$this->set_account_info( $access_token );

			self::log( 'info', "{$this->slug} access token successfully created from code: {$code}" );

			// redirect back to where we came from
			if ( ! empty( $_REQUEST['wpo_wcpdf_'.$this->slug.'_return_url'] ) ) {
				$url = $_REQUEST['wpo_wcpdf_'.$this->slug.'_return_url'];
			} else {
				$url = admin_url();
			}

			$url = add_query_arg( 'wpo_wcpdf_'.$this->slug.'_success', $access_token, $url );
			wp_redirect( esc_url_raw( $url ) );

		} catch ( \Exception $e ) {
			self::log( 'error', "{$this->slug} failed to create access token: ".$e->getMessage() );
			$url = add_query_arg( [ 'wpo_wcpdf_'.$this->slug.'_fail', 'true' ], remove_query_arg( 'wpo_wcpdf_'.$this->slug.'_code' ) );
			wp_redirect( esc_url_raw( $url ) );
		} catch ( \TypeError $e ) {
			self::log( 'error', "{$this->slug} failed to create access token: ".$e->getMessage() );
			$url = add_query_arg( [ 'wpo_wcpdf_'.$this->slug.'_fail', 'true' ], remove_query_arg( 'wpo_wcpdf_'.$this->slug.'_code' ) );
			wp_redirect( esc_url_raw( $url ) );
		} catch ( \Error $e ) {
			self::log( 'error', "{$this->slug} failed to create access token: ".$e->getMessage() );
			$url = add_query_arg( [ 'wpo_wcpdf_'.$this->slug.'_fail', 'true' ], remove_query_arg( 'wpo_wcpdf_'.$this->slug.'_code' ) );
			wp_redirect( esc_url_raw( $url ) );
		}
	}

	/**
	 * Dropbox upload process
	 * 
	 * @return	array|bool
	 */
	public function upload( $filepath, $order = null, $document_type = null )
	{	
		if ( empty( $filepath ) ) {
			return false;
		}

		// get service api settings
		$service_api_settings = get_option( $this->service_api_settings_option );
		// check if authorized
		if ( empty($service_api_settings) || !isset($service_api_settings['access_token']) || empty($service_access_token = $service_api_settings['access_token']) ) {
			self::log( 'error', "no access token" );
			return array( 'error' => __( 'Cloud service credentials not set', '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' );
			$dropbox_client = $this->get_dropbox_client();
			$uploaded_file  = $dropbox_client->upload( "{$destination_folder}{$filename}", $file_contents, 'overwrite' );
			
			self::log( 'info', "successfully uploaded {$filename} to {$service_name}" );
			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 );
			
			// check for JSON
			if ( is_string( $error_response ) && $decoded_response = $this->maybe_json_decode( $error_response ) ) {
				if (isset($decoded_response['error'])) {
					$error = $decoded_response['error'];
					$unlink_on = array( 'invalid_access_token' );
					if ( in_array( $error['.tag'], $unlink_on ) ) {
						$this->set_account_info('');
					}
				}
			}

			return array( 'error' => $error_message );
		}
	}

	/**
	 * Gets the destination folder(s)
	 * 
	 * @return	array
	 */
	public function get_destination_folder( $file, $order, $document_type ) {
		$settings = self::$cloud_storage_settings;

		// get destination folder setting
		if ( isset($settings['access_type']) && $settings['access_type'] == 'root_folder' && !empty($settings['destination_folder']) ) {
			// format folder name
			// 1: forward slashes only
			$destination_folder = str_replace("\\", "/", $settings['destination_folder'] );
			// 2: start and end with slash
			$destination_folder = '/'.trim( $destination_folder, '\/').'/';
		} else {
			$destination_folder = '/';
		}

		// append year/month according to setting
		$date = $this->get_file_date_with_fallback( $file );
		$destination_folder = $this->maybe_append_year_month_folders( $destination_folder, $date );

		// filters
		$destination_folder = apply_filters( 'wpo_wcpdf_dropbox_destination_folder', $destination_folder, $order, $document_type ); // legacy (v2.6.6)
		$destination_folder = apply_filters( 'wpo_wcpdf_cloud_service_destination_folder', $destination_folder, $order, $document_type, $file );

		return $destination_folder;
	}

	/**
	 * Displays the authorization notice for Dropbox service
	 * 
	 * @return	resource|void
	 */
	public function api_auth_message( $active_tab, $active_section )
	{
		if ( $active_tab == 'cloud_storage' ) {
			return $this->auth_message( $this->auth_url() );
		}
	}

}

endif; // class_exists

return new Dropbox_API();