<?php
/**
 * Privacy Data Handler for Kurs Booking
 *
 * Implements WordPress Privacy Export and Eraser hooks (GDPR/DSGVO compliance).
 * Allows users to export and delete their booking data via WordPress Tools.
 *
 * @package KursBooking
 * @since   1.7.0
 */

declare(strict_types=1);

// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Kurs_Booking_Privacy
 *
 * Handles GDPR/DSGVO data export and erasure requests.
 */
class Kurs_Booking_Privacy {

	/**
	 * Number of bookings to process per batch.
	 */
	private const BATCH_SIZE = 50;

	/**
	 * Initialize privacy hooks.
	 */
	public static function init(): void {
		// Register data exporter.
		add_filter( 'wp_privacy_personal_data_exporters', array( __CLASS__, 'register_exporter' ) );

		// Register data eraser.
		add_filter( 'wp_privacy_personal_data_erasers', array( __CLASS__, 'register_eraser' ) );

		// Add privacy policy content suggestion.
		add_action( 'admin_init', array( __CLASS__, 'add_privacy_policy_content' ) );
	}

	/**
	 * Register the personal data exporter.
	 *
	 * @param array<string, array<string, mixed>> $exporters Registered exporters.
	 * @return array<string, array<string, mixed>> Modified exporters.
	 */
	public static function register_exporter( array $exporters ): array {
		$exporters['kurs-booking'] = array(
			'exporter_friendly_name' => __( 'Kurs-Booking Buchungsdaten', 'kurs-booking' ),
			'callback'               => array( __CLASS__, 'export_personal_data' ),
		);

		return $exporters;
	}

	/**
	 * Register the personal data eraser.
	 *
	 * @param array<string, array<string, mixed>> $erasers Registered erasers.
	 * @return array<string, array<string, mixed>> Modified erasers.
	 */
	public static function register_eraser( array $erasers ): array {
		$erasers['kurs-booking'] = array(
			'eraser_friendly_name' => __( 'Kurs-Booking Buchungsdaten', 'kurs-booking' ),
			'callback'             => array( __CLASS__, 'erase_personal_data' ),
		);

		return $erasers;
	}

	/**
	 * Export personal data for a given email address.
	 *
	 * @param string $email_address The email address to export data for.
	 * @param int    $page          The page number (for batching).
	 * @return array{data: array<int, array<string, mixed>>, done: bool} Export data and completion status.
	 */
	public static function export_personal_data( string $email_address, int $page = 1 ): array {
		$export_items = array();
		$done         = true;

		// Find all bookings for this email.
		$bookings = self::get_bookings_by_email( $email_address, $page );

		if ( empty( $bookings ) ) {
			return array(
				'data' => array(),
				'done' => true,
			);
		}

		foreach ( $bookings as $booking_id ) {
			$export_items[] = self::build_export_item( $booking_id );
		}

		// Check if there are more pages.
		$total_bookings = self::count_bookings_by_email( $email_address );
		$done           = ( $page * self::BATCH_SIZE ) >= $total_bookings;

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

	/**
	 * Erase personal data for a given email address.
	 *
	 * @param string $email_address The email address to erase data for.
	 * @param int    $page          The page number (for batching).
	 * @return array{items_removed: int, items_retained: int, messages: array<string>, done: bool} Erasure result.
	 */
	public static function erase_personal_data( string $email_address, int $page = 1 ): array {
		$items_removed  = 0;
		$items_retained = 0;
		$messages       = array();

		// Find all bookings for this email.
		$bookings = self::get_bookings_by_email( $email_address, $page );

		if ( empty( $bookings ) ) {
			return array(
				'items_removed'  => 0,
				'items_retained' => 0,
				'messages'       => array(),
				'done'           => true,
			);
		}

		foreach ( $bookings as $booking_id ) {
			$result = self::erase_booking_data( $booking_id );

			if ( $result['erased'] ) {
				++$items_removed;
			} else {
				++$items_retained;
				if ( ! empty( $result['message'] ) ) {
					$messages[] = $result['message'];
				}
			}
		}

		// Check if there are more pages.
		$total_bookings = self::count_bookings_by_email( $email_address );
		$done           = ( $page * self::BATCH_SIZE ) >= $total_bookings;

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

	/**
	 * Get bookings by email address.
	 *
	 * @param string $email_address The email address.
	 * @param int    $page          Page number for batching.
	 * @return array<int> Array of booking post IDs.
	 */
	private static function get_bookings_by_email( string $email_address, int $page = 1 ): array {
		global $wpdb;

		$offset = ( $page - 1 ) * self::BATCH_SIZE;

		// Query bookings by email in custom fields meta.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$booking_ids = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT DISTINCT p.ID
				FROM {$wpdb->posts} p
				INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
				WHERE p.post_type = %s
				AND p.post_status IN ('publish', 'draft', 'pending')
				AND pm.meta_key = '_buchung_custom_fields'
				AND (
					pm.meta_value LIKE %s
					OR pm.meta_value LIKE %s
				)
				ORDER BY p.ID DESC
				LIMIT %d OFFSET %d",
				Kurs_Booking_Buchung::POST_TYPE,
				'%"email":"' . $wpdb->esc_like( $email_address ) . '"%',
				'%"e_mail":"' . $wpdb->esc_like( $email_address ) . '"%',
				self::BATCH_SIZE,
				$offset
			)
		);

		return array_map( 'intval', $booking_ids ?: array() );
	}

