<?php
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;

/**
 * WC_Appointments_Admin_Exporters Class.
 */
class WC_Appointments_Admin_Exporters {

	/**
	 * Array of exporter IDs.
	 *
	 * @var string[]
	 */
	protected array $exporters = [];

	/**
	 * Export manager instance.
	 *
	 * @var WC_Appointments_Export_Manager
	 */
	protected WC_Appointments_Export_Manager $export_manager;

	/**
	 * Constructor.
	 */
	public function __construct() {
		// Initialize export manager.
		$this->export_manager = new WC_Appointments_Export_Manager();

		// Register default exporters.
		$this->register_default_exporters();
		// Only register hooks if user has export permissions.
		if ( ! $this->export_allowed() ) {
			return;
		}

		// Register download handler very early to prevent page load (fallback).
		add_action( 'admin_init', [ $this, 'download_export_file' ], 1 );
		// Also hook into load-{page} to catch it even earlier.
		add_action( 'load-wc_appointment_page_appointment_exporter', [ $this, 'download_export_file' ], 1 );

		// Check for download request in admin_menu before page structure loads.
		add_action( 'admin_menu', [ $this, 'maybe_handle_download' ], 5 );

		// Register admin menu.
		add_action( 'admin_menu', [ $this, 'add_to_menus' ], 20 );
		// Hide menu item from main menu (page still accessible via direct URL).
		add_action( 'admin_menu', [ $this, 'hide_from_menus' ], 99 );

		// Ensure page title is set to prevent null values in admin-header.php.
		// Hook early to catch it before admin-header.php processes it.
		add_action( 'current_screen', [ $this, 'set_page_title_on_screen' ], 1 );
		add_filter( 'admin_title', [ $this, 'set_admin_page_title' ], 10, 2 );
		// Also set title in admin_head to ensure it's available early.
		add_action( 'admin_head', [ $this, 'set_page_title_early' ] );

		// Register AJAX handler for export.
		add_action( 'wp_ajax_woocommerce_do_ajax_appointment_export', [ $this, 'do_ajax_appointment_export' ] );
	}

	/**
	 * Register default exporters with the export manager.
	 *
	 * @return void
	 */
	protected function register_default_exporters(): void {
		include_once WC_APPOINTMENTS_ABSPATH . 'includes/admin/export/class-wc-appointments-csv-exporter.php';
		include_once WC_APPOINTMENTS_ABSPATH . 'includes/admin/export/class-wc-appointments-ics-exporter.php';

		// Register CSV exporter.
		$this->export_manager->register_exporter( new WC_Appointments_CSV_Exporter() );

		// Register ICS exporter.
		$this->export_manager->register_exporter( new WC_Appointments_ICS_Exporter() );
	}

	/**
	 * Get the export manager instance.
	 *
	 * @return WC_Appointments_Export_Manager Export manager instance.
	 */
	public function get_export_manager(): WC_Appointments_Export_Manager {
		return $this->export_manager;
	}

	/**
	 * Return true if WooCommerce export is allowed for current user, false otherwise.
	 *
	 * @return bool Whether current user can perform export.
	 */
	protected function export_allowed(): bool {
		return current_user_can( 'edit_appointments' ) && current_user_can( 'export' );
	}

	/**
	 * Check for download request early in admin_menu hook.
	 */
	public function maybe_handle_download(): void {
		if ( isset( $_GET['action'], $_GET['nonce'] ) 
			&& ( 'download_appointment_csv' === wp_unslash( $_GET['action'] ) || 'download_appointment_export' === wp_unslash( $_GET['action'] ) )
			&& wp_verify_nonce( wp_unslash( $_GET['nonce'] ), 'appointment-export' ) 
		) { // WPCS: input var ok, sanitization ok.
			$this->download_export_file();
			// Should never reach here, but exit just in case.
			exit;
		}
	}

