<?php
namespace WPO\WC\PDF_Invoices_Pro;

use WPO\WC\PDF_Invoices_Pro\Cloud\Cloud_API;

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

if ( !class_exists( '\\WPO\\WC\\PDF_Invoices_Pro\\Bulk_Export' ) ) :

class Bulk_Export {
	public function __construct() {
		// hook into main pdf plugin settings
		add_filter( 'wpo_wcpdf_settings_tabs', array( $this, 'settings_tab' ) );

		add_action( 'admin_enqueue_scripts', array( $this, 'load_scripts_styles' ) ); // Load scripts & styles

		// bulk export page
		add_action( 'wpo_wcpdf_after_settings_page', array( $this, 'bulk_export_tab' ), 10, 1 );

		// Bulk export ajax actions
		add_action( 'wp_ajax_wpo_wcpdf_export_get_order_ids', array( $this, 'ajax_get_order_ids' ) );
		add_action( 'wp_ajax_wpo_wcpdf_export_bulk', array( $this, 'save_bulk' ) );
		add_action( 'wp_ajax_wpo_wcpdf_zip_bulk', array( $this, 'zip_bulk' ) );
		add_action( 'wp_ajax_wpo_wcpdf_download_pdf', array( $this, 'download_pdf' ) );
		add_action( 'wp_ajax_wpo_wcpdf_cloud_upload', array( $this, 'cloud_upload' ) );

		// query vars for get_orders by document date
		add_filter( 'woocommerce_order_data_store_cpt_get_orders_query', array( $this, 'document_date_query_vars' ), 10, 3 );
	}

	/**
	 * add Bulk Export settings tab to the PDF Invoice settings page
	 * @param  array $tabs slug => Title
	 * @return array $tabs with Bulk Export
	 */
	public function settings_tab( $tabs ) {
		// if (WPO_WCPDF_Dropbox()->api->is_enabled() !== false) {
			$tabs['bulk_export'] = __('Bulk export','wpo_wcpdf_pro');
		// }

		return $tabs;
	}

	/**
	 * Scrips & styles for settings page
	 */
	public function load_scripts_styles($hook) {
		$tab = isset($_GET['tab']) ? $_GET['tab'] : '';
		$page = isset($_GET['page']) ? $_GET['page'] : '';
		if( $page != 'wpo_wcpdf_options_page' || $tab != 'bulk_export') {
			return;
		}

		wp_enqueue_style(
			'woocommerce-pdf-ips-pro-jquery-ui-style',
			'https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/themes/smoothness/jquery-ui.css'
		);
		wp_enqueue_script(
			'woocommerce-pdf-pro-bulk',
			WPO_WCPDF_Pro()->plugin_url() . '/assets/js/pro-bulk-export.js',
			array( 'jquery', 'jquery-ui-datepicker' ),
			WPO_WCPDF_PRO_VERSION
		);
		wp_localize_script(
			'woocommerce-pdf-pro-bulk',
			'woocommerce_pdf_pro_bulk',
			array(
				'ajax_url'     => admin_url( 'admin-ajax.php' ),
				'nonce'        => wp_create_nonce('wpo_wcpdf_pro_bulk'),
				'chunk_size'   => apply_filters( 'wpo_wcpdf_export_bulk_chunk_size', 5 ),
				// send following dynamic variables for accessibility
				'order_date'   => __( 'Order date', 'wpo_wcpdf_pro' ),
				'refund_date'  => __( 'Refund date', 'wpo_wcpdf_pro' ),
				'download_zip' => __( 'Download ZIP', 'wpo_wcpdf_pro' ),
				'download_pdf' => __( 'Download PDF', 'wpo_wcpdf_pro' ),
				'pdf_saved'    => __( 'PDF document(s) saved', 'wpo_wcpdf_pro' ),
				'uploading'    => __( 'Uploading files to cloud service, please wait...', 'wpo_wcpdf_pro' ),
			)
		);

	}

	public function bulk_export_tab($tab) {
		if ( $tab == 'bulk_export' ) {
			if( ! is_null( WPO_WCPDF_Pro()->cloud_api ) ) {
				$cloud_api_is_enabled = Cloud_API::is_enabled();
				$cloud_service_name = Cloud_API::$service_name;
				$cloud_service_slug = Cloud_API::service_enabled();
			}
			
			include( WPO_WCPDF_Pro()->plugin_path() . '/includes/views/bulk-export.php' );
		}
	}