	/**
	 * Count total bookings for an email address.
	 *
	 * @param string $email_address The email address.
	 * @return int Total count.
	 */
	private static function count_bookings_by_email( string $email_address ): int {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$count = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(DISTINCT p.ID)
				FROM {$wpdb->posts} p
				INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
				WHERE p.post_type = %s
				AND p.post_status IN ('publish', 'draft', 'pending')
				AND pm.meta_key = '_buchung_custom_fields'
				AND (
					pm.meta_value LIKE %s
					OR pm.meta_value LIKE %s
				)",
				Kurs_Booking_Buchung::POST_TYPE,
				'%"email":"' . $wpdb->esc_like( $email_address ) . '"%',
				'%"e_mail":"' . $wpdb->esc_like( $email_address ) . '"%'
			)
		);

		return (int) $count;
	}

	/**
	 * Build export item for a single booking.
	 *
	 * @param int $booking_id The booking post ID.
	 * @return array{group_id: string, group_label: string, item_id: string, data: array<int, array{name: string, value: string}>}
	 */
	private static function build_export_item( int $booking_id ): array {
		$booking = get_post( $booking_id );

		if ( ! $booking ) {
			return array(
				'group_id'    => 'kurs-booking',
				'group_label' => __( 'Kursbuchungen', 'kurs-booking' ),
				'item_id'     => 'buchung-' . $booking_id,
				'data'        => array(),
			);
		}

		// Collect all booking data.
		$data = array();

		// Booking number.
		$data[] = array(
			'name'  => __( 'Buchungsnummer', 'kurs-booking' ),
			'value' => $booking->post_title,
		);

		// Status.
		$status = get_post_meta( $booking_id, '_buchung_status', true );
		$data[] = array(
			'name'  => __( 'Status', 'kurs-booking' ),
			'value' => self::get_status_label( $status ?: 'pending' ),
		);

		// Kurs.
		$kurs_id = (int) get_post_meta( $booking_id, '_buchung_kurs_id', true );
		if ( $kurs_id ) {
			$data[] = array(
				'name'  => __( 'Kurs', 'kurs-booking' ),
				'value' => get_the_title( $kurs_id ),
			);

			$kurs_date = get_post_meta( $kurs_id, '_kurs_start_date', true );
			if ( $kurs_date ) {
				$data[] = array(
					'name'  => __( 'Kursdatum', 'kurs-booking' ),
					'value' => wp_date( 'd.m.Y', strtotime( $kurs_date ) ),
				);
			}
		}

		// Price.
		$total_price = (float) get_post_meta( $booking_id, '_buchung_total_price', true );
		$data[]      = array(
			'name'  => __( 'Gesamtbetrag', 'kurs-booking' ),
			'value' => number_format( $total_price, 2, ',', '.' ) . ' EUR',
		);

		// Custom fields (personal data).
		$custom_fields = get_post_meta( $booking_id, '_buchung_custom_fields', true );
		if ( ! empty( $custom_fields ) ) {
			$fields = is_array( $custom_fields ) ? $custom_fields : ( json_decode( $custom_fields, true ) ?: array() );

			$field_labels = array(
				'name'     => __( 'Name', 'kurs-booking' ),
				'email'    => __( 'E-Mail', 'kurs-booking' ),
				'e_mail'   => __( 'E-Mail', 'kurs-booking' ),
				'phone'    => __( 'Telefon', 'kurs-booking' ),
				'telefon'  => __( 'Telefon', 'kurs-booking' ),
				'address'  => __( 'Adresse', 'kurs-booking' ),
				'adresse'  => __( 'Adresse', 'kurs-booking' ),
				'city'     => __( 'Stadt', 'kurs-booking' ),
				'ort'      => __( 'Stadt', 'kurs-booking' ),
				'zip'      => __( 'PLZ', 'kurs-booking' ),
				'plz'      => __( 'PLZ', 'kurs-booking' ),
				'message'  => __( 'Nachricht', 'kurs-booking' ),
				'nachricht' => __( 'Nachricht', 'kurs-booking' ),
			);

			foreach ( $fields as $key => $value ) {
				if ( ! empty( $value ) && is_string( $value ) ) {
					$label  = $field_labels[ $key ] ?? ucfirst( str_replace( '_', ' ', $key ) );
					$data[] = array(
						'name'  => $label,
						'value' => $value,
					);
				}
			}
		}

		// Booking timestamp.
		$created_at = get_post_meta( $booking_id, '_buchung_created_at', true );
		if ( $created_at ) {
			$data[] = array(
				'name'  => __( 'Buchungsdatum', 'kurs-booking' ),
				'value' => wp_date( 'd.m.Y H:i', strtotime( $created_at ) ),
			);
		}

		// IP address (if stored).
		$ip_address = get_post_meta( $booking_id, '_buchung_ip_address', true );
		if ( $ip_address ) {
			$data[] = array(
				'name'  => __( 'IP-Adresse', 'kurs-booking' ),
				'value' => $ip_address,
			);
		}

		return array(
			'group_id'    => 'kurs-booking',
			'group_label' => __( 'Kursbuchungen', 'kurs-booking' ),
			'item_id'     => 'buchung-' . $booking_id,
			'data'        => $data,
		);
	}

	/**
	 * Erase data for a single booking.
	 *
	 * @param int $booking_id The booking post ID.
	 * @return array{erased: bool, message: string} Result with status and message.
	 */
	private static function erase_booking_data( int $booking_id ): array {
		$booking = get_post( $booking_id );

		if ( ! $booking ) {
			return array(
				'erased'  => false,
				'message' => sprintf(
					/* translators: %d: booking ID */
					__( 'Buchung #%d nicht gefunden.', 'kurs-booking' ),
					$booking_id
				),
			);
		}

		// Check if booking is within retention period (10 years for tax purposes).
		$created_at = get_post_meta( $booking_id, '_buchung_created_at', true );

		if ( $created_at ) {
			$booking_date    = strtotime( $created_at );
			$retention_years = (int) apply_filters( 'kurs_booking_retention_years', 10 );
			$retention_end   = strtotime( "+{$retention_years} years", $booking_date );

			if ( time() < $retention_end ) {
				// Cannot fully delete - anonymize instead.
				return self::anonymize_booking_data( $booking_id );
			}
		}

		// Outside retention period - full deletion allowed.
		$deleted = wp_delete_post( $booking_id, true );

		if ( $deleted ) {
			return array(
				'erased'  => true,
				'message' => '',
			);
		}

		return array(
			'erased'  => false,
			'message' => sprintf(
				/* translators: %s: booking number */
				__( 'Buchung %s konnte nicht gelöscht werden.', 'kurs-booking' ),
				$booking->post_title
			),
		);
	}

	/**
	 * Anonymize booking data (when full deletion is not possible due to retention).
	 *
	 * @param int $booking_id The booking post ID.
	 * @return array{erased: bool, message: string} Result with status and message.
	 */
	private static function anonymize_booking_data( int $booking_id ): array {
		$booking = get_post( $booking_id );

		if ( ! $booking ) {
			return array(
				'erased'  => false,
				'message' => '',
			);
		}

		// Anonymize custom fields (personal data).
		$custom_fields = get_post_meta( $booking_id, '_buchung_custom_fields', true );

		if ( ! empty( $custom_fields ) ) {
			$fields = is_array( $custom_fields ) ? $custom_fields : ( json_decode( $custom_fields, true ) ?: array() );

			// Fields to anonymize.
			$personal_fields = array( 'name', 'email', 'e_mail', 'phone', 'telefon', 'address', 'adresse', 'city', 'ort', 'zip', 'plz', 'message', 'nachricht' );

			foreach ( $personal_fields as $field ) {
				if ( isset( $fields[ $field ] ) ) {
					$fields[ $field ] = '[' . __( 'geloescht', 'kurs-booking' ) . ']';
				}
			}

			update_post_meta( $booking_id, '_buchung_custom_fields', $fields );
		}

		// Anonymize IP address.
		update_post_meta( $booking_id, '_buchung_ip_address', '0.0.0.0' );

		// Anonymize user agent.
		delete_post_meta( $booking_id, '_buchung_user_agent' );

		// Mark as anonymized.
		update_post_meta( $booking_id, '_buchung_anonymized', current_time( 'mysql' ) );

		// Log the anonymization.
		error_log(
			sprintf(
				'Kurs Booking: Buchung #%d anonymisiert (DSGVO-Anfrage, Aufbewahrungsfrist laeuft noch)',
				$booking_id
			)
		);

		return array(
			'erased'  => true,
			'message' => sprintf(
				/* translators: %s: booking number */
				__( 'Buchung %s wurde anonymisiert (steuerliche Aufbewahrungspflicht).', 'kurs-booking' ),
				$booking->post_title
			),
		);
	}

	/**
	 * Get human-readable status label.
	 *
	 * @param string $status The status key.
	 * @return string Status label.
	 */
	private static function get_status_label( string $status ): string {
		$labels = array(
			'pending'   => __( 'Ausstehend', 'kurs-booking' ),
			'confirmed' => __( 'Bestaetigt', 'kurs-booking' ),
			'cancelled' => __( 'Storniert', 'kurs-booking' ),
			'anfrage'   => __( 'Anfrage', 'kurs-booking' ),
		);

		return $labels[ $status ] ?? $status;
	}

	/**
	 * Add privacy policy content suggestion.
	 */
	public static function add_privacy_policy_content(): void {
		if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) {
			return;
		}

		$content = self::get_privacy_policy_content();

		wp_add_privacy_policy_content(
			__( 'Kurs Booking', 'kurs-booking' ),
			$content
		);
	}

	/**
	 * Get privacy policy content HTML.
	 *
	 * @return string Privacy policy HTML content.
	 */
	private static function get_privacy_policy_content(): string {
		ob_start();
		?>
		<h3><?php esc_html_e( 'Kursbuchungen', 'kurs-booking' ); ?></h3>
		<p>
			<?php esc_html_e( 'Bei der Buchung eines Kurses erheben wir folgende Daten:', 'kurs-booking' ); ?>
		</p>
		<ul>
			<li><?php esc_html_e( 'Name, E-Mail-Adresse, Telefonnummer', 'kurs-booking' ); ?></li>
			<li><?php esc_html_e( 'Buchungsdetails (Kurs, Datum, Anzahl Teilnehmer)', 'kurs-booking' ); ?></li>
			<li><?php esc_html_e( 'IP-Adresse und Zeitstempel (zur Spam-Abwehr)', 'kurs-booking' ); ?></li>
		</ul>
		<p>
			<strong><?php esc_html_e( 'Rechtsgrundlage:', 'kurs-booking' ); ?></strong>
			<?php esc_html_e( 'Art. 6 Abs. 1 lit. b DSGVO (Vertragserfuellung)', 'kurs-booking' ); ?>
		</p>
		<p>
			<strong><?php esc_html_e( 'Speicherdauer:', 'kurs-booking' ); ?></strong>
			<?php esc_html_e( '10 Jahre (steuerliche Aufbewahrungspflicht)', 'kurs-booking' ); ?>
		</p>
		<p>
			<strong><?php esc_html_e( 'Double-Opt-In:', 'kurs-booking' ); ?></strong>
			<?php esc_html_e( 'Buchungen werden erst nach E-Mail-Bestaetigung aktiv.', 'kurs-booking' ); ?>
		</p>
		<?php
		return ob_get_clean() ?: '';
	}
}