	/**
	 * Set page title when screen is set up.
	 *
	 * @param WP_Screen $screen Current screen object.
	 */
	public function set_page_title_on_screen( $screen ): void {
		if ( $screen && 'wc_appointment_page_appointment_exporter' === $screen->id ) {
			// Set global title variable early to ensure it's never null.
			global $title;
			if ( empty( $title ) || null === $title ) {
				$title = __( 'Appointment Export', 'woocommerce-appointments' );
			}
		}
	}

	/**
	 * Set page title early in admin_head to prevent null values.
	 */
	public function set_page_title_early(): void {
		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
		
		if ( $screen && 'wc_appointment_page_appointment_exporter' === $screen->id ) {
			// Set global title variable to ensure it's never null.
			global $title;
			if ( empty( $title ) || null === $title ) {
				$title = __( 'Appointment Export', 'woocommerce-appointments' );
			}
		}
	}

	/**
	 * Set admin page title to prevent null values.
	 *
	 * @param string $admin_title The page title.
	 * @param string $title The original title.
	 * @return string
	 */
	public function set_admin_page_title( $admin_title, $title ): string {
		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
		
		if ( $screen && 'wc_appointment_page_appointment_exporter' === $screen->id ) {
			// Ensure we have a valid title - never return null or empty.
			$page_title = __( 'Appointment Export', 'woocommerce-appointments' );
			
			// Handle null or empty title parameter.
			if ( empty( $title ) || null === $title || '' === $title ) {
				return $page_title;
			}
			
			// Ensure the admin_title itself is not null.
			if ( null === $admin_title || '' === $admin_title ) {
				return $page_title;
			}
			
			// Ensure title is a string, not null.
			$title = (string) $title;
		}
		
		// Ensure we never return null - convert to string if needed.
		if ( null === $admin_title ) {
			return __( 'Appointment Export', 'woocommerce-appointments' );
		}
		
		return (string) $admin_title;
	}

	/**
	 * Add menu items for our custom exporters.
	 */
	public function add_to_menus(): void {
		add_submenu_page(
		    'edit.php?post_type=wc_appointment',
		    __( 'Appointment Export', 'woocommerce-appointments' ),
		    __( 'Appointment Export', 'woocommerce-appointments' ),
		    'export',
		    'appointment_exporter',
		    [ $this, 'appointment_exporter' ],
		);
	}

	/**
	 * Hide menu items from view so the pages exist, but the menu items do not.
	 */
	public function hide_from_menus(): void {
		global $submenu;

		$menu_to_unset = 'edit.php?post_type=wc_appointment';

		if ( isset( $submenu[ $menu_to_unset ] ) ) {
			foreach ( $submenu[ $menu_to_unset ] as $key => $menu ) {
				if ( 'appointment_exporter' === $menu[2] ) {
					unset( $submenu[ $menu_to_unset ][ $key ] );
				}
			}
		}
	}