	/**
	 * Handle AJAX request
	 */
	public function ajax_get_order_ids() {
		check_ajax_referer( 'wpo_wcpdf_pro_bulk', 'security' );

		if ( !isset($_POST['status_filter']) && !(isset($_POST['date_type']) && $_POST['date_type'] == 'document_date') ) {
			$return = array(
				'error'	=> __('No orders found!', 'wpo_wcpdf_pro'),
				'posted'=> var_export($_POST,true),
			);
			echo json_encode($return);
			exit();
		}

		// get in utc timestamp for WC3.1+
		$utc_timestamp = version_compare( WOOCOMMERCE_VERSION, '3.1', '>=' ) ? true : false;
		// get dates from input
		$export_settings = array(
			'date_after'    => $this->get_date_string_from_input( 'date_from', 'hour_from', 'minute_from', false, $utc_timestamp ),
			'date_before'   => $this->get_date_string_from_input( 'date_to', 'hour_to', 'minute_to', true, $utc_timestamp ),
			'date_type'     => $_POST['date_type'],
			'document_type' => $_POST['document_type'],
			'statuses'      => $_POST['status_filter'],
		);

		// save export settings temporarily to be used by save_bulk() function
		$transient = "wpo_wcpdf_bulk_export_settings{$_POST['document_type']}";
		if ( get_transient( $transient ) ) {
			delete_transient( $transient );
		}
		$transient_set = set_transient( $transient, $export_settings, HOUR_IN_SECONDS );
		if ( $transient_set === false ) {
			wcpdf_log_error( sprintf( 'Could not set transient for bulk export settings (%s)', $transient ), 'critical' );
		}

		$order_ids = $this->get_orders_by_status( $export_settings );

		if (empty($order_ids)) {
			$return = array(
				'error'	=> __('No orders found!', 'wpo_wcpdf_pro'),
			);
			echo json_encode($return);
			exit();
		} else {
			echo json_encode(array_values($order_ids));
			exit();
		}
	}

	public function save_bulk() {
		check_ajax_referer( 'wpo_wcpdf_pro_bulk', 'security' );
		if ( empty( $_POST['order_ids'] ) ) {
			$return = array(
				'error'	=> __( 'No orders found!', 'wpo_wcpdf_pro' ),
			);
			echo json_encode( $return );
			exit();
		}

		$return        = array();
		$order_ids     = $_POST['order_ids'];
		$document_type = $_POST['document_type'];
		$skip_free     = isset( $_POST['skip_free'] ) && $_POST['skip_free'] == 'true' ? true : false;
		$only_existing = isset( $_POST['only_existing'] ) && $_POST['only_existing'] == 'true' ? true : false;
		$args          = array(
			'order_ids'     => $order_ids,
			'document_type' => $document_type,
			'skip_free'     => $skip_free,
			'only_existing' => $only_existing
		);

		// Allows an external bulk handler to hook in here, before any of the
		// logic below is being executed, effectively short circuiting the routine
		do_action( 'wpo_wcpdf_export_bulk_save_bulk_handler', $args );

		// create transient with file list of this export
		// @TODO: use unique transient name to allow parallel downloads
		$transient = "wpo_wcpdf_bulk_export_files_{$document_type}";
		$filelist  = get_transient( $transient );
		if ( ! is_array( $filelist ) ) {
			$filelist = array();
		}

		// turn off deprecation notices during bulk creation
		add_filter( 'wcpdf_disable_deprecation_notices', '__return_true' );

		// get bulk documents chunk and merge with trasient files
		$bulk_documents            = $this->get_documents_for_bulk( $args );
		$bulk_documents['success'] = array_merge( $filelist, $bulk_documents['success'] );

		// prepare return
		$return['success']       = ! empty( $bulk_documents['success'] ) ? $bulk_documents['success'] : array();
		$return['error']         = ! empty( $bulk_documents['error'] ) ? $bulk_documents['error'] : array();
		$return['transient']     = $transient;
		$return['filename']      = sanitize_file_name( $document_type.'.zip' );
		$return['document_type'] = $document_type;

		// set transient
		$transient_set = set_transient( $return['transient'], $return['success'], DAY_IN_SECONDS );
		if ( $transient_set === false ) {
			wcpdf_log_error( sprintf( 'Could not set transient for bulk export process (%s)', $return['transient'] ), 'critical' );
		}

		// re-enable deprecation notices
		remove_filter( 'wcpdf_disable_deprecation_notices', '__return_true' );

		echo json_encode( $return );
		exit();
	}

