<?php
/**
 * Privacy — WordPress Privacy API integration
 *
 * Registers a personal-data exporter and eraser so admins can fulfil
 * GDPR data-access and erasure requests from the WordPress privacy tools
 * (Tools → Erase Personal Data / Export Personal Data).
 *
 * ## Exporter scope
 *   - WordPress account fields + _ambh_* user-meta
 *   - Fundraiser posts authored by the user
 *   - Donor record + linked donation rows
 *   - Consent records
 *
 * ## Eraser scope
 *   - Anonymise every linked donor via DonorsModule::anonymize()
 *   - Clear all _ambh_* user-meta
 *   - Delete profile-picture attachment
 *   - Set fundraiser posts to 'draft' + delete featured images
 *   - Withdraw all consent records for the user
 *   - Notify registered connectors via on_erasure()
 *
 * @package AmbassadorHub\Privacy
 */

namespace AmbassadorHub\Privacy;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AmbassadorHub\Connectors\ConnectorRegistry;
use AmbassadorHub\Consent\ConsentManager;
use AmbassadorHub\Donations\DonationsModule;
use AmbassadorHub\Donors\DonorsModule;
use AmbassadorHub\Fundraisers\FundraisersModule;

/**
 * Privacy API integration for AmbassadorHub.
 */
final class Privacy {

	/**
	 * Register exporter, eraser, and privacy policy suggestion with WordPress privacy tools.
	 *
	 * @return void
	 */
	public static function init(): void {
		add_filter( 'wp_privacy_personal_data_exporters', array( self::class, 'register_exporter' ) );
		add_filter( 'wp_privacy_personal_data_erasers', array( self::class, 'register_eraser' ) );

		// Suggest privacy policy text under Settings → Privacy → Policy Guide.
		add_action( 'admin_init', array( self::class, 'add_privacy_policy_content' ) );
	}

	/**
	 * Register a suggested privacy policy text block with WordPress.
	 *
	 * Visible in the wp-admin Privacy Policy Guide (Settings → Privacy → Policy Guide).
	 * Site owners should review and incorporate this text into their actual privacy policy.
	 *
	 * @return void
	 */
	public static function add_privacy_policy_content(): void {
		if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) {
			return;
		}

