<?php
/**
 * Tracking Event Factory
 *
 * @package AmbassadorHub\Tracking
 */

namespace AmbassadorHub\Tracking;

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

/**
 * Builds TrackingEvent instances from raw ambh_* hook arguments.
 *
 * Each static method corresponds to one ambh_* WordPress action, enriching
 * the raw hook data into a provider-agnostic TrackingEvent.
 */
final class EventFactory {

	/**
	 * Donation statuses considered a successful payment completion.
	 *
	 * Public so TrackingModule can check the initial status on donation_created.
	 * Filterable so PSP connectors can extend the list.
	 *
	 * @return string[]
	 */
	public static function completed_statuses(): array {
		/**
		 * Filter the donation statuses that map to a "donation_completed" tracking event.
		 *
		 * PSP connectors may use status values other than 'completed' (e.g. 'paid').
		 *
		 * @since 0.4.0
		 *
		 * @param string[] $statuses
		 */
		return (array) apply_filters( 'ambh_tracking_completed_statuses', array( 'completed', 'paid' ) );
	}

	// =========================================================================
	// Donations
	// =========================================================================

	/**
	 * Build event for ambh_donation_created.
	 *
	 * Uses the GA4 client_id from the _ga cookie if available. The client_id is
	 * also stored in a transient so it can be replayed on the subsequent
	 * donation_updated event (Mollie / PayPal server-to-server webhooks have no
	 * browser session and therefore no _ga cookie).
	 *
	 * @param int   $donation_id Newly created donation ID.
	 * @param array $data        Donation data as passed to the hook.
	 *
	 * @return TrackingEvent
	 */
	public static function from_donation_created( int $donation_id, array $data ): TrackingEvent {
		$client_id = TrackingDispatcher::get_client_id_from_cookie();

		if ( $client_id ) {
			// Cache for 24 h so it is available when the async PSP webhook fires.
			set_transient( "ambh_tracking_ga_client_{$donation_id}", $client_id, DAY_IN_SECONDS );
		}

		return new TrackingEvent(
			name:        'donation_created',
			entity_type: 'donation',
			entity_id:   $donation_id,
			properties:  array(
				'amount'        => (float) ( $data['amount'] ?? 0 ),
				'currency'      => sanitize_text_field( $data['currency'] ?? 'EUR' ),
				'fundraiser_id' => (int) ( $data['fundraiser_id'] ?? 0 ),
				'status'        => sanitize_key( $data['status'] ?? 'pending' ),
			),
			client_id:   $client_id,
		);
	}

	/**
	 * Build event for ambh_donation_updated.
	 *
	 * Returns null when the status change does not map to a trackable event
	 * (e.g. minor field updates that don't change status).
	 *
	 * @param int    $id       Donation ID.
	 * @param array  $data     Updated fields as passed to the hook.
	 * @param object $donation Donation object *before* the update.
	 *
	 * @return TrackingEvent|null
	 */
	public static function from_donation_updated( int $id, array $data, object $donation ): ?TrackingEvent {
		$new_status = $data['status'] ?? null;

		if ( ! $new_status ) {
			return null;
		}

		if ( in_array( $new_status, self::completed_statuses(), true ) ) {
			$event_name = 'donation_completed';
		} elseif ( 'failed' === $new_status ) {
			$event_name = 'donation_failed';
		} else {
			return null;
		}

		// Replay client_id stored at donation creation (no browser session in PSP webhook).
		$client_id = get_transient( "ambh_tracking_ga_client_{$id}" );
		$client_id = $client_id ? $client_id : TrackingDispatcher::get_client_id_from_cookie();
		$client_id = $client_id ? $client_id : null;

		// Clean up transient for terminal statuses.
		if ( 'donation_completed' === $event_name || 'donation_failed' === $event_name ) {
			delete_transient( "ambh_tracking_ga_client_{$id}" );
		}

		return new TrackingEvent(
			name:        $event_name,
			entity_type: 'donation',
			entity_id:   $id,
			properties:  array(
				'amount'         => (float) $donation->amount,
				'currency'       => sanitize_text_field( $donation->currency ?? 'EUR' ),
				'fundraiser_id'  => (int) $donation->fundraiser_id,
				// transaction_id is required for GA4 purchase event deduplication.
				'transaction_id' => (string) $id,
			),
			client_id:   $client_id,
		);
	}

	// =========================================================================
	// Campaigns
	// =========================================================================

	/**
	 * Build event for ambh_campaign_started.
	 *
	 * @param int $campaign_id Campaign post ID.
	 *
	 * @return TrackingEvent
	 */
	public static function from_campaign_started( int $campaign_id ): TrackingEvent {
		return new TrackingEvent(
			name:        'campaign_started',
			entity_type: 'campaign',
			entity_id:   $campaign_id,
			properties:  array(
				'campaign_title' => get_the_title( $campaign_id ),
			),
		);
	}

	/**
	 * Build event for ambh_campaign_ended.
	 *
	 * @param int $campaign_id Campaign post ID.
	 *
	 * @return TrackingEvent
	 */
	public static function from_campaign_ended( int $campaign_id ): TrackingEvent {
		return new TrackingEvent(
			name:        'campaign_ended',
			entity_type: 'campaign',
			entity_id:   $campaign_id,
			properties:  array(
				'campaign_title' => get_the_title( $campaign_id ),
			),
		);
	}

	// =========================================================================
	// Fundraisers
	// =========================================================================

	/**
	 * Build event for ambh_fundraiser_published.
	 *
	 * @param int $fundraiser_id Fundraiser post ID.
	 *
	 * @return TrackingEvent
	 */
	public static function from_fundraiser_published( int $fundraiser_id ): TrackingEvent {
		return new TrackingEvent(
			name:        'fundraiser_published',
			entity_type: 'fundraiser',
			entity_id:   $fundraiser_id,
			properties:  array(
				'fundraiser_title' => get_the_title( $fundraiser_id ),
			),
		);
	}

	// =========================================================================
	// Accounts
	// =========================================================================

	/**
	 * Build event for ambh_ambassador_role_added.
	 *
	 * @param int $user_id WordPress user ID.
	 *
	 * @return TrackingEvent
	 */
	public static function from_ambassador_registered( int $user_id ): TrackingEvent {
		$user = get_userdata( $user_id );

		return new TrackingEvent(
			name:        'ambassador_registered',
			entity_type: 'user',
			entity_id:   $user_id,
			properties:  array(
				'user_email' => $user ? sanitize_email( $user->user_email ) : '',
			),
		);
	}
}
