<?php

namespace WC_Affiliate\API;

defined( 'ABSPATH' ) || exit;

use Exception;
use WC_Affiliate\Models\Affiliate;
use WC_Affiliate\Models\ExportJob;
use WC_Affiliate\Models\Referral;
use WC_Affiliate\Models\Settings;
use WC_Affiliate\Models\Transaction;
use WC_Affiliate\Models\Visit;
use WC_Affiliate\Traits\Auth;
use WC_Affiliate\Traits\Rest;
use WC_Affiliate\Traits\Validation;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

/**
 * Export API Controller
 *
 * Handles REST API requests for exporting affiliate program data including
 * affiliates, visits, referrals, transactions, and settings. Supports various
 * export formats and provides background processing for large datasets.
 *
 * @since   1.0.0
 * @package WC_Affiliate\API
 */
class Export {

	use Auth;
	use Rest;
	use Validation;

	public function handle_download_export() {
		$export_id = sanitize_text_field( wp_unslash( $_GET['export_id'] ?? '' ) );
		$nonce     = sanitize_text_field( wp_unslash( $_GET['nonce'] ?? '' ) );

		if ( empty( $export_id ) || empty( $nonce ) ) {
			wp_die( 'Invalid request', 400 );
		}

		// Verify nonce.
		if ( ! wp_verify_nonce( $nonce, "download_export_{$export_id}" ) ) {
			wp_die( 'Invalid nonce', 403 );
		}

		$job_data = get_option( "wc_affiliate_export_job_{$export_id}" );
		if ( ! $job_data || 'completed' !== $job_data['status'] ) {
			wp_die( 'Export not found or not completed', 404 );
		}

		// Permission check.
		$current_user_id = get_current_user_id();
		if (
			! current_user_can( 'manage_options' ) &&
			(int) $job_data['created_by'] !== $current_user_id
		) {
			wp_die( 'Unauthorized', 403 );
		}

		$file_path = $job_data['file_path'];
		if ( empty( $file_path ) || ! file_exists( $file_path ) ) {
			wp_die( 'File not found', 404 );
		}

		// Clean output buffer.
		if ( ob_get_level() ) {
			ob_end_clean();
		}

		header( 'Content-Type: application/octet-stream' );
		header( 'Content-Disposition: attachment; filename="' . basename( $file_path ) . '"' );
		header( 'Content-Length: ' . filesize( $file_path ) );
		header( 'Cache-Control: no-cache, must-revalidate' );
		header( 'Pragma: no-cache' );

		readfile( $file_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile
		exit;
	}

	/**
	 * Get available export types
	 *
	 * @return WP_REST_Response
	 */
	public function get_types(): WP_REST_Response {
		return $this->response_success( wca_get_affiliate_data_types() );
	}

	/**
	 * Start data export
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function start( WP_REST_Request $request ) {
		$export_type = sanitize_text_field( $request->get_param( 'type' ) );
		$format      = sanitize_text_field( $request->get_param( 'format' ) );
		$filters     = $request->get_param( 'filters' );

		// Validate required parameters.
		if ( empty( $export_type ) ) {
			return $this->response_error( __( 'Export type is required', 'wc-affiliate' ), 400 );
		}

		if ( empty( $format ) ) {
			return $this->response_error( __( 'Export format is required', 'wc-affiliate' ), 400 );
		}

		// Validate export type.
		if ( ! array_key_exists( $export_type, wca_get_affiliate_data_types() ) ) {
			return $this->response_error( __( 'Invalid export type', 'wc-affiliate' ), 400 );
		}

		// Validate format.
		$allowed_formats = array( 'json', 'csv', 'xml' );
		if ( ! in_array( $format, $allowed_formats, true ) ) {
			return $this->response_error( __( 'Invalid export format', 'wc-affiliate' ), 400 );
		}

		// Validate filters if provided.
		if ( ! is_array( $filters ) ) {
			$filters = array();
		}

		// Generate export ID.
		$export_id = wp_generate_uuid4();
		if ( empty( $export_id ) ) {
			return $this->response_error( __( 'Failed to generate export ID', 'wc-affiliate' ), 500 );
		}

		try {
			// Get data count for progress tracking.
			$total_items = $this->get_count( $export_type, $filters );
			if ( $total_items < 0 ) {
				return $this->response_error( __( 'Failed to count export items', 'wc-affiliate' ), 500 );
			}

			// Check if export would be too large.
			$max_items = apply_filters( 'wc_affiliate_export_max_items', 50000 );
			if ( $total_items > $max_items ) {
				return $this->response_error(
					sprintf(
						/* translators: %d: maximum number of items */
						__( 'Export too large. Maximum %d items allowed.', 'wc-affiliate' ),
						$max_items
					),
					400
				);
			}

			// Store export job details.
			$job_data = array(
				'id'          => $export_id,
				'type'        => $export_type,
				'format'      => $format,
				'filters'     => $filters,
				'total_items' => $total_items,
				'status'      => 'pending',
				'progress'    => 0,
				'created_at'  => time(),
				'created_by'  => get_current_user_id(),
				'file_path'   => null,
			);

			$result = update_option( "wc_affiliate_export_job_{$export_id}", $job_data );
			if ( ! $result ) {
				return $this->response_error( __( 'Failed to create export job', 'wc-affiliate' ), 500 );
			}

			// Schedule background processing for large datasets.
			if ( $total_items > apply_filters( 'wc_affiliate_export_background_threshold', 1000 ) ) {
				$scheduled = wp_schedule_single_event( time(), 'wc_affiliate_process_export', array( $export_id ) );
				if ( ! $scheduled ) {
					return $this->response_error( __( 'Failed to schedule background export', 'wc-affiliate' ), 500 );
				}
			} else {
				// Process immediately for small datasets
				$process_result = $this->process( $export_id );
				if ( ! $process_result ) {
					return $this->response_error( __( 'Export processing failed', 'wc-affiliate' ), 500 );
				}
			}

			$response = array(
				'export_id'   => $export_id,
				'status'      => 'initiated',
				'total_items' => $total_items,
				'message'     => $total_items > apply_filters( 'wc_affiliate_export_background_threshold', 1000 ) ?
					__( 'Export job queued for background processing', 'wc-affiliate' ) :
					__( 'Export processing started', 'wc-affiliate' ),
			);

			return $this->response_success( $response );
		} catch ( Exception $e ) {
			return $this->response_error( $e->getMessage(), 500 );
		}
	}

	/**
	 * Get export status
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function get_status( WP_REST_Request $request ) {
		$export_id = sanitize_text_field( $request->get_param( 'export_id' ) );

		$job_data = get_option( "wc_affiliate_export_job_{$export_id}" );
		if ( ! $job_data ) {
			return $this->response_error( __( 'Export job not found', 'wc-affiliate' ), 404 );
		}

		return $this->response_success( $job_data );
	}

	/**
	 * Download exported file
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function download( WP_REST_Request $request ) {
		$export_id = sanitize_text_field( $request->get_param( 'export_id' ) );

		$job_data = get_option( "wc_affiliate_export_job_{$export_id}" );
		if ( ! $job_data ) {
			return $this->response_error( __( 'Export job not found', 'wc-affiliate' ), 404 );
		}

		if ( 'completed' !== $job_data['status'] ) {
			return $this->response_error( __( 'Export not completed yet', 'wc-affiliate' ), 400 );
		}

		if ( ! file_exists( $job_data['file_path'] ) ) {
			return $this->response_error( __( 'Export file not found', 'wc-affiliate' ), 404 );
		}

		// Return download URL..
		$download_url = add_query_arg(
			array(
				'action'    => 'wc_affiliate_download_export',
				'export_id' => $export_id,
				'nonce'     => wp_create_nonce( "download_export_{$export_id}" ),
			),
			admin_url( 'admin-ajax.php' )
		);

		$response = array(
			'download_url' => $download_url,
			'filename'     => basename( $job_data['file_path'] ),
			'filesize'     => filesize( $job_data['file_path'] ),
			'format'       => $job_data['format'],
		);

		return $this->response_success( $response );
	}

	/**
	 * Delete export job and file
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function delete( WP_REST_Request $request ) {
		$export_id = sanitize_text_field( $request->get_param( 'export_id' ) );

		$job_data = get_option( "wc_affiliate_export_job_{$export_id}" );
		if ( ! $job_data ) {
			return $this->response_error( __( 'Export job not found', 'wc-affiliate' ), 404 );
		}

		// Delete file if exists..
		if ( ! empty( $job_data['file_path'] ) && file_exists( $job_data['file_path'] ) ) {
			wp_delete_file( $job_data['file_path'] );
		}

		// Delete job data..
		delete_option( "wc_affiliate_export_job_{$export_id}" );

		return $this->response_success( null, __( 'Export deleted successfully', 'wc-affiliate' ) );
	}

	/**
	 * Get list of export jobs
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_jobs(): WP_REST_Response {
		$export_jobs = ExportJob::get_jobs();

		return $this->response_success( $export_jobs );
	}

	/**
	 * Process export job
	 *
	 * @param string $export_id Export job ID.
	 *
	 * @return bool
	 */
	public function process( string $export_id ): bool {
		// Validate export_id
		if ( empty( $export_id ) || ! is_string( $export_id ) ) {
			return false;
		}

		$job_data = get_option( "wc_affiliate_export_job_{$export_id}" );
		if ( ! $job_data || ! is_array( $job_data ) ) {
			return false;
		}

		try {
			// Update status to processing
			$job_data['status']     = 'processing';
			$job_data['started_at'] = time();
			$result                 = update_option( "wc_affiliate_export_job_{$export_id}", $job_data );

			if ( ! $result ) {
				throw new Exception( __( 'Failed to update job status to processing', 'wc-affiliate' ) );
			}

			// Update progress to 25% - starting data collection
			$job_data['progress'] = 25;
			update_option( "wc_affiliate_export_job_{$export_id}", $job_data );

			// Get export data
			$data = $this->get_data( $job_data['type'], $job_data['filters'] ?? array() );

			// Update progress to 75% - data collected, generating file
			$job_data['progress'] = 75;
			update_option( "wc_affiliate_export_job_{$export_id}", $job_data );

			// Generate file
			$file_path = $this->export_file( $data, $job_data['format'], $export_id );

			if ( ! file_exists( $file_path ) ) {
				throw new Exception( __( 'Failed to generate export file', 'wc-affiliate' ) );
			}

			// Update job with completion data
			$job_data['status']         = 'completed';
			$job_data['progress']       = 100;
			$job_data['completed_at']   = time();
			$job_data['file_path']      = $file_path;
			$job_data['exported_items'] = count( $data );

			$update_result = update_option( "wc_affiliate_export_job_{$export_id}", $job_data );
			if ( ! $update_result ) {
				wca_log( "Warning: Failed to update completed job data for export {$export_id}" );
			}

			return true;
		} catch ( Exception $e ) {
			// Update status to failed
			$job_data['status']    = 'failed';
			$job_data['error']     = $e->getMessage();
			$job_data['failed_at'] = time();
			update_option( "wc_affiliate_export_job_{$export_id}", $job_data );

			wca_log( "Export job {$export_id} failed: " . $e->getMessage() );

			return false;
		}
	}

	/**
	 * Get export data count
	 *
	 * @param string $export_type Export type.
	 * @param array  $filters     Filters to apply.
	 *
	 * @return int
	 */
	protected function get_count( string $export_type, array $filters ): int {
		switch ( $export_type ) {
			case 'affiliates':
				$model = new Affiliate();

				return $model->get_count( $filters );

			case 'visits':
				$model = new Visit();

				return $model->get_count( $filters );

			case 'referrals':
				$model = new Referral();

				return $model->get_count( $filters );

			case 'payables':
				$model             = new Referral();
				$filters['status'] = array( 'approved', 'paid' );

				return $model->get_count( $filters );

			case 'transactions':
				$model = new Transaction();

				return $model->get_count( $filters );

			case 'settings':
				return 1; // Settings is always 1 item.

			case 'complete':
				$affiliate_model   = new Affiliate();
				$visit_model       = new Visit();
				$referral_model    = new Referral();
				$transaction_model = new Transaction();

				return $affiliate_model->get_count() +
						$visit_model->get_count() +
						$referral_model->get_count() +
						$transaction_model->get_count() + 1; // +1 for settings.

			default:
				return 0;
		}
	}

	/**
	 * Get export data
	 *
	 * @param string $export_type Export type.
	 * @param array  $filters     Filters to apply.
	 *
	 * @return array
	 */
	protected function get_data( string $export_type, array $filters ): array {
		// Add memory-efficient processing for large datasets
		$chunk_size = apply_filters( 'wc_affiliate_export_chunk_size', 1000 );

		switch ( $export_type ) {
			case 'affiliates':
				return $this->affiliates( $filters );

			case 'visits':
				$filters['limit'] = $chunk_size;
				return $this->visits( $filters );

			case 'referrals':
				$filters['limit'] = $chunk_size;
				return $this->referrals( $filters );

			case 'payables':
				$filters['status'] = array( 'approved', 'paid' );
				$filters['limit']  = $chunk_size;
				return $this->referrals( $filters );

			case 'transactions':
				$filters['limit'] = $chunk_size;
				return $this->transactions( $filters );

			case 'settings':
				return $this->settings();

			case 'complete':
				// For complete export, limit individual components to prevent memory issues
				$limited_filters = array_merge( $filters, array( 'limit' => $chunk_size ) );

				return array(
					'affiliates'     => $this->affiliates( $limited_filters ),
					'visits'         => $this->visits( $limited_filters ),
					'referrals'      => $this->referrals( $limited_filters ),
					'transactions'   => $this->transactions( $limited_filters ),
					'settings'       => $this->settings(),
					'exported_at'    => time(),
					'plugin_version' => WC_AFFILIATE_VERSION,
					'note'           => sprintf(
						/* translators: %d: chunk size limit */
						__( 'Data limited to %d items per type for memory efficiency', 'wc-affiliate' ),
						$chunk_size
					),
				);

			default:
				return array();
		}
	}

	/**
	 * Export affiliates data
	 *
	 * @param array $filters Filters to apply.
	 *
	 * @return array
	 */
	protected function affiliates( array $filters ): array {
		$affiliate_model = new Affiliate();

		// For export, get all affiliates without limit.
		$export_filters = array_merge( $filters, array( 'limit' => PHP_INT_MAX ) );
		$affiliates     = $affiliate_model->get_affiliates( $export_filters );

		$export_data = array();
		foreach ( $affiliates as $affiliate ) {
			$affiliate_data = $affiliate->to_array();

			$export_data[] = array(
				'id'                => $affiliate_data['id'],
				'user_id'           => $affiliate_data['id'],
				'user_login'        => $affiliate_data['user_login'],
				'user_email'        => $affiliate_data['user_email'],
				'display_name'      => $affiliate_data['display_name'],
				'user_registered'   => $affiliate_data['user_registered'],
				'status'            => $affiliate_data['status'],
				'commission_type'   => $affiliate_data['commission_type'],
				'commission_amount' => $affiliate_data['commission_amount'],
				'time_applied'      => $affiliate_data['time_applied'],
				'token'             => $affiliate_data['token'],
				'payment_method'    => $affiliate_data['payment_method'],
				'payment_details'   => $affiliate_data['payment_details'],
				'auto_payout'       => $affiliate_data['auto_payout'],
				'campaign'          => '', // Campaign is not stored per affiliate.
			);
		}

		return $export_data;
	}

	/**
	 * Export visits data
	 *
	 * @param array $filters Filters to apply.
	 *
	 * @return array
	 */
	protected function visits( array $filters ): array {
		$visit_model = new Visit();

		// For export, get all visits without limit.
		$export_filters = array_merge( $filters, array( 'limit' => PHP_INT_MAX ) );
		$visits         = $visit_model->get_visits( $export_filters );

		$export_data = array();
		foreach ( $visits as $visit ) {
			$visit_data    = $visit->to_array();
			$export_data[] = array(
				'id'           => $visit_data['id'],
				'affiliate_id' => $visit_data['affiliate'],
				'page_url'     => $visit_data['page_url'],
				'referrer_url' => $visit_data['referrer_url'],
				'campaign'     => $visit_data['campaign'],
				'ip_address'   => $visit_data['ip'],
				'visited_at'   => wp_date( 'Y-m-d H:i:s', $visit_data['time'] ),
			);
		}

		return $export_data;
	}

	/**
	 * Export referrals data
	 *
	 * @param array $filters Filters to apply.
	 *
	 * @return array
	 */
	protected function referrals( array $filters ): array {
		$referral_model = new Referral();
		$referrals      = $referral_model->get_referrals( $filters );

		$export_data = array();
		foreach ( $referrals as $referral ) {
			$export_data[] = array(
				'id'             => $referral->get_id(),
				'affiliate_id'   => $referral->get( 'affiliate' ),
				'type'           => $referral->get( 'type' ),
				'visit_id'       => $referral->get( 'visit' ),
				'order_id'       => $referral->get( 'order_id' ),
				'products'       => $referral->get( 'products' ),
				'order_total'    => $referral->get( 'order_total' ),
				'commission'     => $referral->get( 'commission' ),
				'payment_status' => $referral->get( 'payment_status' ),
				'transaction_id' => $referral->get( 'transaction_id' ),
				'created_at'     => wp_date( 'Y-m-d H:i:s', $referral->get( 'time' ) ),
			);
		}

		return $export_data;
	}

	/**
	 * Export transactions data
	 *
	 * @param array $filters Filters to apply.
	 *
	 * @return array
	 */
	protected function transactions( array $filters ): array {
		$transaction_model = new Transaction();
		$transactions      = $transaction_model->get_transactions( $filters );

		$export_data = array();
		foreach ( $transactions as $transaction ) {
			$export_data[] = array(
				'id'             => $transaction->get_id(),
				'affiliate_id'   => $transaction->get( 'affiliate' ),
				'amount'         => $transaction->get( 'amount' ),
				'payment_method' => $transaction->get( 'payment_method' ),
				'txn_id'         => $transaction->get( 'txn_id' ),
				'status'         => $transaction->get( 'status' ),
				'requested_at'   => wp_date( 'Y-m-d H:i:s', $transaction->get( 'request_at' ) ),
				'processed_at'   => $transaction->get( 'process_at' ) ? wp_date( 'Y-m-d H:i:s', $transaction->get( 'process_at' ) ) : null,
			);
		}

		return $export_data;
	}

	/**
	 * Export settings data
	 *
	 * @return array
	 */
	protected function settings(): array {
		$option_model = new Settings();

		// Export all settings from the new unified structure..
		return $option_model->get_all_settings();
	}

	/**
	 * Generate export file
	 *
	 * @param array  $data      Export data.
	 * @param string $format    File format.
	 * @param string $export_id Export ID.
	 *
	 * @return string File path.
	 */
	protected function export_file( array $data, string $format, string $export_id ): string {
		$upload_dir = wp_upload_dir();
		$export_dir = $upload_dir['basedir'] . '/wc-affiliate-exports';

		// Create export directory if it doesn't exist..
		if ( ! file_exists( $export_dir ) ) {
			wp_mkdir_p( $export_dir );
			// Add index.php for security..
			file_put_contents( $export_dir . '/index.php', '<?php // Silence is golden.' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
		}

		$filename  = "wc-affiliate-export-{$export_id}.{$format}";
		$file_path = $export_dir . '/' . $filename;

		global $wp_filesystem;

		// Initialize WordPress Filesystem if not already done
		if ( ! function_exists( 'WP_Filesystem' ) ) {
			require_once ABSPATH . 'wp-admin/includes/file.php';
		}

		if ( ! WP_Filesystem() ) {
			throw new Exception( __( 'Unable to initialize WordPress Filesystem', 'wc-affiliate' ) );
		}

		switch ( $format ) {
			case 'json':
				$content = wp_json_encode( $data, JSON_PRETTY_PRINT );
				$result  = $wp_filesystem->put_contents( $file_path, $content );
				if ( ! $result ) {
					throw new Exception( __( 'Failed to write JSON file', 'wc-affiliate' ) );
				}
				break;

			case 'csv':
				$this->csv_file( $data, $file_path );
				break;

			case 'xml':
				$this->generate_xml_file( $data, $file_path );
				break;
		}

		return $file_path;
	}

	/**
	 * Generate CSV file
	 *
	 * @param array  $data      Export data.
	 * @param string $file_path File path.
	 */
	protected function csv_file( array $data, string $file_path ): void {
		global $wp_filesystem;

		// Initialize WordPress Filesystem if not already done
		if ( ! function_exists( 'WP_Filesystem' ) ) {
			require_once ABSPATH . 'wp-admin/includes/file.php';
		}

		if ( ! WP_Filesystem() ) {
			throw new Exception( __( 'Unable to initialize WordPress Filesystem', 'wc-affiliate' ) );
		}

		$content = '';

		if ( ! empty( $data ) ) {
			// Write headers
			$first_row = reset( $data );
			if ( is_array( $first_row ) ) {
				$csv_data = array( array_keys( $first_row ) );

				// Write data rows
				foreach ( $data as $row ) {
					if ( ! is_array( $row ) ) {
						continue;
					}
					// Convert arrays/objects to JSON strings
					$row        = array_map( fn( $value ) => is_array( $value ) || is_object( $value ) ? wp_json_encode( $value ) : $value, $row );
					$csv_data[] = $row;
				}

				// Convert to CSV string
				$handle = fopen( 'php://temp', 'r+' );
				foreach ( $csv_data as $row ) {
					fputcsv( $handle, $row, ',', '"', '\\' );
				}
				rewind( $handle );
				$content = stream_get_contents( $handle );
				fclose( $handle );
			}
		}

		// Write file using WordPress Filesystem
		$result = $wp_filesystem->put_contents( $file_path, $content );
		if ( ! $result ) {
			throw new Exception( __( 'Failed to write CSV file', 'wc-affiliate' ) );
		}
	}

	/**
	 * Generate XML file
	 *
	 * @param array  $data      Export data.
	 * @param string $file_path File path.
	 */
	protected function generate_xml_file( array $data, string $file_path ): void {
		global $wp_filesystem;

		// Initialize WordPress Filesystem if not already done
		if ( ! function_exists( 'WP_Filesystem' ) ) {
			require_once ABSPATH . 'wp-admin/includes/file.php';
		}

		if ( ! WP_Filesystem() ) {
			throw new Exception( __( 'Unable to initialize WordPress Filesystem', 'wc-affiliate' ) );
		}

		$xml = new \SimpleXMLElement( '<?xml version="1.0" encoding="UTF-8"?><wc_affiliate_export></wc_affiliate_export>' );

		$this->array_to_xml( $data, $xml );

		$dom               = new \DOMDocument( '1.0', 'UTF-8' );
		$dom->formatOutput = true; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
		libxml_use_internal_errors( true );
		$dom->loadXML( $xml->asXML() );
		libxml_clear_errors();

		// Save using WordPress Filesystem
		$result = $wp_filesystem->put_contents( $file_path, $dom->saveXML() );
		if ( ! $result ) {
			throw new Exception( __( 'Failed to write XML file', 'wc-affiliate' ) );
		}
	}

	/**
	 * Convert array to XML
	 *
	 * @param array             $data Array data.
	 * @param \SimpleXMLElement $xml  XML element.
	 */
	protected function array_to_xml( array $data, \SimpleXMLElement $xml ): void {
		foreach ( $data as $key => $value ) {
			// XML element names cannot start with digits or contain invalid characters.
			if ( is_numeric( $key ) || ! preg_match( '/^[a-zA-Z_][\w\-.]*$/', (string) $key ) ) {
				$key = 'item';
			}
			if ( is_array( $value ) ) {
				$sub_node = $xml->addChild( $key );
				$this->array_to_xml( $value, $sub_node );
			} else {
				$xml->addChild( $key, htmlspecialchars( (string) $value ) );
			}
		}
	}

	/**
	 * Export CSV report directly (legacy AJAX equivalent)
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_Error|void
	 */
	public function export_csv_report( WP_REST_Request $request ) {
		$page   = sanitize_text_field( $request->get_param( 'page' ) );
		$status = sanitize_text_field( $request->get_param( 'status' ) );
		$from   = sanitize_text_field( $request->get_param( 'from' ) );
		$to     = sanitize_text_field( $request->get_param( 'to' ) );

		// Validate export type.
		if ( ! array_key_exists( $page, wca_get_affiliate_data_types() ) ) {
			return $this->response_error( __( 'Invalid export type', 'wc-affiliate' ) );
		}

		// Build filters.
		$filters = array();
		if ( '' !== $status ) {
			$filters['status'] = $status;
		}
		if ( $from && $to ) {
			$filters['start_date'] = $from;
			$filters['end_date']   = $to;
		}

		// If current user is an affiliate (not admin), filter data to their own records.
		if ( ! current_user_can( 'manage_options' ) && wca_is_affiliate() ) {
			$current_user_id      = get_current_user_id();
			$filters['affiliate'] = $current_user_id;
		}

		// Get data using the same logic as the main export.
		$data = $this->get_data( $page, $filters );

		// Handle special cases for legacy formatting.
		switch ( $page ) {
			case 'affiliates':
				$formatted_data = array();
				foreach ( $data as $item ) {
					$formatted_data[] = array(
						'id'                => $item['id'],
						'affiliate_id'      => "#{$item['user_id']}",
						'name'              => $item['display_name'],
						'user_login'        => $item['user_login'],
						'user_email'        => $item['user_email'],
						'registered'        => $item['user_registered'],
						'applied_time'      => $item['time_applied'] ?? '',
						'status'            => $item['status'],
						'commission_type'   => $item['commission_type'],
						'commission_amount' => $item['commission_amount'],
						'payment_method'    => $item['payment_method'],
						'payment_details'   => is_array( $item['payment_details'] ) ? wp_json_encode( $item['payment_details'] ) : (string) ( $item['payment_details'] ?? '' ),
						'auto_payout'       => $item['auto_payout'],
						'token'             => $item['token'],
					);
				}
				$data = $formatted_data;
				break;

			case 'payables':
				$formatted_data = array();
				foreach ( $data as $item ) {
					$user             = get_userdata( $item['affiliate_id'] );
					$formatted_data[] = array(
						'id'             => $item['id'],
						'affiliate_id'   => "#{$item['affiliate_id']}",
						'affiliate_name' => $user ? $user->display_name : '',
						'order_id'       => $item['order_id'],
						'commission'     => $item['commission'],
						'order_total'    => $item['order_total'],
						'payment_status' => $item['payment_status'],
						'date'           => $item['created_at'],
					);
				}
				$data = $formatted_data;
				break;

			case 'transactions':
				$formatted_data = array();
				foreach ( $data as $item ) {
					$user             = get_userdata( $item['affiliate_id'] );
					$formatted_data[] = array(
						'affiliate'      => $user ? $user->display_name : '',
						'amount'         => $item['amount'],
						'payment_method' => $item['payment_method'],
						'txn_id'         => $item['txn_id'],
						'request_at'     => $item['requested_at'],
						'process_at'     => $item['processed_at'] ?? '',
						'status'         => $item['status'],
					);
				}
				$data = $formatted_data;
				break;

			case 'visits':
				$formatted_data = array();
				foreach ( $data as $item ) {
					$formatted_data[] = array(
						'affiliate_id' => $item['affiliate_id'],
						'page_url'     => $item['page_url'],
						'referrer_url' => $item['referrer_url'],
						'campaign'     => $item['campaign'],
						'ip'           => $item['ip_address'],
						'time'         => $item['visited_at'],
					);
				}
				$data = $formatted_data;
				break;

			case 'referrals':
				$formatted_data = array();
				foreach ( $data as $item ) {
					$user             = get_userdata( $item['affiliate_id'] );
					$raw_products     = maybe_unserialize( $item['products'] );
					$products         = is_array( $raw_products ) ? implode( ', ', array_filter( array_map( 'strval', $raw_products ) ) ) : (string) ( $raw_products ?? '' );
					$formatted_data[] = array(
						'affiliate'      => $user ? $user->display_name : '',
						'visit'          => $item['visit_id'],
						'order_id'       => $item['order_id'],
						'products'       => $products,
						'order_total'    => $item['order_total'],
						'commission'     => $item['commission'],
						'payment_status' => $item['payment_status'],
						'time'           => $item['created_at'],
					);
				}
				$data = $formatted_data;
				break;

			case 'settings':
				// Settings export is not applicable for CSV report.
				$data = array();
				break;

			case 'complete':
				// Complete export is not applicable for CSV report.
				$data = array();
				break;
		}

		// Generate headings from data.
		$headings = array();
		if ( ! empty( $data ) ) {
			$first_row = reset( $data );
			if ( is_array( $first_row ) ) {
				$headings = array_keys( $first_row );
			}
		}

		// Set headers for CSV download.
		header( 'Content-Type: text/csv' );
		header( 'Content-Disposition: attachment;filename=' . esc_attr( $page ) . '-export-' . wp_date( 'Y-m-d' ) . '.csv' );

		// Output CSV.
		$output = fopen( 'php://output', 'w' );
		if ( ! empty( $headings ) ) {
			fputcsv( $output, $headings );
		}
		foreach ( $data as $row ) {
			fputcsv( $output, $row );
		}
		fclose( $output );

		exit;
	}

	/**
	 * Export table-specific CSV report (legacy AJAX equivalent)
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_Error|void
	 */
	public function export_table_report( WP_REST_Request $request ) {
		$name = sanitize_text_field( $request->get_param( 'name' ) );

		$allowed_tables = array( 'transactions', 'shortlinks', 'visits', 'referrals' );
		if ( ! in_array( $name, $allowed_tables, true ) ) {
			return $this->response_error( __( 'Invalid table name.', 'wc-affiliate' ) );
		}

		$data     = array();
		$headings = array();

		// If current user is an affiliate (not admin), add affiliate filter.
		$filters = array();
		if ( ! current_user_can( 'manage_options' ) && wca_is_affiliate() ) {
			$current_user_id      = get_current_user_id();
			$filters['affiliate'] = $current_user_id;
		}

		// Get data using model methods instead of direct SQL.
		switch ( $name ) {
			case 'transactions':
				$headings = array(
					'id',
					'affiliate',
					'amount',
					'payment_method',
					'txn_id',
					'request_at',
					'process_at',
					'status',
				);

				$transaction_model = new Transaction();
				$transactions      = $transaction_model->get_transactions( array_merge( $filters, array( 'limit' => PHP_INT_MAX ) ) );

				foreach ( $transactions as $transaction ) {
					$user   = get_userdata( $transaction->get( 'affiliate' ) );
					$data[] = array(
						'id'             => $transaction->get_id(),
						'affiliate'      => $user ? $user->display_name : $transaction->get( 'affiliate' ),
						'amount'         => $transaction->get( 'amount' ),
						'payment_method' => $transaction->get( 'payment_method' ),
						'txn_id'         => $transaction->get( 'txn_id' ),
						'request_at'     => wp_date( 'Y-m-d H:i:s', $transaction->get( 'request_at' ) ),
						'process_at'     => wp_date( 'Y-m-d H:i:s', $transaction->get( 'process_at' ) ),
						'status'         => $transaction->get( 'status' ),
					);
				}
				break;

			case 'shortlinks':
				$export_data = \WC_Affiliate\Models\DataExporter::export_data( 'shortlinks' );

				if ( is_wp_error( $export_data ) ) {
					return $this->response_error( $export_data->get_error_message(), 404 );
				}

				$headings = $export_data['headings'];
				$data     = $export_data['data'];
				break;

			case 'visits':
				$headings = array( 'id', 'referral', 'page_url', 'referrer_url', 'campaign', 'ip', 'time' );

				$visit_model = new Visit();
				$visits      = $visit_model->get_visits( array_merge( $filters, array( 'limit' => PHP_INT_MAX ) ) );

				foreach ( $visits as $visit ) {
					$visit_data = $visit->to_array();
					$data[]     = array(
						'id'           => $visit_data['id'],
						'referral'     => $visit_data['referral'] ?? '',
						'page_url'     => $visit_data['page_url'],
						'referrer_url' => $visit_data['referrer_url'],
						'campaign'     => $visit_data['campaign'],
						'ip'           => $visit_data['ip'],
						'time'         => wp_date( 'Y-m-d H:i:s', $visit_data['time'] ),
					);
				}
				break;

			case 'referrals':
				$headings = array(
					'id',
					'affiliate',
					'visit',
					'order_id',
					'products',
					'order_total',
					'commission',
					'payment_status',
					'time',
				);

				$referral_model = new Referral();
				$referrals      = $referral_model->get_referrals( array_merge( $filters, array( 'limit' => PHP_INT_MAX ) ) );

				foreach ( $referrals as $referral ) {
					$user   = get_userdata( $referral->get( 'affiliate' ) );
					$data[] = array(
						'affiliate'      => $user ? $user->display_name : $referral->get( 'affiliate' ),
						'visit'          => $referral->get( 'visit' ),
						'order_id'       => $referral->get( 'order_id' ),
						'products'       => implode( ', ', array_filter( array_map( 'strval', $referral->get_products() ) ) ),
						'order_total'    => $referral->get( 'order_total' ),
						'commission'     => $referral->get( 'commission' ),
						'payment_status' => $referral->get( 'payment_status' ),
						'time'           => wp_date( 'Y-m-d H:i:s', $referral->get( 'time' ) ),
					);
				}
				break;
		}

		// Check if we have data for model-based tables.
		if ( in_array( $name, array( 'transactions', 'visits', 'referrals' ), true ) && empty( $data ) ) {
			return $this->response_error( __( 'No data found for export.', 'wc-affiliate' ), 404 );
		}

		// Set headers for CSV download.
		header( 'Content-Type: text/csv' );
		header( 'Content-Disposition: attachment;filename=' . esc_attr( $name ) . '-export-' . wp_date( 'Y-m-d' ) . '.csv' );

		// Output CSV.
		$output = fopen( 'php://output', 'w' );
		fputcsv( $output, $headings );
		foreach ( $data as $row ) {
			fputcsv( $output, $row );
		}
		fclose( $output );

		exit;
	}

	/**
	 * Export all affiliate data (legacy AJAX equivalent)
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function export_all_data( WP_REST_Request $request ) {
		$meta_table = \WC_Affiliate\Models\DataExporter::get_usermeta_table();

		// If current user is an affiliate (not admin), limit to their own data.
		$current_user_id   = get_current_user_id();
		$is_affiliate_only = ! current_user_can( 'manage_options' ) && wca_is_affiliate();

		$affiliate_model = new Affiliate();
		if ( $is_affiliate_only ) {
			$affiliates_data = array();
			$affiliate       = $affiliate_model->get_by_user_id( $current_user_id );
			if ( $affiliate ) {
				$affiliates_data[] = (object) array(
					'user_id'    => $current_user_id,
					'meta_value' => $affiliate->get( 'time_applied' ) ?? time(),
				);
			}
			$affiliates = $affiliates_data;
		} else {
			$affiliates_data = $affiliate_model->get_affiliates( array( 'limit' => -1 ) );
			$affiliates      = array();
			foreach ( $affiliates_data as $affiliate ) {
				$affiliates[] = (object) array(
					'user_id'    => $affiliate->user_id,
					'meta_value' => $affiliate->get( 'time_applied' ) ?? time(),
				);
			}
		}

		$db_data = array();

		// Get data using model methods
		$visit_model       = new Visit();
		$referral_model    = new Referral();
		$transaction_model = new Transaction();

		$db_data['visits']       = $visit_model->get_visits( array( 'limit' => -1 ) );
		$db_data['referrals']    = $referral_model->get_referrals( array( 'limit' => -1 ) );
		$db_data['transactions'] = $transaction_model->get_transactions( array( 'limit' => -1 ) );

		// Check if pro version for shortlinks.
		if ( defined( 'WC_AFFILIATE_PRO' ) && WC_AFFILIATE_PRO ) {
			$db_data['shortlinks'] = array(); // Would need shortlinks model if available
		}

		$data_set = array();
		foreach ( $db_data as $table => $_data_set ) {
			if ( ! empty( $_data_set ) ) {
				foreach ( $_data_set as $_data ) {
					// Get affiliate ID from the data
					if ( $_data instanceof Visit || $_data instanceof Referral || $_data instanceof Transaction ) {
						$affiliate_id = $_data->get_affiliate_id();
					} else {
						$affiliate_id = $_data->affiliate ?? 0;
					}

					if ( ! isset( $data_set[ $table ][ $affiliate_id ] ) ) {
						$data_set[ $table ][ $affiliate_id ] = array();
					}

					switch ( $table ) {
						case 'visits':
							if ( $_data instanceof Visit ) {
								$data_set[ $table ][ $affiliate_id ][] = array(
									'referral'     => $_data->get_referral_id(),
									'page_url'     => $_data->get_page_url(),
									'referrer_url' => $_data->get_referrer_url(),
									'campaign'     => $_data->get_campaign(),
									'ip'           => $_data->get_ip(),
									'time'         => $_data->get_time(),
								);
							}
							break;

						case 'transactions':
							if ( $_data instanceof Transaction ) {
								$data_set[ $table ][ $affiliate_id ][] = array(
									'amount'         => $_data->get_amount(),
									'payment_method' => $_data->get_payment_method(),
									'txn_id'         => $_data->get_txn_id(),
									'status'         => $_data->get_status(),
									'request_at'     => $_data->get_request_time(),
									'process_at'     => $_data->get_process_time(),
								);
							}
							break;

						case 'referrals':
							if ( $_data instanceof Referral ) {
								$data_set[ $table ][ $affiliate_id ][] = array(
									'visit'          => $_data->get_visit_id(),
									'order_id'       => $_data->get_order_id(),
									'products'       => $_data->get_products(),
									'order_total'    => $_data->get_order_total(),
									'commission'     => $_data->get_commission(),
									'payment_status' => $_data->get_status(),
									'transaction_id' => $_data->get_transaction_id(),
									'time'           => $_data->get_time(),
								);
							}
							break;

						case 'shortlinks':
							$data_set[ $table ][ $affiliate_id ][] = array(
								'page_url'   => $_data->page_url,
								'campaign'   => $_data->campaign,
								'identifier' => $_data->identifier,
								'time'       => $_data->time,
							);
							break;
					}
				}
			}
		}

		$export_data = array();
		foreach ( $affiliates as $affiliate ) {
			$user = get_user_by( 'ID', $affiliate->user_id );
			if ( $user ) {
				// Use User model for consistent meta access.
				$user_model = new \WC_Affiliate\Models\User( $user->ID );

				$userdata = array(
					'user_name'       => $user->user_login,
					'applied'         => $affiliate->meta_value,
					'status'          => $user_model->get_meta( '_wc_affiliate_status' ),
					'city'            => $user_model->get_meta( '_wc_affiliate_city' ),
					'state'           => $user_model->get_meta( '_wc_affiliate_state' ),
					'country'         => $user_model->get_meta( '_wc_affiliate_country' ),
					'avatar'          => $user_model->get_meta( '_wc_affiliate_avatar' ),
					'paypal_email'    => $user_model->get_meta( '_wc_affiliate_paypal_email' ),
					'payout_method'   => $user_model->get_meta( '_wc_affiliate_payout_method' ),
					'mannual_payment' => $user_model->get_meta( '_wc_affiliate_mannual_payment' ),
				);

				$commission_type   = $user_model->get_meta( 'commission_type' );
				$commission_amount = $user_model->get_meta( 'commission_amount' );

				if ( $commission_type && $commission_amount ) {
					$userdata['commission_type']   = $commission_type;
					$userdata['commission_amount'] = $commission_amount;
				}

				foreach ( $db_data as $table => $data ) {
					if ( ! empty( $data ) && isset( $data_set[ $table ][ $affiliate->user_id ] ) ) {
						$userdata[ $table ] = $data_set[ $table ][ $affiliate->user_id ];
					}
				}
				$export_data[ $user->user_email ] = $userdata;
			}
		}

		$settings_section = array( 'basic', 'advanced', 'payout', 'mlc', 'messages', 'email', 'xdomain', 'shortlinks' );
		foreach ( $settings_section as $section ) {
			$export_data['settings'][ $section ] = get_option( "wc_affiliate_{$section}" );
		}

		/**
		 * Filter export all data before response.
		 *
		 * Allows modification of the complete export data.
		 *
		 * @since 1.0.0
		 *
		 * @param array           $export_data Complete export data array.
		 * @param WP_REST_Request $request     The original request object.
		 */
		$export_data = apply_filters( 'wc_affiliate_rest_api_export_all_data', $export_data, $request );

		return $this->response_success( $export_data );
	}
}