		wp_add_privacy_policy_content(
			esc_html__( 'AmbassadorHub', 'ambassadorhub' ),
			self::get_policy_content()
		);
	}

	/**
	 * Build the suggested privacy policy text for this plugin.
	 *
	 * Returns HTML-formatted content ready for wp_add_privacy_policy_content().
	 *
	 * @return string
	 */
	private static function get_policy_content(): string {
		ob_start();
		?>
		<h2><?php esc_html_e( 'AmbassadorHub — Peer-to-peer fundraising', 'ambassadorhub' ); ?></h2>

		<h3><?php esc_html_e( 'What personal data we collect and why', 'ambassadorhub' ); ?></h3>
		<p><?php esc_html_e( 'This website uses AmbassadorHub to manage a peer-to-peer fundraising platform. Depending on how you interact with this site, we may collect the following personal data.', 'ambassadorhub' ); ?></p>

		<h4><?php esc_html_e( 'Ambassador accounts', 'ambassadorhub' ); ?></h4>
		<p><?php esc_html_e( 'When you register as an ambassador, we store:', 'ambassadorhub' ); ?></p>
		<ul>
			<li><?php esc_html_e( 'Your name and email address', 'ambassadorhub' ); ?></li>
			<li><?php esc_html_e( 'Your registration date and email verification status', 'ambassadorhub' ); ?></li>
			<li><?php esc_html_e( 'Any additional profile information you provide (such as phone number, address, or other fields defined by this site)', 'ambassadorhub' ); ?></li>
			<li><?php esc_html_e( 'Your fundraiser pages and their content', 'ambassadorhub' ); ?></li>
		</ul>
		<p><?php esc_html_e( 'This information is used to manage your ambassador account, display your fundraiser, and send you platform-related communications.', 'ambassadorhub' ); ?></p>

		<h4><?php esc_html_e( 'Donors', 'ambassadorhub' ); ?></h4>
		<p><?php esc_html_e( 'When you make a donation through this platform, we store:', 'ambassadorhub' ); ?></p>
		<ul>
			<li><?php esc_html_e( 'Your name and email address', 'ambassadorhub' ); ?></li>
			<li><?php esc_html_e( 'The donation amount, currency, and status', 'ambassadorhub' ); ?></li>
			<li><?php esc_html_e( 'Any public message you choose to leave', 'ambassadorhub' ); ?></li>
			<li><?php esc_html_e( 'A transaction reference provided by the payment provider', 'ambassadorhub' ); ?></li>
		</ul>
		<p><?php esc_html_e( 'Payment card details are never stored on this website. Payments are processed by a third-party payment provider; please refer to their privacy policy for details.', 'ambassadorhub' ); ?></p>

		<h4><?php esc_html_e( 'Consent records', 'ambassadorhub' ); ?></h4>
		<p><?php esc_html_e( 'If you have given consent for marketing or other communications, we store a record of that consent, including the date, the purpose, and the channel (e.g. email). This record is kept for legal accountability even after consent is withdrawn.', 'ambassadorhub' ); ?></p>

		<h3><?php esc_html_e( 'How long we retain your data', 'ambassadorhub' ); ?></h3>
		<ul>
			<li><?php esc_html_e( 'Ambassador account data is retained for as long as your account exists.', 'ambassadorhub' ); ?></li>
			<li><?php esc_html_e( 'Donation records are retained indefinitely for financial and audit purposes. Your name and email address linked to a donation can be anonymised on request.', 'ambassadorhub' ); ?></li>
			<li><?php esc_html_e( 'Fundraiser pages are retained for as long as the related campaign is active. On an erasure request the content is depublished.', 'ambassadorhub' ); ?></li>
			<li><?php esc_html_e( 'Automation logs (internal records of sent emails and other actions) are automatically deleted after [X] days.', 'ambassadorhub' ); ?></li>
		</ul>

		<h3><?php esc_html_e( 'Who we share your data with', 'ambassadorhub' ); ?></h3>
		<p><?php esc_html_e( 'Your personal data may be shared with:', 'ambassadorhub' ); ?></p>
		<ul>
			<li><?php esc_html_e( 'Payment providers (to process donations)', 'ambassadorhub' ); ?></li>
			<li><?php esc_html_e( 'Email marketing or CRM platforms (only if you have given consent, and only to the extent required to send you communications you have opted into)', 'ambassadorhub' ); ?></li>
		</ul>
		<p><?php esc_html_e( 'We do not sell your personal data to third parties.', 'ambassadorhub' ); ?></p>

		<h3><?php esc_html_e( 'Your rights', 'ambassadorhub' ); ?></h3>
		<p><?php esc_html_e( 'You have the right to request access to, rectification of, or erasure of your personal data. To submit a request, please use the contact details provided in our privacy policy or, if you have an account, contact the site administrator. We will respond within 30 days.', 'ambassadorhub' ); ?></p>
		<?php
		return (string) ob_get_clean();
	}

	// -------------------------------------------------------------------------
	// Registration helpers
	// -------------------------------------------------------------------------

	/**
	 * Append the AmbassadorHub exporter to the WordPress exporter list.
	 *
	 * @param array $exporters Existing exporters.
	 * @return array
	 */
	public static function register_exporter( array $exporters ): array {
		$exporters['ambassadorhub'] = array(
			'exporter_friendly_name' => esc_html__( 'AmbassadorHub', 'ambassadorhub' ),
			'callback'               => array( self::class, 'export_personal_data' ),
		);
		return $exporters;
	}

	/**
	 * Append the AmbassadorHub eraser to the WordPress eraser list.
	 *
	 * @param array $erasers Existing erasers.
	 * @return array
	 */
	public static function register_eraser( array $erasers ): array {
		$erasers['ambassadorhub'] = array(
			'eraser_friendly_name' => esc_html__( 'AmbassadorHub', 'ambassadorhub' ),
			'callback'             => array( self::class, 'erase_personal_data' ),
		);
		return $erasers;
	}

	// -------------------------------------------------------------------------
	// Exporter
	// -------------------------------------------------------------------------

	/**
	 * Export all AmbassadorHub personal data for a given email address.
	 *
	 * @param string $email_address Requester email.
	 * @param int    $page          Pagination page (1-based); currently unused —
	 *                              all data is returned in a single page.
	 * @return array{
	 *     data: array,
	 *     done: bool,
	 * }
	 */
	public static function export_personal_data( string $email_address, int $page = 1 ): array { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- required by wp_privacy_personal_data_exporters callback signature
		$data = array();

		// ----- WordPress account -----
		$user = get_user_by( 'email', $email_address );
		if ( $user ) {
			$data = array_merge( $data, self::export_account( $user ) );
			$data = array_merge( $data, self::export_fundraisers( $user->ID ) );
			$data = array_merge( $data, self::export_consent_records( 'wp_user', $user->ID ) );
		}

		// ----- Donor record -----
		$donor = DonorsModule::get_by_email( $email_address );
		if ( $donor ) {
			$data = array_merge( $data, self::export_donor( $donor ) );
			$data = array_merge( $data, self::export_donations( (int) $donor->id ) );
			$data = array_merge( $data, self::export_consent_records( 'donor', (int) $donor->id ) );
		}

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

	/**
	 * Build export items for a WordPress user account.
	 *
	 * @param \WP_User $user User object.
	 * @return array Export items.
	 */
	private static function export_account( \WP_User $user ): array {
		$items = array();

		// Core account fields.
		$items[] = array(
			'group_id'    => 'ambassadorhub-account',
			'group_label' => esc_html__( 'AmbassadorHub Account', 'ambassadorhub' ),
			'item_id'     => 'account-' . $user->ID,
			'data'        => array(
				array(
					'name'  => esc_html__( 'First Name', 'ambassadorhub' ),
					'value' => esc_html( $user->first_name ),
				),
				array(
					'name'  => esc_html__( 'Last Name', 'ambassadorhub' ),
					'value' => esc_html( $user->last_name ),
				),
				array(
					'name'  => esc_html__( 'Email', 'ambassadorhub' ),
					'value' => esc_html( $user->user_email ),
				),
				array(
					'name'  => esc_html__( 'Registered', 'ambassadorhub' ),
					'value' => esc_html( $user->user_registered ),
				),
			),
		);

		// _ambh_* and ambh_* user-meta (covers private keys and profile fields).
		$all_meta  = get_user_meta( $user->ID );
		$ambh_data = array();
		foreach ( $all_meta as $key => $values ) {
			if ( str_starts_with( $key, '_ambh_' ) || str_starts_with( $key, 'ambh_' ) ) {
				$ambh_data[] = array(
					'name'  => esc_html( $key ),
					'value' => esc_html( maybe_serialize( $values[0] ) ),
				);
			}
		}

		if ( ! empty( $ambh_data ) ) {
			$items[] = array(
				'group_id'    => 'ambassadorhub-profile-meta',
				'group_label' => esc_html__( 'AmbassadorHub Profile Data', 'ambassadorhub' ),
				'item_id'     => 'profile-meta-' . $user->ID,
				'data'        => $ambh_data,
			);
		}

		return $items;
	}

	/**
	 * Build export items for fundraisers owned by the user.
	 *
	 * @param int $user_id WordPress user ID.
	 * @return array Export items.
	 */
	private static function export_fundraisers( int $user_id ): array {
		$fundraisers = get_posts(
			array(
				'post_type'      => FundraisersModule::POST_TYPE,
				'author'         => $user_id,
				'posts_per_page' => -1,
				'post_status'    => 'any',
			)
		);

		if ( empty( $fundraisers ) ) {
			return array();
		}

		$items = array();
		foreach ( $fundraisers as $fundraiser ) {
			$items[] = array(
				'group_id'    => 'ambassadorhub-fundraisers',
				'group_label' => esc_html__( 'AmbassadorHub Fundraisers', 'ambassadorhub' ),
				'item_id'     => 'fundraiser-' . $fundraiser->ID,
				'data'        => array(
					array(
						'name'  => esc_html__( 'Title', 'ambassadorhub' ),
						'value' => esc_html( $fundraiser->post_title ),
					),
					array(
						'name'  => esc_html__( 'Status', 'ambassadorhub' ),
						'value' => esc_html( $fundraiser->post_status ),
					),
					array(
						'name'  => esc_html__( 'Created', 'ambassadorhub' ),
						'value' => esc_html( $fundraiser->post_date_gmt ),
					),
					array(
						'name'  => esc_html__( 'URL', 'ambassadorhub' ),
						'value' => esc_url( get_permalink( $fundraiser->ID ) ),
					),
				),
			);
		}

		return $items;
	}

	/**
	 * Build export items for a donor record.
	 *
	 * @param object $donor Donor database row.
	 * @return array Export items.
	 */
	private static function export_donor( object $donor ): array {
		return array(
			array(
				'group_id'    => 'ambassadorhub-donor',
				'group_label' => esc_html__( 'AmbassadorHub Donor Profile', 'ambassadorhub' ),
				'item_id'     => 'donor-' . $donor->id,
				'data'        => array(
					array(
						'name'  => esc_html__( 'First Name', 'ambassadorhub' ),
						'value' => esc_html( $donor->first_name ),
					),
					array(
						'name'  => esc_html__( 'Last Name', 'ambassadorhub' ),
						'value' => esc_html( $donor->last_name ),
					),
					array(
						'name'  => esc_html__( 'Email', 'ambassadorhub' ),
						'value' => esc_html( $donor->email ),
					),
					array(
						'name'  => esc_html__( 'Created', 'ambassadorhub' ),
						'value' => esc_html( $donor->created_at ),
					),
				),
			),
		);
	}

	/**
	 * Build export items for all donations linked to a donor.
	 *
	 * @param int $donor_id Donor ID.
	 * @return array Export items.
	 */
	private static function export_donations( int $donor_id ): array {
		global $wpdb;

		$table = esc_sql( DonationsModule::get_table_name() );

		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $table is from $wpdb->prefix (safe).
		$donations = $wpdb->get_results(
			$wpdb->prepare( "SELECT * FROM {$table} WHERE donor_id = %d ORDER BY created_at ASC", $donor_id )
		);
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter

		if ( empty( $donations ) ) {
			return array();
		}

		$items = array();
		foreach ( $donations as $donation ) {
			$items[] = array(
				'group_id'    => 'ambassadorhub-donations',
				'group_label' => esc_html__( 'AmbassadorHub Donations', 'ambassadorhub' ),
				'item_id'     => 'donation-' . $donation->id,
				'data'        => array(
					array(
						'name'  => esc_html__( 'Amount', 'ambassadorhub' ),
						'value' => esc_html( $donation->amount . ' ' . $donation->currency ),
					),
					array(
						'name'  => esc_html__( 'Status', 'ambassadorhub' ),
						'value' => esc_html( $donation->status ),
					),
					array(
						'name'  => esc_html__( 'Message', 'ambassadorhub' ),
						'value' => esc_html( (string) $donation->message ),
					),
					array(
						'name'  => esc_html__( 'Anonymous', 'ambassadorhub' ),
						'value' => $donation->is_anonymous ? esc_html__( 'Yes', 'ambassadorhub' ) : esc_html__( 'No', 'ambassadorhub' ),
					),
					array(
						'name'  => esc_html__( 'Date', 'ambassadorhub' ),
						'value' => esc_html( $donation->created_at ),
					),
				),
			);
		}

		return $items;
	}

	/**
	 * Build export items for consent records.
	 *
	 * @param string $subject_type 'wp_user' or 'donor'.
	 * @param int    $subject_id   Subject ID.
	 * @return array Export items.
	 */
	private static function export_consent_records( string $subject_type, int $subject_id ): array {
		$records = ConsentManager::get_export( $subject_type, $subject_id );

		if ( empty( $records ) ) {
			return array();
		}

		$items = array();
		foreach ( $records as $record ) {
			$items[] = array(
				'group_id'    => 'ambassadorhub-consent',
				'group_label' => esc_html__( 'AmbassadorHub Consent Records', 'ambassadorhub' ),
				'item_id'     => 'consent-' . $record['id'],
				'data'        => array(
					array(
						'name'  => esc_html__( 'Purpose', 'ambassadorhub' ),
						'value' => esc_html( $record['purpose_slug'] ),
					),
					array(
						'name'  => esc_html__( 'Channel', 'ambassadorhub' ),
						'value' => esc_html( $record['channel'] ),
					),
					array(
						'name'  => esc_html__( 'Granted', 'ambassadorhub' ),
						'value' => $record['granted'] ? esc_html__( 'Yes', 'ambassadorhub' ) : esc_html__( 'No', 'ambassadorhub' ),
					),
					array(
						'name'  => esc_html__( 'Source', 'ambassadorhub' ),
						'value' => esc_html( $record['source'] ),
					),
					array(
						'name'  => esc_html__( 'Consented at', 'ambassadorhub' ),
						'value' => esc_html( $record['consented_at'] ),
					),
					array(
						'name'  => esc_html__( 'Withdrawn at', 'ambassadorhub' ),
						'value' => esc_html( (string) $record['withdrawn_at'] ),
					),
				),
			);
		}

		return $items;
	}

	// -------------------------------------------------------------------------
	// Eraser
	// -------------------------------------------------------------------------

	/**
	 * Erase all AmbassadorHub personal data for a given email address.
	 *
	 * @param string $email_address Requester email.
	 * @param int    $page          Pagination page (1-based); unused — single pass.
	 * @return array{
	 *     items_removed:  bool,
	 *     items_retained: bool,
	 *     messages:       string[],
	 *     done:           bool,
	 * }
	 */
	public static function erase_personal_data( string $email_address, int $page = 1 ): array { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- required by wp_privacy_personal_data_erasers callback signature
		$items_removed = false;
		$messages      = array();

		// ----- WordPress account -----
		$user = get_user_by( 'email', $email_address );
		if ( $user ) {
			self::erase_account_meta( $user->ID );
			self::erase_fundraisers( $user->ID );
			self::withdraw_all_consent( 'wp_user', $user->ID );
			$items_removed = true;

			// Notify connectors — entity_type 'user' = ambassador (WP user).
			self::notify_connectors_on_erasure( $user->ID, 'user' );
		}

		// ----- Donor record -----
		$donor = DonorsModule::get_by_email( $email_address );
		if ( $donor ) {
			$donor_id = (int) $donor->id;
			DonorsModule::anonymize( $donor_id );
			self::withdraw_all_consent( 'donor', $donor_id );
			$items_removed = true;

			// Notify connectors — entity_type 'donor' = payment donor.
			self::notify_connectors_on_erasure( $donor_id, 'donor' );
		}

		if ( ! $user && ! $donor ) {
			$messages[] = esc_html__( 'No AmbassadorHub data found for this email address.', 'ambassadorhub' );
		}

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

	/**
	 * Remove all _ambh_* user-meta and profile-picture attachment.
	 *
	 * @param int $user_id WordPress user ID.
	 * @return void
	 */
	private static function erase_account_meta( int $user_id ): void {
		// Delete the profile-picture attachment first (we need the meta to find it).
		$profile_picture_id = (int) get_user_meta( $user_id, '_ambh_profile_picture_id', true );
		if ( $profile_picture_id > 0 ) {
			wp_delete_attachment( $profile_picture_id, true );
		}

		global $wpdb;

		// Remove all _ambh_* user-meta keys (internal/private AmbassadorHub data).
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- bulk delete of custom plugin meta; no appropriate WP helper.
		$wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$wpdb->usermeta} WHERE user_id = %d AND meta_key LIKE %s",
				$user_id,
				$wpdb->esc_like( '_ambh_' ) . '%'
			)
		);

		// Also remove ambh_* meta keys (no leading underscore).
		// These include profile field values (ambh_profile_field_*)
		// which contain real PII but do not follow the _ambh_ private-key convention.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- bulk delete of custom plugin meta; no appropriate WP helper.
		$wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$wpdb->usermeta} WHERE user_id = %d AND meta_key LIKE %s",
				$user_id,
				$wpdb->esc_like( 'ambh_' ) . '%'
			)
		);
	}

	/**
	 * Set all fundraisers owned by the user to 'draft' and delete their featured images.
	 *
	 * We retain the post rows (for financial/audit integrity via donation FKs to
	 * the fundraiser ID) but make them private and remove personally identifiable
	 * content from title/description.
	 *
	 * @param int $user_id WordPress user ID.
	 * @return void
	 */
	private static function erase_fundraisers( int $user_id ): void {
		$fundraisers = get_posts(
			array(
				'post_type'      => FundraisersModule::POST_TYPE,
				'author'         => $user_id,
				'posts_per_page' => -1,
				'post_status'    => 'any',
			)
		);

		foreach ( $fundraisers as $fundraiser ) {
			// Delete featured image attachment.
			$thumbnail_id = (int) get_post_thumbnail_id( $fundraiser->ID );
			if ( $thumbnail_id > 0 ) {
				wp_delete_attachment( $thumbnail_id, true );
			}

			// Set post to draft so it's no longer publicly visible.
			wp_update_post(
				array(
					'ID'          => $fundraiser->ID,
					'post_status' => 'draft',
				)
			);
		}
	}

	/**
	 * Withdraw all active consent records for a given subject.
	 *
	 * @param string $subject_type 'wp_user' or 'donor'.
	 * @param int    $subject_id   Subject ID.
	 * @return void
	 */
	private static function withdraw_all_consent( string $subject_type, int $subject_id ): void {
		global $wpdb;

		$table = esc_sql( $wpdb->prefix . 'ambh_consent_records' );

		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $table is from $wpdb->prefix (safe).
		$record_ids = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT id FROM {$table} WHERE subject_type = %s AND subject_id = %d AND withdrawn_at IS NULL",
				$subject_type,
				$subject_id
			)
		);
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter

		foreach ( $record_ids as $record_id ) {
			ConsentManager::withdraw( (int) $record_id, 'gdpr_erasure' );
		}
	}

	/**
	 * Call on_erasure() on every registered connector.
	 *
	 * Connectors that sync ambassadors or donors with external CRM/marketing
	 * tools (Mailchimp, ActiveCampaign, etc.) must remove the contact there too.
	 *
	 * @param int    $entity_id   WP user ID or donor ID.
	 * @param string $entity_type 'user' or 'donor'.
	 * @return void
	 */
	private static function notify_connectors_on_erasure( int $entity_id, string $entity_type ): void {
		$connectors = ConnectorRegistry::get_all();
		foreach ( $connectors as $connector ) {
			$connector::on_erasure( $entity_id, $entity_type );
		}
	}
}
