<?php
/**
 * WordPress core Personal Data integration for the plugin's custom tables.
 *
 * WordPress core owns the confirmed-request lifecycle (Tools -> Export/Erase
 * Personal Data) and the downloadable report; this service only supplies the
 * data and mutates it. It exposes an exporter that bundles a customer's contact,
 * vehicle, and work-order records keyed by email, an eraser that anonymizes PII
 * in place while RETAINING the shop's work-order and document rows for tax and
 * accounting obligations (D-06), and the suggested privacy-policy content
 * (PRIV-05).
 *
 * The email column is intentionally non-unique on the customers table, so both
 * the exporter and the eraser process EVERY customer row matched by the address,
 * never just the first.
 *
 * @package WrenchPress
 */

defined( 'ABSPATH' ) || exit;

/**
 * Class WrenchPress_Privacy
 */
class WrenchPress_Privacy {

	/**
	 * Personal-data exporter callback (PRIV-01).
	 *
	 * Builds one 'wrenchpress_customer' group per matched customer, one
	 * 'wrenchpress_vehicle' group per owned vehicle, and one
	 * 'wrenchpress_work_order' group per work order, following the WordPress core
	 * data-group shape (group_id / group_label / item_id / data). Because the
	 * email is non-unique, every customer matched on it is processed, and the
	 * lookup spans archived rows too so a soft-deleted customer's PII is never
	 * silently omitted from an export.
	 * A single-shop CRM's volume fits one page, so this always reports done=true
	 * while honoring the paginated return contract.
	 *
	 * @param string $email_address Confirmed request email (identity owned by core).
	 * @param int    $page          1-based page number (unused; single page).
	 * @return array{data: array<int, array<string, mixed>>, done: bool}
	 */
	public static function export( $email_address, $page = 1 ) {
		unset( $page );

		$data      = array();
		$customers = WrenchPress_Customers_Repository::find_by_email_all_statuses( $email_address );

		foreach ( $customers as $customer ) {
			$customer_id = (int) $customer->id;

			$data[] = array(
				'group_id'    => 'wrenchpress_customer',
				'group_label' => __( 'Customer record', 'pitdesk-repair-crm' ),
				'item_id'     => 'wrenchpress-customer-' . $customer_id,
				'data'        => array(
					array(
						'name'  => __( 'Name', 'pitdesk-repair-crm' ),
						'value' => trim( $customer->first_name . ' ' . $customer->last_name ),
					),
					array(
						'name'  => __( 'Phone', 'pitdesk-repair-crm' ),
						'value' => (string) $customer->phone,
					),
					array(
						'name'  => __( 'Email', 'pitdesk-repair-crm' ),
						'value' => (string) $customer->email,
					),
					array(
						'name'  => __( 'Address', 'pitdesk-repair-crm' ),
						'value' => (string) $customer->address,
					),
				),
			);

			// Status-agnostic: an archived (soft-deleted) vehicle's VIN/plate is
			// still the customer's personal data and the eraser already anonymizes
			// it, so the export must include it too (list_active would omit it).
			$vehicles = WrenchPress_Vehicles_Repository::list_all_for_customer( $customer_id );

			foreach ( $vehicles as $vehicle ) {
				$data[] = array(
					'group_id'    => 'wrenchpress_vehicle',
					'group_label' => __( 'Vehicle', 'pitdesk-repair-crm' ),
					'item_id'     => 'wrenchpress-vehicle-' . (int) $vehicle->id,
					'data'        => array(
						array(
							'name'  => __( 'Make', 'pitdesk-repair-crm' ),
							'value' => (string) $vehicle->make,
						),
						array(
							'name'  => __( 'Model', 'pitdesk-repair-crm' ),
							'value' => (string) $vehicle->model,
						),
						array(
							'name'  => __( 'Year', 'pitdesk-repair-crm' ),
							'value' => (string) $vehicle->year,
						),
						array(
							'name'  => __( 'VIN', 'pitdesk-repair-crm' ),
							'value' => (string) $vehicle->vin,
						),
						array(
							'name'  => __( 'License plate', 'pitdesk-repair-crm' ),
							'value' => (string) $vehicle->plate,
						),
						array(
							'name'  => __( 'Mileage', 'pitdesk-repair-crm' ),
							'value' => (string) $vehicle->mileage,
						),
					),
				);
			}

			$work_orders = WrenchPress_Work_Orders_Repository::list_for_customer( $customer_id );

			foreach ( $work_orders as $work_order ) {
				$totals = WrenchPress_Work_Order_Items_Repository::totals(
					(int) $work_order->id,
					isset( $work_order->tax_rate ) ? $work_order->tax_rate : 0
				);

				$data[] = array(
					'group_id'    => 'wrenchpress_work_order',
					'group_label' => __( 'Work order', 'pitdesk-repair-crm' ),
					'item_id'     => 'wrenchpress-work-order-' . (int) $work_order->id,
					'data'        => array(
						array(
							'name'  => __( 'Status', 'pitdesk-repair-crm' ),
							'value' => (string) $work_order->status,
						),
						array(
							'name'  => __( 'Created', 'pitdesk-repair-crm' ),
							'value' => (string) $work_order->created_at,
						),
						array(
							'name'  => __( 'Total', 'pitdesk-repair-crm' ),
							'value' => number_format( (float) $totals['grand_total'], 2, '.', '' ),
						),
					),
				);
			}
		}

		// Leads are keyed by email, not by customer, so they are gathered outside
		// the customer loop: this captures a lead-only person (one who submitted the
		// public visit form but was never promoted to a customer) whose entire PII
		// footprint - name, contact details, address, and vehicle identifiers - lives
		// only in the leads table and would otherwise be invisible to an export.
		foreach ( WrenchPress_Leads_Repository::find_by_email( $email_address ) as $lead ) {
			$data[] = array(
				'group_id'    => 'wrenchpress_lead',
				'group_label' => __( 'Visit request', 'pitdesk-repair-crm' ),
				'item_id'     => 'wrenchpress-lead-' . (int) $lead->id,
				'data'        => array(
					array(
						'name'  => __( 'Name', 'pitdesk-repair-crm' ),
						'value' => trim( $lead->first_name . ' ' . $lead->last_name ),
					),
					array(
						'name'  => __( 'Phone', 'pitdesk-repair-crm' ),
						'value' => (string) $lead->phone,
					),
					array(
						'name'  => __( 'Email', 'pitdesk-repair-crm' ),
						'value' => (string) $lead->email,
					),
					array(
						'name'  => __( 'Address', 'pitdesk-repair-crm' ),
						'value' => (string) $lead->address,
					),
					array(
						'name'  => __( 'Make', 'pitdesk-repair-crm' ),
						'value' => (string) $lead->make,
					),
					array(
						'name'  => __( 'Model', 'pitdesk-repair-crm' ),
						'value' => (string) $lead->model,
					),
					array(
						'name'  => __( 'Year', 'pitdesk-repair-crm' ),
						'value' => (string) $lead->year,
					),
					array(
						'name'  => __( 'VIN', 'pitdesk-repair-crm' ),
						'value' => (string) $lead->vin,
					),
					array(
						'name'  => __( 'License plate', 'pitdesk-repair-crm' ),
						'value' => (string) $lead->plate,
					),
					array(
						'name'  => __( 'Subject', 'pitdesk-repair-crm' ),
						'value' => (string) $lead->subject,
					),
					array(
						'name'  => __( 'Message', 'pitdesk-repair-crm' ),
						'value' => (string) $lead->description,
					),
					array(
						'name'  => __( 'Consent given', 'pitdesk-repair-crm' ),
						'value' => $lead->consent ? __( 'Yes', 'pitdesk-repair-crm' ) : __( 'No', 'pitdesk-repair-crm' ),
					),
					array(
						'name'  => __( 'Consent date', 'pitdesk-repair-crm' ),
						'value' => (string) $lead->consent_at,
					),
					array(
						'name'  => __( 'Submitted', 'pitdesk-repair-crm' ),
						'value' => (string) $lead->created_at,
					),
				),
			);
		}

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

	/**
	 * Personal-data eraser callback (PRIV-02, D-06/D-07).
	 *
	 * For every customer matched by the non-unique email:
	 * - if the customer has work-order rows, their identifying data is anonymized
	 *   IN PLACE (customer contact and vehicle VIN/plate) and the work-order and
	 *   document rows are RETAINED so the shop keeps the financial/tax history the
	 *   law requires it to hold (Pitfall 7 - erasure never cascades a delete to
	 *   work orders or documents); this reports items_retained=true;
	 * - if the customer has no work-order rows there is no financial record to
	 *   preserve, so the customer and their vehicles are hard-deleted (discretion).
	 *
	 * In BOTH branches the customer's portal-session rows are deleted so no
	 * sign-in IP/user-agent fingerprint tied to their customer_id survives.
	 *
	 * Independently of any customer match, every lead sharing the email is
	 * anonymized in place: name, contact details, address, and vehicle identifiers
	 * are blanked while the row is retained for the shop's funnel and audit trail,
	 * and the submitter IP/UA are cleared (D-07). This closes the gap for a
	 * lead-only person who never became a customer and whose entire PII footprint
	 * lives in the leads table; when a lead is scrubbed, items_removed is true even
	 * if no customer row matched.
	 *
	 * @param string $email_address Confirmed request email (identity owned by core).
	 * @param int    $page          1-based page number (unused; single page).
	 * @return array{items_removed: bool, items_retained: bool, messages: array<int, string>, done: bool}
	 */
	public static function erase( $email_address, $page = 1 ) {
		unset( $page );

		$items_removed  = false;
		$items_retained = false;
		$messages       = array();

		$customers = WrenchPress_Customers_Repository::find_by_email_all_statuses( $email_address );

		foreach ( $customers as $customer ) {
			$customer_id = (int) $customer->id;

			// Remove the customer's portal-session ip/ua rows in every branch (D-07).
			WrenchPress_Customer_Sessions_Repository::delete_for_customer( $customer_id );

			$work_orders = WrenchPress_Work_Orders_Repository::list_for_customer( $customer_id );

			if ( ! empty( $work_orders ) ) {
				// Financial/job history exists: anonymize in place, never delete the
				// work-order or document rows (D-06 / Pitfall 7).
				WrenchPress_Customers_Repository::anonymize( $customer_id );
				WrenchPress_Vehicles_Repository::anonymize_for_customer( $customer_id );

				$items_removed  = true;
				$items_retained = true;
			} else {
				// No financial/job rows to preserve: the discretion branch removes
				// the customer and their vehicles outright.
				WrenchPress_Vehicles_Repository::delete_for_customer( $customer_id );
				WrenchPress_Customers_Repository::delete( $customer_id );

				$items_removed = true;
			}
		}

		// Anonymize every lead sharing this email in place, keeping the row for the
		// shop's funnel and audit trail (D-07). A lead-only person (one who never
		// became a customer) holds their entire PII footprint here, so this is what
		// completes their erasure; it also blanks the submitter IP/UA. When it
		// scrubs at least one lead, personal data was removed even if no customer
		// row matched, so items_removed must reflect that.
		$leads_anonymized = WrenchPress_Leads_Repository::anonymize_pii_for_email( $email_address );

		if ( $leads_anonymized > 0 ) {
			$items_removed = true;
		}

		if ( $items_retained ) {
			$messages[] = __(
				'Contact and vehicle identifiers were anonymized. Work orders, invoices, and estimates were kept without personal data so the shop can meet its tax and accounting retention obligations.',
				'pitdesk-repair-crm'
			);
		}

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

	/**
	 * Register the plugin's suggested privacy-policy content (PRIV-05).
	 *
	 * MUST run on admin_init or later - calling wp_add_privacy_policy_content()
	 * earlier triggers a _doing_it_wrong() notice. Declares the PII the plugin
	 * stores (customer contact, vehicle identifiers, the submitter IP used for
	 * spam defence, and the portal sign-in IP/user-agent session fingerprint), the
	 * purpose, the retention windows, and the optional CAPTCHA third parties. The
	 * CAPTCHA verify endpoints are read from the WrenchPress_Anti_Abuse constants
	 * so the policy text and the code that actually contacts them share one source.
	 *
	 * @return void
	 */
	public static function add_policy_content() {
		if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) {
			return;
		}

		$recaptcha_url = esc_url( WrenchPress_Anti_Abuse::RECAPTCHA_VERIFY_URL );
		$turnstile_url = esc_url( WrenchPress_Anti_Abuse::TURNSTILE_VERIFY_URL );

		$content = '<p>' . esc_html__(
			'This shop uses WrenchPress to manage its customer and repair records. When you request a visit or use the customer portal it stores your name, phone number, email address, and postal address, the vehicles you bring in (including make, model, year, VIN, and license plate), and the work orders, invoices, and estimates created for those vehicles.',
			'pitdesk-repair-crm'
		) . '</p>';

		$content .= '<p>' . esc_html__(
			'To defend the public visit-request form against spam it also records the IP address and browser user-agent of each submission. When the customer portal is used it records the IP address and user-agent of each sign-in as a session fingerprint.',
			'pitdesk-repair-crm'
		) . '</p>';

		$content .= '<p>' . esc_html__(
			'Retention: unconverted visit requests are kept for about 180 days, the submitter IP address on those requests is cleared after about 30 days, and portal sign-in sessions (including their IP and user-agent fingerprint) are cleared when they expire, after about 30 days. Customer, vehicle, and work-order records are kept for as long as the shop needs them to meet its tax and accounting obligations; on an erasure request the personal identifiers are anonymized while the financial records are retained without personal data.',
			'pitdesk-repair-crm'
		) . '</p>';

		$content .= '<p>' . sprintf(
			/* translators: 1: Google reCAPTCHA verify endpoint URL, 2: Cloudflare Turnstile verify endpoint URL. */
			esc_html__(
				'If the shop enables CAPTCHA protection on the visit-request form, and only then, the token from your browser is sent to the CAPTCHA provider to confirm you are human: Google reCAPTCHA (%1$s) or Cloudflare Turnstile (%2$s). No CAPTCHA data leaves your browser when CAPTCHA is disabled.',
				'pitdesk-repair-crm'
			),
			$recaptcha_url,
			$turnstile_url
		) . '</p>';

		wp_add_privacy_policy_content( 'WrenchPress', $content );
	}
}
