<?php
/**
 * Cleanup
 *
 * Scheduled data pruning and WordPress GDPR personal-data hooks.
 *
 * Pruning: a monthly cron event deletes tracking rows older than the
 * configured retention period (default 90 days). This prevents the
 * wp_relatedly_clicks table from growing unbounded on busy sites.
 *
 * GDPR: registers export and erasure handlers so the plugin integrates
 * with WordPress's built-in "Export Personal Data" / "Erase Personal Data"
 * tools (Tools → Export/Erase Personal Data in wp-admin).
 *
 * @package Relatedly\Tracking
 * @since   2.0.0
 */

namespace Relatedly\Tracking;

use Relatedly\Core\Options;

defined( 'ABSPATH' ) || exit;

/**
 * Prunes tracking data and registers privacy export/erase callbacks.
 *
 * @since 2.0.0
 */
class Cleanup {

	/** Default number of days to retain raw tracking rows. */
	const DEFAULT_RETENTION_DAYS = 90;

	/**
	 * Plugin settings repository.
	 *
	 * @since 2.0.0
	 * @var Options
	 */
	private Options $options;

	/**
	 * Create the cleanup service.
	 *
	 * @since 2.0.0
	 * @param Options $options Plugin settings repository.
	 */
	public function __construct( Options $options ) {
		$this->options = $options;
	}

	/**
	 * Register pruning and privacy hooks.
	 *
	 * @since 2.0.0
	 */
	public function init_hooks(): void {
		add_action( 'relatedly_data_pruning', [ $this, 'prune_old_data' ] );
		add_action( 'init', [ $this, 'maybe_schedule_pruning' ] );

		add_filter( 'wp_privacy_personal_data_exporters', [ $this, 'register_exporter' ] );
		add_filter( 'wp_privacy_personal_data_erasers', [ $this, 'register_eraser' ] );
	}

	/**
	 * Schedule the monthly pruning cron event.
	 *
	 * Runs on init so translations are loaded before cron schedule labels are translated.
	 *
	 * @since 2.1.0
	 */
	public function maybe_schedule_pruning(): void {
		if ( ! wp_next_scheduled( 'relatedly_data_pruning' ) ) {
			wp_schedule_event( time(), 'monthly', 'relatedly_data_pruning' );
		}
	}

	// -------------------------------------------------------------------------
	// Pruning
	// -------------------------------------------------------------------------

	/**
	 * Delete tracking rows older than the retention period.
	 *
	 * @since 2.0.0
	 */
	public function prune_old_data(): void {
		global $wpdb;

		$days  = absint( $this->options->get( 'data_retention_days', self::DEFAULT_RETENTION_DAYS ) );
		$table = esc_sql( $wpdb->prefix . 'relatedly_clicks' );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->query(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				"DELETE FROM `{$table}` WHERE event_date < DATE_SUB( NOW(), INTERVAL %d DAY )",
				$days
			)
		);
	}

	// -------------------------------------------------------------------------
	// GDPR — export
	// -------------------------------------------------------------------------

	/**
	 * Register the personal data exporter callback.
	 *
	 * @since 2.0.0
	 * @param array<string,array<string,mixed>> $exporters Existing exporters.
	 * @return array<string,array<string,mixed>>
	 */
	public function register_exporter( array $exporters ): array {
		$exporters['relatedly'] = [
			'exporter_friendly_name' => __( 'Relatedly Click Data', 'relatedly' ),
			'callback'               => [ $this, 'export_personal_data' ],
		];
		return $exporters;
	}

	/**
	 * Export tracking rows associated with the requester's email.
	 *
	 * IP-to-email mapping is not stored, so we return a notice explaining
	 * what data is collected and how to request manual deletion.
	 *
	 * @since 2.0.0
	 * @param string $email Requester's email address.
	 * @param int    $page  Export page number.
	 * @return array<string,mixed>
	 */
	public function export_personal_data( string $email, int $page = 1 ): array {
		unset( $email, $page );

		return [
			'data' => [
				[
					'group_id'    => 'relatedly-tracking',
					'group_label' => __( 'Relatedly — Click & Impression Tracking', 'relatedly' ),
					'item_id'     => 'relatedly-tracking-notice',
					'data'        => [
						[
							'name'  => __( 'Data collected', 'relatedly' ),
							'value' => __( 'Source post ID, clicked post ID, display type, event type, IP address, user agent, timestamp.', 'relatedly' ),
						],
						[
							'name'  => __( 'Retention period', 'relatedly' ),
							'value' => sprintf(
								/* translators: %d: number of days */
								__( '%d days (configurable in Relatedly settings).', 'relatedly' ),
								absint( $this->options->get( 'data_retention_days', self::DEFAULT_RETENTION_DAYS ) )
							),
						],
					],
				],
			],
			'done' => true,
		];
	}

	// -------------------------------------------------------------------------
	// GDPR — erasure
	// -------------------------------------------------------------------------

	/**
	 * Register the personal data eraser callback.
	 *
	 * @since 2.0.0
	 * @param array<string,array<string,mixed>> $erasers Existing erasers.
	 * @return array<string,array<string,mixed>>
	 */
	public function register_eraser( array $erasers ): array {
		$erasers['relatedly'] = [
			'eraser_friendly_name' => __( 'Relatedly Click Data', 'relatedly' ),
			'callback'             => [ $this, 'erase_personal_data' ],
		];
		return $erasers;
	}

	/**
	 * Erase tracking rows by IP address derived from the user's account.
	 *
	 * WordPress does not pass the IP directly; we look up the user by email
	 * and attempt to match stored IPs. This is best-effort — IP addresses
	 * are not reliably linked to user accounts.
	 *
	 * @since 2.0.0
	 * @param string $email Requester's email address.
	 * @param int    $page  Erasure page number.
	 * @return array<string,mixed>
	 */
	public function erase_personal_data( string $email, int $page = 1 ): array {
		global $wpdb;

		unset( $page );

		$user = get_user_by( 'email', $email );
		if ( ! $user ) {
			return [
				'items_removed'  => 0,
				'items_retained' => 0,
				'messages'       => [],
				'done'           => true,
			];
		}

		// We cannot reliably map a WP user to an IP address stored in the tracking table.
		// The "Purge all tracking data" button in settings provides a manual erasure path.
		return [
			'items_removed'  => 0,
			'items_retained' => 0,
			'messages'       => [
				__( 'Relatedly stores anonymous click data by IP address. Use the "Purge tracking data" button in Relatedly → Settings to erase all tracking records.', 'relatedly' ),
			],
			'done'           => true,
		];
	}
}
