<?php
/**
 * WordPress privacy integration.
 *
 * @package VoidPulse
 */

namespace VoidPulse\Privacy;

defined( 'ABSPATH' ) || exit;

final class Privacy {
	/**
	 * Whether the most recent WooCommerce order page was the final page.
	 *
	 * @var bool
	 */
	private $last_batch_done = true;

	/**
	 * Register privacy hooks.
	 *
	 * @return void
	 */
	public function register() {
		add_action( 'admin_init', array( $this, 'policy_text' ) );
		add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'exporters' ) );
		add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'erasers' ) );
	}

	/**
	 * Register the cached-display-data exporter.
	 *
	 * @param array<string,mixed> $exporters Exporters.
	 * @return array<string,mixed>
	 */
	public function exporters( $exporters ) {
		$exporters['voidpulse-social-proof-for-woocommerce'] = array(
			'exporter_friendly_name' => __( 'VoidPulse cached sale notifications', 'voidpulse-social-proof-for-woocommerce' ),
			'callback'               => array( $this, 'export_personal_data' ),
		);
		return $exporters;
	}

	/**
	 * Register the cached-display-data eraser.
	 *
	 * @param array<string,mixed> $erasers Erasers.
	 * @return array<string,mixed>
	 */
	public function erasers( $erasers ) {
		$erasers['voidpulse-social-proof-for-woocommerce'] = array(
			'eraser_friendly_name' => __( 'VoidPulse cached sale notifications', 'voidpulse-social-proof-for-woocommerce' ),
			'callback'             => array( $this, 'erase_personal_data' ),
		);
		return $erasers;
	}

	/**
	 * Export cached public display data associated with matching WC orders.
	 *
	 * @param string $email_address Email.
	 * @param int    $page Page.
	 * @return array<string,mixed>
	 */
	public function export_personal_data( $email_address, $page = 1 ) {
		$records = $this->cached_records_for_email( $email_address, $page );
		$data    = array();
		foreach ( $records as $record ) {
			$payload = json_decode( $record['payload'], true );
			if ( ! is_array( $payload ) ) {
				continue;
			}
			$data[] = array(
				'group_id'    => 'voidpulse-social-proof-for-woocommerce',
				'group_label' => __( 'VoidPulse cached sale notifications', 'voidpulse-social-proof-for-woocommerce' ),
				'item_id'     => 'voidpulse-event-' . hash( 'sha256', $record['source_record_id'] ),
				'data'        => array(
					array( 'name' => __( 'Display name', 'voidpulse-social-proof-for-woocommerce' ), 'value' => sanitize_text_field( isset( $payload['customer'] ) ? $payload['customer'] : '' ) ),
					array( 'name' => __( 'General location', 'voidpulse-social-proof-for-woocommerce' ), 'value' => sanitize_text_field( isset( $payload['location'] ) ? $payload['location'] : '' ) ),
					array( 'name' => __( 'Product', 'voidpulse-social-proof-for-woocommerce' ), 'value' => sanitize_text_field( isset( $payload['product'] ) ? $payload['product'] : '' ) ),
				),
			);
		}
		return array( 'data' => $data, 'done' => $this->last_batch_done );
	}

	/**
	 * Erase cached public display data associated with matching WC orders.
	 *
	 * @param string $email_address Email.
	 * @param int    $page Page.
	 * @return array<string,mixed>
	 */
	public function erase_personal_data( $email_address, $page = 1 ) {
		global $wpdb;
		$records = $this->cached_records_for_email( $email_address, $page );
		foreach ( $records as $record ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- WordPress privacy erasure must delete matching rows immediately from the plugin-owned event table.
			$wpdb->delete( $wpdb->prefix . 'voidpulse_events', array( 'source_record_id' => $record['source_record_id'] ), array( '%s' ) );
		}
		if ( $records ) {
			wp_cache_delete( 'events_newest', 'voidpulse' );
			wp_cache_delete( 'events_random', 'voidpulse' );
		}
		return array(
			'items_removed'  => (bool) $records,
			'items_retained' => false,
			'messages'       => array(),
			'done'           => $this->last_batch_done,
		);
	}

	/**
	 * Resolve at most 100 cached records through WooCommerce CRUD order matching.
	 *
	 * @param string $email_address Email.
	 * @param int    $page Page.
	 * @return array<int,array<string,string>>
	 */
	private function cached_records_for_email( $email_address, $page ) {
		global $wpdb;
		$email = sanitize_email( $email_address );
		if ( ! $email || ! function_exists( 'wc_get_orders' ) ) {
			$this->last_batch_done = true;
			return array();
		}
		$orders = wc_get_orders(
			array(
				'billing_email' => $email,
				'limit'         => 20,
				'page'          => max( 1, absint( $page ) ),
				'return'        => 'objects',
			)
		);
		$this->last_batch_done = count( $orders ) < 20;
		$hashes = array();
		foreach ( $orders as $order ) {
			foreach ( $order->get_items( 'line_item' ) as $item_id => $item ) {
				unset( $item );
				$hashes[] = hash_hmac( 'sha256', $order->get_id() . ':' . $item_id, wp_salt( 'nonce' ) );
			}
		}
		$hashes = array_slice( array_values( array_unique( $hashes ) ), 0, 100 );
		if ( ! $hashes ) {
			return array();
		}
		$placeholders = implode( ',', array_fill( 0, count( $hashes ), '%s' ) );
		$params       = array_merge( array( $wpdb->prefix . 'voidpulse_events' ), $hashes );
		$sql          = $wpdb->prepare( "SELECT source_record_id,payload FROM %i WHERE source_record_id IN ({$placeholders}) LIMIT 100", $params ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Only placeholder count is generated; values and identifier are prepared.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Request-specific privacy export lookup; caching personal data is inappropriate and values are prepared above.
		return $wpdb->get_results( $sql, ARRAY_A );
	}

	/**
	 * Add suggested policy text.
	 *
	 * @return void
	 */
	public function policy_text() {
		if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) {
			return;
		}
		$text = '<p>' . esc_html__( 'VoidPulse can display shortened customer names, general locations, product names, and relative purchase times derived from eligible WooCommerce orders. It does not expose street addresses, email addresses, telephone numbers, payment data, or order numbers.', 'voidpulse-social-proof-for-woocommerce' ) . '</p>';
		$text .= '<p>' . esc_html__( 'Basic display and click counts are stored locally for up to one year so reporting history survives edition changes. Freemius provides optional licensing, updates, and account services; no WooCommerce customer or order data is sent to Freemius by VoidPulse.', 'voidpulse-social-proof-for-woocommerce' ) . '</p>';
		$text .= '<p>' . esc_html__( 'Optional detailed analytics and viewer counting in eligible complete packages are disabled by default and require an explicit setting plus an affirmative consent-integration filter. They use random client identifiers stored in the visitor browser and hashed before local storage; they do not store raw IP addresses.', 'voidpulse-social-proof-for-woocommerce' ) . '</p>';
		wp_add_privacy_policy_content( __( 'VoidPulse', 'voidpulse-social-proof-for-woocommerce' ), wp_kses_post( $text ) );
	}
}