	public function get_documents_for_bulk( $args ) {
		$return = array(
			'success' => array(),
			'error'   => array(),
		);

		if ( ! empty( $args ) ) {
			$document_type     = $args['document_type'];
			$skip_free         = $args['skip_free'];
			$only_existing     = $args['only_existing'];
			$order_ids         = $args['order_ids'];
			$summary_order_ids = array();

			foreach ( $order_ids as $order_id ) {
				$order = wc_get_order( $order_id );
				if ( empty( $order ) ) {
					continue;
				}
	
				// check skip free setting
				if ( $skip_free && method_exists( $order, 'get_total' ) && $order->get_total() == 0 ) {
					continue;
				}

				$file_path = apply_filters( 'wpo_wcpdf_export_bulk_create_file', '', $order, $args );
				if ( ! $file_path ) {
					// check only existing setting
					if ( $only_existing ) {
						if ( $document_type == 'summary' ) {
							$document = wcpdf_get_invoice( $order );
						} else {
							$document = wcpdf_get_document( $document_type, $order );
						}
						
						if ( $document && $document->exists() === false ) {
							continue;
						}
					} else {
						if ( $document_type == 'summary' ) {
							$document = wcpdf_get_invoice( $order, true );
						} else {
							$document = wcpdf_get_document( $document_type, $order, true );
						}
					}
	
					if ( ! $document ) {
						continue;
					}

					if ( $document_type == 'summary' ) {
						$summary_order_ids[] = $order_id;
						continue;
					}
	
					try {
						// try to create the PDF
						$file_path = $this->create_pdf_file( $document );
						if ( ! empty( $file_path ) ) {
							$return['success'][] = $file_path;
						} else {
							$return['error'][] = sprintf( 'An error ocurred when trying to get the %s document!', $document_type );
						}
					// catch any errors that might could happen
					} catch ( \Exception $e ) {
						wcpdf_log_error( $e->getMessage(), 'critical', $e );
						continue;
					} catch ( \Dompdf\Exception $e ) {
						wcpdf_log_error( 'DOMPDF exception: '.$e->getMessage(), 'critical', $e );
						continue;
					} catch ( \Error $e ) {
						wcpdf_log_error( $e->getMessage(), 'critical', $e );
						continue;
					}
					
				} else {
					$return['success'][] = $file_path;
				}
			}

			// summary specific
			if ( ! empty( $summary_order_ids ) ) {
				$export_settings = get_transient( "wpo_wcpdf_bulk_export_settings{$document_type}" );
				if ( empty( $export_settings ) ) {
					$export_settings = array();
				}

				if ( $document ) {
					// try to create the PDF
					try {
						$document  = WPO_WCPDF_Pro()->functions->get_summary_document( $summary_order_ids, $export_settings );
						$file_path = $this->create_pdf_file( $document );
						if ( ! empty( $file_path ) ) {
							$return['success'][] = $file_path;
						} else {
							$return['error'][] = sprintf( 'An error ocurred when trying to get the %s document!', $document_type );
						}
					// catch any errors that might could happen
					} catch ( \Exception $e ) {
						wcpdf_log_error( $e->getMessage(), 'critical', $e );
					} catch ( \Dompdf\Exception $e ) {
						wcpdf_log_error( 'DOMPDF exception: '.$e->getMessage(), 'critical', $e );
					} catch ( \Error $e ) {
						wcpdf_log_error( $e->getMessage(), 'critical', $e );
					}
				}

				delete_transient( "wpo_wcpdf_bulk_export_settings{$document_type}" );
			}
		}

		return $return;
	}

