<?php
/**
 * Data export functionality for GDPR compliance.
 *
 * @package TrustLens
 * @since   1.0.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * Data export class.
 *
 * Handles data export and erasure for GDPR compliance.
 *
 * @since 1.0.0
 */
class TrustLens_Data_Export {

	/**
	 * Single instance.
	 *
	 * @var TrustLens_Data_Export|null
	 */
	private static ?TrustLens_Data_Export $instance = null;

	/**
	 * Get instance.
	 *
	 * @since 1.0.0
	 * @return TrustLens_Data_Export
	 */
	public static function instance(): TrustLens_Data_Export {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Serialize one row of fields into a CSV line.
	 *
	 * The single, canonical CSV-cell encoder for the whole plugin: every export
	 * path (admin AJAX export, scheduled/bulk export) routes through here so the
	 * formula-injection guard can never be forgotten in one path while present
	 * in another.
	 *
	 * A cell beginning with =, +, -, @, tab or carriage return can execute as a
	 * formula when the file is opened in Excel/Sheets, so it is prefixed with a
	 * single quote to force literal text; cells containing a comma, quote or
	 * newline are wrapped in double quotes with embedded quotes doubled.
	 *
	 * @since 1.3.0
	 * @param array $fields Ordered cell values for the row.
	 * @return string CSV-encoded line (no trailing newline).
	 */
	public static function to_csv_row( array $fields ): string {
		$escaped = array();
		foreach ( $fields as $field ) {
			$field = (string) $field;

			if ( '' !== $field && false !== strpos( "=+-@\t\r", $field[0] ) ) {
				$field = "'" . $field;
			}

			if ( false !== strpos( $field, ',' ) || false !== strpos( $field, '"' ) || false !== strpos( $field, "\n" ) ) {
				$field = '"' . str_replace( '"', '""', $field ) . '"';
			}

			$escaped[] = $field;
		}

		return implode( ',', $escaped );
	}

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 */
	private function __construct() {
		$this->init_hooks();
	}

	/**
	 * Initialize hooks.
	 *
	 * @since 1.0.0
	 */
	private function init_hooks(): void {
		// WordPress privacy hooks.
		add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'register_exporter' ) );
		add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_eraser' ) );