	/**
	 * Export page UI.
	 */
	public function appointment_exporter(): void {
		// If this is a download request, the download handler should have already processed it.
		// This should not be reached, but just in case, exit early.
		if ( isset( $_GET['action'] ) && ( 'download_appointment_csv' === wp_unslash( $_GET['action'] ) || 'download_appointment_export' === wp_unslash( $_GET['action'] ) ) ) { // WPCS: input var ok, sanitization ok.
			return;
		}

		// Check permissions before displaying the page.
		if ( ! $this->export_allowed() ) {
			wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'woocommerce-appointments' ) );
		}

		include_once WC_APPOINTMENTS_ABSPATH . 'includes/admin/export/class-wc-appointments-csv-exporter.php';
		include_once __DIR__ . '/views/html-appointment-export.php';
	}

	/**
	 * Serve the generated file.
	 */
	public function download_export_file(): void {
		// Check if this is a download request.
		$action = isset( $_GET['action'] ) ? wp_unslash( $_GET['action'] ) : ''; // WPCS: input var ok, sanitization ok.
		
		if ( ! is_admin()
		    || ! isset( $_GET['action'], $_GET['nonce'] )
			|| ( 'download_appointment_csv' !== $action && 'download_appointment_export' !== $action )
		) { // WPCS: input var ok, sanitization ok.
			return;
		}

		// Verify nonce (support both old and new nonce names for backward compatibility).
		$nonce_action = ( 'download_appointment_csv' === $action ) ? 'appointment-csv' : 'appointment-export';
		if ( ! wp_verify_nonce( wp_unslash( $_GET['nonce'] ), $nonce_action ) ) { // WPCS: input var ok, sanitization ok.
			wp_die( esc_html__( 'Security check failed.', 'woocommerce-appointments' ) );
		}

		// Check permissions before serving the file.
		if ( ! $this->export_allowed() ) {
			wp_die( esc_html__( 'You do not have sufficient permissions to download this file.', 'woocommerce-appointments' ) );
		}

		// Prevent any output or page rendering.
		// Clear all output buffers.
		while ( ob_get_level() ) {
			ob_end_clean();
		}

		// Prevent any output before we send the file.
		if ( ob_get_level() ) {
			ob_end_clean();
		}

		// Get format and filename from the request.
		$format   = isset( $_GET['format'] ) ? sanitize_text_field( wp_unslash( $_GET['format'] ) ) : 'csv'; // WPCS: input var ok, sanitization ok.
		$filename = ! empty( $_GET['filename'] ) ? wp_unslash( $_GET['filename'] ) : 'export.' . $format; // WPCS: input var ok, sanitization ok.

		// The file should already be generated by the AJAX handler.
		// Find it in the uploads directory.
		$upload_dir = wp_upload_dir();
		$file_path  = trailingslashit( $upload_dir['path'] ) . $filename;

		// If not found in path, try basedir.
		if ( ! file_exists( $file_path ) ) {
			$file_path = trailingslashit( $upload_dir['basedir'] ) . $filename;
		}

		// If still not found, try to get it from the exporter's expected location.
		if ( ! file_exists( $file_path ) ) {
			$exporter = $this->export_manager->get_exporter( $format );
			if ( $exporter ) {
				// For CSV exporter.
				if ( 'csv' === $format && method_exists( $exporter, 'set_filename' ) && method_exists( $exporter, 'get_export_file_path' ) ) {
					$exporter->set_filename( $filename );
					$file_path = $exporter->get_export_file_path();
				}
				// For ICS exporter, files are created with full path, so check uploads directory more thoroughly.
				elseif ( 'ics' === $format ) {
					// ICS files are stored in uploads directory with sanitized filename.
					$sanitized_filename = sanitize_title( pathinfo( $filename, PATHINFO_FILENAME ) ) . '.ics';
					$file_path          = trailingslashit( $upload_dir['path'] ) . $sanitized_filename;
					if ( ! file_exists( $file_path ) ) {
						$file_path = trailingslashit( $upload_dir['basedir'] ) . $sanitized_filename;
					}
				}
			}
		}

		if ( $file_path && file_exists( $file_path ) ) {
			// Determine content type based on format.
			$content_type = 'csv' === $format ? 'text/csv; charset=utf-8' : 'text/calendar; charset=utf-8';

			// Set headers for file download.
			nocache_headers();
			header( 'Content-Type: ' . $content_type );
			header( 'Content-Disposition: attachment; filename=' . sanitize_file_name( basename( $file_path ) ) );
			header( 'Pragma: no-cache' );
			header( 'Expires: 0' );
			header( 'Content-Length: ' . filesize( $file_path ) );

			// Output the file.
			if ( false !== ( $handle = fopen( $file_path, 'r' ) ) ) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
				// Output BOM for UTF-8 (CSV only).
				if ( 'csv' === $format ) {
					echo "\xEF\xBB\xBF"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
				}

				// Read and output the file.
				while ( ! feof( $handle ) ) {
					echo fread( $handle, 8192 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.AlternativeFunctions.file_system_read_fread
				}
				fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
			}

			// Clean up the temporary file.
			@unlink( $file_path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.unlink_unlink
		} else {
			wp_die( esc_html__( 'Export file could not be generated.', 'woocommerce-appointments' ) );
		}

		exit; // Prevent further execution after file download.
	}

	/**
	 * AJAX callback for doing the actual export.
	 */
	public function do_ajax_appointment_export(): void {
		check_ajax_referer( 'wc-appointment-export', 'security' );

		if ( ! $this->export_allowed() ) {
			wp_send_json_error(
			    [
					'message' => __( 'Insufficient privileges to export appointments.', 'woocommerce-appointments' ),
				],
			);
		}

		// Get format from POST data, default to CSV for backward compatibility.
		$format = isset( $_POST['format'] ) ? sanitize_text_field( wp_unslash( $_POST['format'] ) ) : 'csv'; // WPCS: input var ok, sanitization ok.

		// Get appointments based on filters.
		$appointments = $this->get_appointments_for_export();

		// For ICS format, export directly (no batch processing needed).
		if ( 'ics' === $format ) {
			$filename = isset( $_POST['filename'] ) ? sanitize_file_name( wp_unslash( $_POST['filename'] ) ) : ''; // WPCS: input var ok, sanitization ok.
			if ( empty( $filename ) ) {
				$filename = 'appointments-' . date_i18n( 'Ymd-His', current_time( 'timestamp' ) );
			}

			// Remove .ics extension if present (exporter will add it).
			$filename = preg_replace( '/\.ics$/i', '', $filename );
			// Remove any "-ics" suffix.
			$filename = preg_replace( '/-ics$/i', '', $filename );

			// Check if add-ons should be exported.
			$export_addons = ! empty( $_POST['export_addon'] ); // WPCS: input var ok.

			$file_path = $this->export_manager->export_appointments(
				'ics',
				$appointments,
				[
					'filename'     => $filename,
					'export_addons' => $export_addons,
				]
			);

			if ( $file_path ) {
				$query_args = apply_filters(
					'woocommerce_export_get_ajax_query_args',
					[
						'nonce'    => wp_create_nonce( 'appointment-export' ),
						'action'   => 'download_appointment_export',
						'format'   => 'ics',
						'filename' => basename( $file_path ),
					],
				);

				wp_send_json_success(
					[
						'step'       => 'done',
						'percentage' => 100,
						'url'        => add_query_arg( $query_args, admin_url( 'edit.php?post_type=wc_appointment&page=appointment_exporter' ) ),
					],
				);
			} else {
				wp_send_json_error(
					[
						'message' => __( 'Failed to generate ICS export file.', 'woocommerce-appointments' ),
					],
				);
			}
		}

		// CSV format (batch processing).
		include_once WC_APPOINTMENTS_ABSPATH . 'includes/admin/export/class-wc-appointments-csv-exporter.php';

		$step     = isset( $_POST['step'] ) ? absint( $_POST['step'] ) : 1; // WPCS: input var ok, sanitization ok.
		$exporter = new WC_Appointments_CSV_Exporter();

		if ( ! empty( $_POST['columns'] ) ) { // WPCS: input var ok.
			$exporter->set_column_names( wp_unslash( $_POST['columns'] ) ); // WPCS: input var ok, sanitization ok.
		}

		if ( ! empty( $_POST['selected_columns'] ) ) { // WPCS: input var ok.
			$exporter->set_columns_to_export( wp_unslash( $_POST['selected_columns'] ) ); // WPCS: input var ok, sanitization ok.
		}

		if ( ! empty( $_POST['export_start'] ) ) { // WPCS: input var ok.
			$exporter->set_start_date_to_export( wp_unslash( $_POST['export_start'] ) ); // WPCS: input var ok, sanitization ok.
		}

		if ( ! empty( $_POST['export_end'] ) ) { // WPCS: input var ok.
			$exporter->set_end_date_to_export( wp_unslash( $_POST['export_end'] ) ); // WPCS: input var ok, sanitization ok.
		}

		if ( ! empty( $_POST['export_product'] ) && is_array( $_POST['export_product'] ) ) {// WPCS: input var ok.
			$exporter->set_appointment_product_to_export( wp_unslash( array_values( $_POST['export_product'] ) ) ); // WPCS: input var ok, sanitization ok.
		}

		if ( ! empty( $_POST['export_staff'] ) && is_array( $_POST['export_staff'] ) ) {// WPCS: input var ok.
			$exporter->set_appointment_staff_to_export( wp_unslash( array_values( $_POST['export_staff'] ) ) ); // WPCS: input var ok, sanitization ok.
		}

		if ( ! empty( $_POST['export_addon'] ) ) { // WPCS: input var ok.
			$exporter->enable_addon_export( true );
		}

		if ( ! empty( $_POST['filename'] ) ) { // WPCS: input var ok.
			$exporter->set_filename( wp_unslash( $_POST['filename'] ) ); // WPCS: input var ok, sanitization ok.
		}

		$exporter->set_page( $step );
		$exporter->generate_file();

		$query_args = apply_filters(
		    'woocommerce_export_get_ajax_query_args',
		    [
				'nonce'    => wp_create_nonce( 'appointment-export' ),
				'action'   => 'download_appointment_export',
				'format'   => 'csv',
				'filename' => $exporter->get_filename(),
			],
		);

		if ( 100 === $exporter->get_percent_complete() ) {
			wp_send_json_success(
			    [
					'step'       => 'done',
					'percentage' => 100,
					'url'        => add_query_arg( $query_args, admin_url( 'edit.php?post_type=wc_appointment&page=appointment_exporter' ) ),
				],
			);
		} else {
			wp_send_json_success(
			    [
					'step'       => ++$step,
					'percentage' => $exporter->get_percent_complete(),
					'columns'    => $exporter->get_column_names(),
				],
			);
		}
	}

	/**
	 * Get appointments for export based on POST filters.
	 *
	 * @return array Array of WC_Appointment objects.
	 */
	protected function get_appointments_for_export(): array {
		$args = [
			'start'    => '',
			'end'      => '',
			'product'  => [],
			'staff'    => [],
			'status'   => array_unique( array_merge( get_wc_appointment_statuses( 'all' ), get_wc_appointment_statuses( 'user' ), get_wc_appointment_statuses( 'cancel' ) ) ),
			'limit'    => -1,
			'order_by' => 'date_created',
			'order'    => 'DESC',
			'return'   => 'objects',
		];

		if ( ! empty( $_POST['export_start'] ) ) { // WPCS: input var ok.
			$args['start'] = strtotime( urldecode( wp_unslash( $_POST['export_start'] ) ) ); // WPCS: input var ok, sanitization ok.
		}

		if ( ! empty( $_POST['export_end'] ) ) { // WPCS: input var ok.
			$args['end'] = strtotime( urldecode( wp_unslash( $_POST['export_end'] ) ) ); // WPCS: input var ok, sanitization ok.
		}

		if ( ! empty( $_POST['export_product'] ) && is_array( $_POST['export_product'] ) ) { // WPCS: input var ok.
			$args['product'] = array_map( 'intval', wp_unslash( array_values( $_POST['export_product'] ) ) ); // WPCS: input var ok, sanitization ok.
		}

		if ( ! empty( $_POST['export_staff'] ) && is_array( $_POST['export_staff'] ) ) { // WPCS: input var ok.
			$args['staff'] = array_map( 'intval', wp_unslash( array_values( $_POST['export_staff'] ) ) ); // WPCS: input var ok, sanitization ok.
		}

		// Allow 3rd parties to process the arguments.
		$args = apply_filters( 'woocommerce_appointment_export_data_args', $args, $this );

		$appointments = WC_Appointment_Data_Store::get_appointments_in_date_range(
			$args['start'],
			$args['end'],
			$args['product'],
			$args['staff'],
			false,
			$args,
		);

		return is_array( $appointments ) ? $appointments : [];
	}
}

new WC_Appointments_Admin_Exporters();