	public function create_pdf_file ( $document ) {
		$tmp_path = trailingslashit( WPO_WCPDF()->main->get_tmp_path('attachments') );

		// get pdf data & filename
		$pdf_data = $document->get_pdf();
		$pdf_filename = $document->get_filename();

		$pdf_path = $tmp_path . $pdf_filename;

		// save file
		file_put_contents ( $pdf_path, $pdf_data );
		// set date if we have one
		if ( $document_date = $document->get_date() ) {
			$file_date = $document_date->getTimestamp();
			@touch( $pdf_path, $file_date );
		}

		return $pdf_path;
	}

	public function zip_bulk() {
		check_ajax_referer( 'wpo_wcpdf_pro_bulk', 'security' );

		@set_time_limit(0);

		if ( isset( $_GET['transient'] ) ) {
			$transient = sanitize_key( $_GET['transient'] );
			$filelist  = get_transient( $transient );
			if ( $filelist === false ) {
				wcpdf_log_error( sprintf( 'Could not read file list transient (%s) for bulk export process', $transient ), 'critical' );
			}	
			delete_transient( $transient );
		} else {
			// legacy method using filelist from postdata
			$filelist = $_POST['files'];
			if ( is_string( $filelist ) && strpos( $filelist, '[' ) !== false ) {
				$filelist = json_decode( stripslashes( $filelist ) );
			}
		}

		// wcpdf_log_error( var_export( $filelist, true ), 'debug' );

		do_action( 'wpo_wcpdf_export_bulk_save_bulk_download', array(
			'filelist'      => $filelist,
			'template_type' => $_REQUEST['template_type'],
		) );

		$document_type = $_REQUEST['template_type'];
		$filename      = sanitize_file_name( $document_type.'.zip' );

		try {
			if ( $zipfile = $this->create_zip( $filelist, $filename ) ) {
				if ( headers_sent() ) {
					echo 'HTTP header already sent';
				} else {
					if ( function_exists( 'apache_setenv' ) ) {
						apache_setenv( 'no-gzip', 1 );
						apache_setenv( 'dont-vary', 1 );
					}
					$output_compression = ini_get( 'zlib.output_compression' );
					if ( $output_compression && $output_compression !== 'off' ) {
						$set_output_compression = ini_set( 'zlib.output_compression', 0 );
						if ( $output_compression && $output_compression !== 'off' ) {
							throw new \Exception( 'zlib.output_compression needs to be turned off in PHP to create a zip file' );
						}
					}
					ob_clean();
					ob_end_flush();
					header( 'Content-Description: File Transfer' );
					header( 'Content-Type: application/x-zip' );
					header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
					header( 'Content-Transfer-Encoding: binary' );
					header( 'Connection: Keep-Alive' );
					header( 'Expires: 0' );
					header( 'Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0' );
					header( 'Pragma: public' );
					@readfile( $zipfile );
					@unlink( $zipfile ); // destroy after reading
				}
			}	
		} catch ( \Exception $e ) {
			wcpdf_log_error( $e->getMessage(), 'critical' );
			echo $e->getMessage();
		}
		exit;
	}

	public function check_zip_archive() {
		if (!class_exists('\\ZipArchive')) {
			return false;
		} else {
			return true;
		}
	}

	public function create_zip( $filelist, $zip_filename ) {
		$zip = new \ZipArchive();
		$tmp_path = trailingslashit( WPO_WCPDF()->main->get_tmp_path( 'attachments' ) );
		@unlink( $tmp_path . $zip_filename );
		if ( $zip->open( $tmp_path . $zip_filename, \ZipArchive::CREATE | \ZipArchive::OVERWRITE ) !== TRUE) {
			throw new \Exception( sprintf( 'An error occurred creating your ZIP file (%s)', $tmp_path . $zip_filename ) );
		}

		foreach ( $filelist as $filepath ) {
			if ( is_readable( $filepath ) ) {
				$added = $zip->addFile( $filepath, basename( $filepath ) );
				if ( $added === false ) {
					wcpdf_log_error( sprintf( 'Could not add PDF file (%s) during ZIP process', $filepath ), 'critical' );
				}
			} else {
				wcpdf_log_error( sprintf( 'Could not read PDF file (%s) during ZIP process', $filepath ), 'critical' );
			}
		}

		$closed = $zip->close();
		if ( $closed === true ) {
			return $tmp_path . $zip_filename;
		} else {
			throw new \Exception( sprintf( 'ZIP file could not be saved (%s)', $tmp_path . $zip_filename ) );
		}
	}