		// Admin export action.
		add_action( 'wp_ajax_wstl_export_customers', array( $this, 'ajax_export_customers' ) );
		add_action( 'wp_ajax_wstl_export_customer_data', array( $this, 'ajax_export_customer_data' ) );
	}

	/**
	 * Register data exporter.
	 *
	 * @since 1.0.0
	 * @param array $exporters Registered exporters.
	 * @return array Modified exporters.
	 */
	public function register_exporter( array $exporters ): array {
		$exporters['trustlens'] = array(
			'exporter_friendly_name' => __( 'TrustLens Customer Data', 'trustlens' ),
			'callback'               => array( $this, 'export_personal_data' ),
		);
		return $exporters;
	}

	/**
	 * Register data eraser.
	 *
	 * @since 1.0.0
	 * @param array $erasers Registered erasers.
	 * @return array Modified erasers.
	 */
	public function register_eraser( array $erasers ): array {
		$erasers['trustlens'] = array(
			'eraser_friendly_name' => __( 'TrustLens Customer Data', 'trustlens' ),
			'callback'             => array( $this, 'erase_personal_data' ),
		);
		return $erasers;
	}

	/**
	 * Export personal data for GDPR request.
	 *
	 * @since 1.0.0
	 * @param string $email_address Email address.
	 * @param int    $page          Page number.
	 * @return array Export data.
	 */
	public function export_personal_data( string $email_address, int $page = 1 ): array {
		$export_items = array();
		$email_hash = wstl_get_email_hash( $email_address );
		$customer = wstl_get_customer( $email_hash );

		if ( $customer ) {
			// Customer profile data.
			$data = array(
				array(
					'name'  => __( 'Email Hash', 'trustlens' ),
					'value' => $customer->email_hash,
				),
				array(
					'name'  => __( 'Trust Score', 'trustlens' ),
					'value' => $customer->trust_score . '/100',
				),
				array(
					'name'  => __( 'Segment', 'trustlens' ),
					'value' => wstl_get_segment_label( $customer->segment ),
				),
				array(
					'name'  => __( 'Total Orders', 'trustlens' ),
					'value' => $customer->total_orders,
				),
				array(
					'name'  => __( 'Total Refunds', 'trustlens' ),
					'value' => $customer->total_refunds,
				),
				array(
					'name'  => __( 'Return Rate', 'trustlens' ),
					'value' => number_format( $customer->return_rate, 1 ) . '%',
				),
				array(
					'name'  => __( 'Is Blocked', 'trustlens' ),
					'value' => $customer->is_blocked ? __( 'Yes', 'trustlens' ) : __( 'No', 'trustlens' ),
				),
				array(
					'name'  => __( 'Is Allowlisted', 'trustlens' ),
					'value' => $customer->is_allowlisted ? __( 'Yes', 'trustlens' ) : __( 'No', 'trustlens' ),
				),
				array(
					'name'  => __( 'Is Flagged', 'trustlens' ),
					'value' => ! empty( $customer->is_flagged ) ? __( 'Yes', 'trustlens' ) : __( 'No', 'trustlens' ),
				),
				array(
					'name'  => __( 'First Order Date', 'trustlens' ),
					'value' => $customer->first_order_date ?: __( 'N/A', 'trustlens' ),
				),
				array(
					'name'  => __( 'Last Order Date', 'trustlens' ),
					'value' => $customer->last_order_date ?: __( 'N/A', 'trustlens' ),
				),
			);

			$export_items[] = array(
				'group_id'          => 'trustlens_customer',
				'group_label'       => __( 'TrustLens Customer Profile', 'trustlens' ),
				'group_description' => __( 'Customer trust and behavior data collected by TrustLens.', 'trustlens' ),
				'item_id'           => 'customer-' . $customer->id,
				'data'              => $data,
			);

			foreach ( $this->get_events( $email_hash, 100 ) as $event ) {
				$export_items[] = array(
					'group_id'          => 'trustlens_events',
					'group_label'       => __( 'TrustLens Events', 'trustlens' ),
					'group_description' => __( 'Activity events tracked by TrustLens.', 'trustlens' ),
					'item_id'           => 'event-' . $event->id,
					'data'              => array(
						array(
							'name'  => __( 'Event Type', 'trustlens' ),
							'value' => $event->event_type,
						),
						array(
							'name'  => __( 'Date', 'trustlens' ),
							'value' => $event->created_at,
						),
						array(
							'name'  => __( 'Order ID', 'trustlens' ),
							'value' => $event->order_id ?: __( 'N/A', 'trustlens' ),
						),
					),
				);
			}

			foreach ( $this->get_signals( $email_hash ) as $signal ) {
				$export_items[] = array(
					'group_id'          => 'trustlens_signals',
					'group_label'       => __( 'TrustLens Signals', 'trustlens' ),
					'group_description' => __( 'Trust signals generated for this customer.', 'trustlens' ),
					'item_id'           => 'signal-' . $signal->id,
					'data'              => array(
						array(
							'name'  => __( 'Module', 'trustlens' ),
							'value' => $signal->module_id,
						),
						array(
							'name'  => __( 'Score Impact', 'trustlens' ),
							'value' => (int) $signal->signal_score,
						),
						array(
							'name'  => __( 'Reason', 'trustlens' ),
							'value' => $signal->signal_reason ?: __( 'N/A', 'trustlens' ),
						),
						array(
							'name'  => __( 'Date', 'trustlens' ),
							'value' => $signal->created_at,
						),
					),
				);
			}

			foreach ( $this->get_category_stats( $email_hash ) as $category_stat ) {
				$export_items[] = array(
					'group_id'          => 'trustlens_category_stats',
					'group_label'       => __( 'TrustLens Category Stats', 'trustlens' ),
					'group_description' => __( 'Category-level order and refund statistics tracked by TrustLens.', 'trustlens' ),
					'item_id'           => 'category-' . $category_stat['category'],
					'data'              => array(
						array(
							'name'  => __( 'Category', 'trustlens' ),
							'value' => $category_stat['name'],
						),
						array(
							'name'  => __( 'Orders', 'trustlens' ),
							'value' => $category_stat['order_count'],
						),
						array(
							'name'  => __( 'Order Value', 'trustlens' ),
							'value' => wc_price( (float) $category_stat['order_value'] ),
						),
						array(
							'name'  => __( 'Refunds', 'trustlens' ),
							'value' => $category_stat['refund_count'],
						),
						array(
							'name'  => __( 'Refund Value', 'trustlens' ),
							'value' => wc_price( (float) $category_stat['refund_value'] ),
						),
						array(
							'name'  => __( 'Return Rate', 'trustlens' ),
							'value' => $category_stat['return_rate'] . '%',
						),
					),
				);
			}

			foreach ( $this->get_fingerprints( $email_hash ) as $fingerprint ) {
				$export_items[] = array(
					'group_id'          => 'trustlens_fingerprints',
					'group_label'       => __( 'TrustLens Linked Account Fingerprints', 'trustlens' ),
					'group_description' => __( 'Pseudonymized fingerprint matches used for linked-account detection.', 'trustlens' ),
					'item_id'           => 'fingerprint-' . $fingerprint->fingerprint_type . '-' . $fingerprint->fingerprint_hash,
					'data'              => array(
						array(
							'name'  => __( 'Fingerprint Type', 'trustlens' ),
							'value' => $fingerprint->fingerprint_type,
						),
						array(
							'name'  => __( 'Fingerprint Hash', 'trustlens' ),
							'value' => $fingerprint->fingerprint_hash,
						),
						array(
							'name'  => __( 'Times Seen', 'trustlens' ),
							'value' => (int) $fingerprint->times_seen,
						),
						array(
							'name'  => __( 'First Seen', 'trustlens' ),
							'value' => $fingerprint->first_seen,
						),
						array(
							'name'  => __( 'Last Seen', 'trustlens' ),
							'value' => $fingerprint->last_seen,
						),
					),
				);
			}

			foreach ( $this->get_automation_logs( $email_hash ) as $automation_log ) {
				$export_items[] = array(
					'group_id'          => 'trustlens_automation_logs',
					'group_label'       => __( 'TrustLens Automation Logs', 'trustlens' ),
					'group_description' => __( 'Automation actions triggered for this customer.', 'trustlens' ),
					'item_id'           => 'automation-' . $automation_log->id,
					'data'              => array(
						array(
							'name'  => __( 'Rule ID', 'trustlens' ),
							'value' => $automation_log->rule_id,
						),
						array(
							'name'  => __( 'Rule Name', 'trustlens' ),
							'value' => $automation_log->rule_name ?: __( 'N/A', 'trustlens' ),
						),
						array(
							'name'  => __( 'Trigger', 'trustlens' ),
							'value' => $automation_log->trigger_type,
						),
						array(
							'name'  => __( 'Action', 'trustlens' ),
							'value' => $automation_log->action_type,
						),
						array(
							'name'  => __( 'Order ID', 'trustlens' ),
							'value' => $automation_log->order_id ?: __( 'N/A', 'trustlens' ),
						),
						array(
							'name'  => __( 'Date', 'trustlens' ),
							'value' => $automation_log->created_at,
						),
					),
				);
			}
		}

		return array(
			'data' => $export_items,
			'done' => true,
		);
	}

	/**
	 * Erase personal data for GDPR request.
	 *
	 * @since 1.0.0
	 * @param string $email_address Email address.
	 * @param int    $page          Page number.
	 * @return array Erasure result.
	 */
	public function erase_personal_data( string $email_address, int $page = 1 ): array {
		global $wpdb;

		$email_hash = wstl_get_email_hash( $email_address );
		$items_removed = 0;

		// Delete customer record.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name from $wpdb->prefix, safe.
		$customer_deleted = $wpdb->delete(
			$wpdb->prefix . 'trustlens_customers',
			array( 'email_hash' => $email_hash )
		);

		if ( $customer_deleted ) {
			$items_removed++;
		}

		// Delete events.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name from $wpdb->prefix, safe.
		$events_deleted = $wpdb->delete(
			$wpdb->prefix . 'trustlens_events',
			array( 'email_hash' => $email_hash )
		);

		$items_removed += (int) $events_deleted;

		// Delete signals.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name from $wpdb->prefix, safe.
		$signals_deleted = $wpdb->delete(
			$wpdb->prefix . 'trustlens_signals',
			array( 'email_hash' => $email_hash )
		);

		$items_removed += (int) $signals_deleted;

		// Delete category stats.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name from $wpdb->prefix, safe.
		$category_stats_deleted = $wpdb->delete(
			$wpdb->prefix . 'trustlens_category_stats',
			array( 'email_hash' => $email_hash )
		);

		$items_removed += (int) $category_stats_deleted;

		// Delete fingerprints.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name from $wpdb->prefix, safe.
		$fingerprints_deleted = $wpdb->delete(
			$wpdb->prefix . 'trustlens_fingerprints',
			array( 'email_hash' => $email_hash )
		);

		$items_removed += (int) $fingerprints_deleted;

		// Delete automation logs.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name from $wpdb->prefix, safe.
		$automation_logs_deleted = $wpdb->delete(
			$wpdb->prefix . 'trustlens_automation_logs',
			array( 'email_hash' => $email_hash )
		);

		$items_removed += (int) $automation_logs_deleted;

		return array(
			'items_removed'  => $items_removed,
			'items_retained' => false,
			'messages'       => array(),
			'done'           => true,
		);
	}

	/**
	 * AJAX: Export all customers to CSV.
	 *
	 * @since 1.0.0
	 */
	public function ajax_export_customers(): void {
		check_ajax_referer( 'trustlens_admin', 'nonce' );

		if ( ! current_user_can( 'manage_woocommerce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'trustlens' ) ) );
		}

		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Export query, table name from $wpdb->prefix is safe.
		$customers = $wpdb->get_results(
			"SELECT * FROM {$wpdb->prefix}trustlens_customers ORDER BY trust_score ASC"
		);

		$csv_data = array();
		$csv_data[] = array(
			'Email',
			'Trust Score',
			'Segment',
			'Total Orders',
			'Total Refunds',
			'Return Rate',
			'Total Order Value',
			'Total Refund Value',
			'Is Blocked',
			'Is Allowlisted',
			'Is Flagged',
			'First Order',
			'Last Order',
		);

		foreach ( $customers as $customer ) {
			$csv_data[] = array(
				$customer->customer_email,
				$customer->trust_score,
				$customer->segment,
				$customer->total_orders,
				$customer->total_refunds,
				number_format( $customer->return_rate, 1 ) . '%',
				$customer->total_order_value,
				$customer->total_refund_value,
				$customer->is_blocked ? 'Yes' : 'No',
				$customer->is_allowlisted ? 'Yes' : 'No',
				! empty( $customer->is_flagged ) ? 'Yes' : 'No',
				$customer->first_order_date,
				$customer->last_order_date,
			);
		}

		$filename = 'trustlens-customers-' . gmdate( 'Y-m-d' ) . '.csv';

		wp_send_json_success( array(
			'filename' => $filename,
			'data'     => $csv_data,
		) );
	}

	/**
	 * AJAX: Export single customer data.
	 *
	 * @since 1.0.0
	 */
	public function ajax_export_customer_data(): void {
		check_ajax_referer( 'trustlens_admin', 'nonce' );

		if ( ! current_user_can( 'manage_woocommerce' ) ) {
			wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'trustlens' ) ) );
		}

		$email_hash = sanitize_text_field( wp_unslash( $_POST['email_hash'] ?? '' ) );

		if ( empty( $email_hash ) ) {
			wp_send_json_error( array( 'message' => __( 'Invalid customer.', 'trustlens' ) ) );
		}

		$customer = wstl_get_customer( $email_hash );

		if ( ! $customer ) {
			wp_send_json_error( array( 'message' => __( 'Customer not found.', 'trustlens' ) ) );
		}

		$export_data = array(
			'customer'        => $customer,
			'events'          => $this->get_events( $email_hash ),
			'signals'         => $this->get_signals( $email_hash ),
			'category_stats'  => $this->get_category_stats( $email_hash ),
			'fingerprints'    => $this->get_fingerprints( $email_hash ),
			'automation_logs' => $this->get_automation_logs( $email_hash ),
		);

		$filename = 'trustlens-customer-' . substr( $email_hash, 0, 8 ) . '-' . gmdate( 'Y-m-d' ) . '.json';

		wp_send_json_success( array(
			'filename' => $filename,
			'data'     => $export_data,
		) );
	}

	/**
	 * Get customer events.
	 *
	 * @since 1.1.5
	 * @param string   $email_hash Customer email hash.
	 * @param int|null $limit      Optional row limit.
	 * @return array
	 */
	private function get_events( string $email_hash, ?int $limit = null ): array {
		global $wpdb;

		if ( null !== $limit ) {
			// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix, safe.
			return $wpdb->get_results( $wpdb->prepare(
				"SELECT * FROM {$wpdb->prefix}trustlens_events WHERE email_hash = %s ORDER BY created_at DESC LIMIT %d",
				$email_hash,
				$limit
			) );
			// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
		}

		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix, safe.
		return $wpdb->get_results( $wpdb->prepare(
			"SELECT * FROM {$wpdb->prefix}trustlens_events WHERE email_hash = %s ORDER BY created_at DESC",
			$email_hash
		) );
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
	}

	/**
	 * Get customer trust signals.
	 *
	 * @since 1.1.5
	 * @param string $email_hash Customer email hash.
	 * @return array
	 */
	private function get_signals( string $email_hash ): array {
		global $wpdb;

		// Exclude the internal 'score_update' snapshot rows — those are the daily
		// trust-score time series the trends chart reads, not per-customer risk
		// signals, so they must not appear in the "Signals" export group (mirrors
		// TrustLens_Score_Calculator::get_signals()). Including them would also flood
		// a long-lived customer's export with hundreds of snapshot rows.
		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix, safe.
		return $wpdb->get_results( $wpdb->prepare(
			"SELECT * FROM {$wpdb->prefix}trustlens_signals WHERE email_hash = %s AND module_id != 'score_update' ORDER BY created_at DESC",
			$email_hash
		) );
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
	}

	/**
	 * Get customer category stats.
	 *
	 * @since 1.1.5
	 * @param string $email_hash Customer email hash.
	 * @return array
	 */
	private function get_category_stats( string $email_hash ): array {
		$modules = TrustLens::instance()->get_modules();
		$categories_module = $modules['categories'] ?? null;

		if ( $categories_module instanceof TrustLens_Module_Categories ) {
			return $categories_module->get_customer_category_stats( $email_hash );
		}

		return array();
	}

	/**
	 * Get customer fingerprints.
	 *
	 * @since 1.1.5
	 * @param string $email_hash Customer email hash.
	 * @return array
	 */
	private function get_fingerprints( string $email_hash ): array {
		if ( ! class_exists( 'TrustLens_Linked_Accounts' ) ) {
			return array();
		}

		return TrustLens_Linked_Accounts::get_customer_fingerprints( $email_hash );
	}

	/**
	 * Get automation logs for a customer.
	 *
	 * @since 1.1.5
	 * @param string $email_hash Customer email hash.
	 * @return array
	 */
	private function get_automation_logs( string $email_hash ): array {
		global $wpdb;

		if ( ! class_exists( 'TrustLens_Automation' ) || ! wstl_can_use_pro() ) {
			return array();
		}

		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix, safe.
		return $wpdb->get_results( $wpdb->prepare(
			"SELECT * FROM {$wpdb->prefix}trustlens_automation_logs WHERE email_hash = %s ORDER BY created_at DESC",
			$email_hash
		) );
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
	}
}