	public function download_pdf() {
		check_ajax_referer( 'wpo_wcpdf_pro_bulk', 'security' );

		if ( isset( $_GET['transient'] ) ) {
			$transient = sanitize_key( $_GET['transient'] );
			$filelist  = get_transient( $transient );
			if ( $filelist === false ) {
				wcpdf_log_error( sprintf( 'Could not read file list transient (%s) for PDF export process', $transient ), 'critical' );
				return;
			}	
			delete_transient( $transient );

			if ( ! empty( $pdf = reset( $filelist ) ) ) {
				wcpdf_pdf_headers( basename( $pdf ), 'download', $pdf );
				readfile( $pdf );
			}
		}

		die();
	}

	public function cloud_upload() {
		check_ajax_referer( 'wpo_wcpdf_pro_bulk', 'security' );

		$success  = array();
		$error    = array();
		$messages = array();

		if ( isset( $_REQUEST['transient'] ) ) {
			$transient = sanitize_key( $_REQUEST['transient'] );
			$filelist  = get_transient( $transient );
			if ( $filelist === false ) {
				wcpdf_log_error( sprintf( 'Could not read file list transient (%s) for cloud upload process', $transient ), 'critical' );
			}	
			delete_transient( $transient );

			if ( ! empty( $filelist ) && is_array( $filelist ) ) {
				foreach ( $filelist as $pdf_path ) {
					try {
						// TODO: we could schedule the upload using AS action
						$cloud_storage   = new Cloud_Storage;
						$upload_response = $cloud_storage->upload_to_service( $pdf_path, 'export' );
		
						// Houston, we have a problem
						if ( ! empty( $upload_response['error'] ) ) {
							wcpdf_log_error( $upload_response['error'], 'error' );
							$error[] = $pdf_path;
						} elseif ( ! empty( $upload_response['success'] ) ) {
							$success[] = $pdf_path;
						} else {
							continue;
						}
					} catch ( \Exception $e ) {
						wcpdf_log_error( $e->getMessage(), 'critical' );
						echo $e->getMessage();
					}
				}
			}
		}

		if ( ! empty( $success ) ) {
			$messages['success'] = sprintf(
				/* translators: files count */
				_n( 'Upload completed! %s file transferred to cloud service.', 'Upload completed! %s files transferred to cloud service.', count( $success ), 'wpo_wcpdf_pro' ),
				count( $success )
			);
		}
		if ( ! empty( $error ) ) {
			$messages['error'] = sprintf(
				/* translators: files count */
				_n( '%s file not uploaded to cloud! Please see logs.', '%s files not uploaded to cloud! Please see logs.', count( $error ), 'wpo_wcpdf_pro' ),
				count( $error )
			);
		}

		wp_send_json( $messages );

		die();
	}

	public function get_orders_by_status ( $export_settings ) {
		$args = array(
			'status'	=> $export_settings['statuses'],
			'return'	=> 'ids',
			'type'		=> 'shop_order',
			'limit'		=> -1,
		);

		if ( empty( $export_settings['date_type'] ) || $export_settings['date_type'] == 'order_date' ) {
			$export_settings['date_type'] = 'date_created';
		}
		
		$wc_date_types = array(
			'date_created',
			'date_modified',
			'date_completed',
			'date_paid',
		);

		if ( in_array( $export_settings['date_type'], $wc_date_types ) ) {
			if ( $export_settings['document_type'] == 'credit-note' ) {
				$args['type'] = 'shop_order_refund'; // Note that this will change the export behavior of orders with multiple refunds: instead of one PDF per order with all the credit notes, it will always create them separately.
			}
			$date_arg = $export_settings['date_type'];
		} elseif ( $export_settings['date_type'] == 'document_date' ) {
			if ( $export_settings['document_type'] == 'credit-note' ) {
				$args['type'] = 'shop_order_refund';
			}
			$document_slug = str_replace('-', '_', $export_settings['document_type']); // querying documents functions may be more reliable but this works fine and prevents issues with UBL export
			$date_arg = "wcpdf_{$document_slug}_date";
		} else {
			$date_arg = "wcpdf_bulk_export_{$export_settings['date_type']}";
		}

		// for code readability
		$date_before = $export_settings['date_before'];
		$date_after = $export_settings['date_after'];

		if ( version_compare( WOOCOMMERCE_VERSION, '3.1', '>=' ) ) {
			// WC3.1+
			if ( $date_after && !$date_before ) {
				// after date
				$args[$date_arg] = '>='.$date_after;
			} elseif ( $date_before ) {
				if (!$date_after) {
					// before date
					$args[$date_arg] = '<='.$date_before;
				} else {
					// between dates
					$args[$date_arg] = $date_after.'...'.$date_before;
				}
			}
		} else {
			// WC3.0
			if( $date_after ) {
				$args['date_after'] = $date_after;
			}
			if( $date_before ) {
				$args['date_before'] = $date_before;
			}

		}

		// Allow 3rd parties to alter the arguments used to fetch the order IDs
		// @author Aelia
		$args = apply_filters( 'wpo_wcpdf_export_bulk_get_orders_args', $args, $export_settings );

		$order_ids = wc_get_orders( $args );

		// Allow 3rd parties to alter the list of order IDs returned by the query
		// @author Aelia
		$order_ids = apply_filters( 'wpo_wcpdf_export_bulk_order_ids', $order_ids, $args, $export_settings );

		// sort ids if date type is order date
		if ( apply_filters( 'wpo_wcpdf_export_bulk_sort_order_ids', $date_arg == 'date_created', $order_ids, $args, $export_settings ) ) {
			asort( $order_ids );
		}

		return $order_ids;
	}

	public function get_date_string_from_input( $date_key, $hour_key, $minute_key, $include_minute = false, $utc_timestamp = false ) {
		$date   = isset( $_REQUEST[$date_key] ) ? sanitize_text_field( $_REQUEST[$date_key] ) : null;
		$hour   = isset( $_REQUEST[$hour_key] ) ? sanitize_text_field( $_REQUEST[$hour_key] ) : null;
		$minute = isset( $_REQUEST[$minute_key] ) ? sanitize_text_field( $_REQUEST[$minute_key] ) : null;

		if (empty($date)) {
			return false;
		}

		if( $date_key == 'date_to' && ! is_null( WPO_WCPDF_Pro()->cloud_api ) ) {
			// store last export date & time
			update_option( 'wpo_wcpdf_'.Cloud_API::service_enabled().'_last_export', array('date'=>$date,'hour'=>$hour,'minute'=>$minute) );
		}

		if (!empty($hour)) {
			$seconds = $include_minute ? '59' : '00';
			$date = sprintf("%s %02d:%02d:%02d", $date, $hour, $minute, $seconds);
		}

		if ($utc_timestamp) {
			// Convert local WP timezone to UTC.
			if ( 1 === preg_match( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|((-|\+)\d{2}:\d{2}))$/', $date, $date_bits ) ) {
				$offset    = ! empty( $date_bits[7] ) ? iso8601_timezone_to_offset( $date_bits[7] ) : wc_timezone_offset();
				$timestamp = gmmktime( $date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1] ) - $offset;
			} else {
				$timestamp = wc_string_to_timestamp( get_gmt_from_date( gmdate( 'Y-m-d H:i:s', wc_string_to_timestamp( $date ) ) ) );
			}
			$date = $timestamp;
		}

		return $date;
	}

	public function document_date_query_vars( $wp_query_args, $query_vars, $order_store_cpt ) {
		foreach ( WPO_WCPDF()->documents->get_documents() as $document ) {
			if ( isset( $query_vars[ "wcpdf_{$document->slug}_date" ] ) && '' !== $query_vars[ "wcpdf_{$document->slug}_date" ] ) {
				$wp_query_args = $order_store_cpt->parse_date_for_wp_query( $query_vars[ "wcpdf_{$document->slug}_date" ], "_wcpdf_{$document->slug}_date", $wp_query_args );
			}
		}

		return $wp_query_args;
	}

} // end class

endif; // end class_exists

return new Bulk_Export();
