<?php
/**
 * Donations Module Automations
 *
 * Registers automation triggers, conditions, and event handlers
 * for donation lifecycle events.
 *
 * @package AmbassadorHub\Donations
 */

namespace AmbassadorHub\Donations;

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

use AmbassadorHub\Automations\AbstractAutomations;
use AmbassadorHub\Automations\ScheduleEngine;
use AmbassadorHub\Fundraisers\FundraiserCache;
use AmbassadorHub\Fundraisers\FundraisersModule;

use function add_filter;
use function add_action;
use function esc_html__;
use function get_post;
use function get_post_meta;
use function get_post_field;
use function get_posts;
use function get_permalink;
use function get_userdata;

/**
 * Consolidates automation registration and event handling for the Donations module.
 *
 * Extends AbstractAutomations to follow the same pattern as Accounts/Automations.
 *
 * Registers:
 * - Trigger group: donations
 * - Triggers: donation_received, donation_pending, donation_failed,
 *             ambassador_fundraiser_donation (user entity — the fundraiser owner)
 * - Conditions: inherits donations_total_eur + donations_count from Accounts module;
 *               provides metric resolution for entity_type = fundraiser and user.
 * - Webhook payload builder for fundraiser entity type.
 * - Webhook payload enrichment for user entity (ambassador fundraising stats).
 */
final class DonationsAutomations extends AbstractAutomations {

	// ===========================
	// Bootstrap
	// ===========================

	/**
	 * Register module-specific event handlers.
	 *
	 * Called by AbstractAutomations::init() during initialization.
	 *
	 * @return void
	 */
	protected static function register_event_handlers(): void {
		// Donation lifecycle events.
		add_action( 'ambh_donation_created', array( self::class, 'on_donation_created' ), 20, 2 );
		add_action( 'ambh_donation_updated', array( self::class, 'on_donation_updated' ), 20, 3 );

		// Webhook payload for fundraiser entity type — append donation stats (priority 20, after FundraisersAutomations at 10).
		add_filter( 'ambh_get_webhook_action_payload_fundraiser', array( self::class, 'get_fundraiser_webhook_payload' ), 20, 3 );

		// Enrich user entity webhook payload with ambassador fundraising stats.
		add_filter( 'ambh_get_webhook_action_payload_user', array( self::class, 'get_ambassador_donation_webhook_payload' ), 20, 3 );

		// Email action payload — resolve recipient + template vars per entity type.
		add_filter( 'ambh_get_email_action_payload_fundraiser', array( self::class, 'get_fundraiser_email_payload' ), 10, 3 );
		add_filter( 'ambh_get_email_action_payload_user', array( self::class, 'get_ambassador_email_payload' ), 10, 3 );

		// Donation entity: generic handler (donor as default) + role-specific overrides.
		add_filter( 'ambh_get_email_action_payload_donation', array( self::class, 'get_donation_email_payload' ), 10, 3 );
		add_filter( 'ambh_get_email_action_payload_donation_fundraiser_owner', array( self::class, 'get_donation_email_payload_fundraiser_owner' ), 10, 3 );

		// Fan-out: all donors to a fundraiser entity.
		add_filter( 'ambh_get_email_action_payload_fundraiser_all_donors', array( self::class, 'get_fundraiser_email_payload_all_donors' ), 10, 3 );

		// Email recipient roles — register selectable roles for donation + user entity types.
		add_filter( 'ambh_email_recipient_roles', array( self::class, 'register_email_recipient_roles' ) );

		// Template tokens for the variable picker in the automation UI.
		add_filter( 'ambh_automation_template_tokens', array( self::class, 'register_template_tokens' ), 10, 2 );
	}

	// ===========================
	// Trigger Registration
	// ===========================

	/**
	 * Register the donations trigger group.
	 *
	 * @param array $groups Existing trigger groups.
	 *
	 * @return array Modified groups.
	 */
	public static function register_trigger_groups( array $groups ): array {
		$groups['donations'] = array(
			'label' => esc_html__( 'Donation events', 'ambassadorhub' ),
			'icon'  => 'dashicons-money-alt',
		);

		return $groups;
	}

	/**
	 * Register donation triggers.
	 *
	 * All fundraiser-entity triggers use the fundraiser post ID as the entity.
	 * The ambassador trigger uses the fundraiser owner's user ID as the entity.
	 *
	 * @param array $triggers Existing triggers.
	 *
	 * @return array Modified triggers.
	 */
	public static function register_triggers( array $triggers ): array {

		$triggers['donation_received'] = array(
			'label'       => esc_html__( 'Donation received (completed)', 'ambassadorhub' ),
			'description' => esc_html__( 'Triggered when a completed donation is created or updated to completed status on a fundraiser.', 'ambassadorhub' ),
			'group'       => 'donations',
			'entity_type' => 'fundraiser',
			'event'       => 'donation_received',
			'icon'        => 'dashicons-yes-alt',
		);

		$triggers['donation_pending'] = array(
			'label'       => esc_html__( 'Donation pending', 'ambassadorhub' ),
			'description' => esc_html__( 'Triggered when a donation is created with pending status on a fundraiser.', 'ambassadorhub' ),
			'group'       => 'donations',
			'entity_type' => 'fundraiser',
			'event'       => 'donation_pending',
			'icon'        => 'dashicons-clock',
		);

		$triggers['donation_failed'] = array(
			'label'       => esc_html__( 'Donation failed', 'ambassadorhub' ),
			'description' => esc_html__( 'Triggered when a donation status changes to failed on a fundraiser.', 'ambassadorhub' ),
			'group'       => 'donations',
			'entity_type' => 'fundraiser',
			'event'       => 'donation_failed',
			'icon'        => 'dashicons-no-alt',
		);

		$triggers['ambassador_fundraiser_donation'] = array(
			'label'       => esc_html__( 'Ambassador received a fundraiser donation', 'ambassadorhub' ),
			'description' => esc_html__( 'Triggered when a completed donation is received on any fundraiser owned by an ambassador. Entity is the ambassador user.', 'ambassadorhub' ),
			'group'       => 'donations',
			'entity_type' => 'user',
			'event'       => 'donation_received',
			'icon'        => 'dashicons-admin-users',
		);

		// Donation-level triggers — entity is the donation record itself.
		// These enable per-donation rules (e.g. add donor to CRM, send receipt).
		$triggers['donation_initiated'] = array(
			'label'       => esc_html__( 'Donation initiated', 'ambassadorhub' ),
			'description' => esc_html__( 'Triggered when a new donation record is created. Entity is the donation.', 'ambassadorhub' ),
			'group'       => 'donations',
			'entity_type' => 'donation',
			'event'       => 'created',
			'icon'        => 'dashicons-flag',
		);

		$triggers['donation_completed'] = array(
			'label'       => esc_html__( 'Donation completed', 'ambassadorhub' ),
			'description' => esc_html__( 'Triggered when a donation transitions to completed status. Entity is the donation.', 'ambassadorhub' ),
			'group'       => 'donations',
			'entity_type' => 'donation',
			'event'       => 'completed',
			'icon'        => 'dashicons-saved',
		);

		$triggers['donation_payment_failed'] = array(
			'label'       => esc_html__( 'Donation payment failed', 'ambassadorhub' ),
			'description' => esc_html__( 'Triggered when a donation transitions to failed status. Entity is the donation.', 'ambassadorhub' ),
			'group'       => 'donations',
			'entity_type' => 'donation',
			'event'       => 'failed',
			'icon'        => 'dashicons-warning',
		);

		return $triggers;
	}

	// ===========================
	// Condition Registration
	// ===========================

	/**
	 * Register donation conditions.
	 *
	 * @param array $conditions Existing conditions.
	 * @param array $context    Context data.
	 *
	 * @return array
	 */
	public static function register_conditions( array $conditions, array $context ): array {

		$conditions['donations_total_eur'] = array(
			'label'                  => esc_html__( 'Total donations amount', 'ambassadorhub' ),
			'description'            => esc_html__( 'Compare the total amount of donations in EUR.', 'ambassadorhub' ),
			'supported_entity_types' => array( 'campaign', 'user', 'fundraiser' ),
			'operator_type'          => 'compare',
			'operators'              => array( '<', '>', '==', '!=', '<=', '>=' ),
			'data_source'            => 'metric',
			'metric_key'             => 'donations_total_eur',
			'value_type'             => 'number',
			'value_options'          => null,
		);

		$conditions['donations_count'] = array(
			'label'                  => esc_html__( 'Number of donations', 'ambassadorhub' ),
			'description'            => esc_html__( 'Compare the count of donations received.', 'ambassadorhub' ),
			'supported_entity_types' => array( 'campaign', 'user', 'fundraiser' ),
			'operator_type'          => 'compare',
			'operators'              => array( '<', '>', '==', '!=', '<=', '>=' ),
			'data_source'            => 'metric',
			'metric_key'             => 'donations_count',
			'value_type'             => 'number',
			'value_options'          => null,
		);

		return $conditions;
	}

	// ===========================
	// Metric Values
	// ===========================

	/**
	 * Provide metric values for condition evaluation.
	 *
	 * Resolves donations_total_eur and donations_count for:
	 * - entity_type = fundraiser  → data from FundraiserCache (single fundraiser)
	 * - entity_type = user        → aggregate across all fundraisers owned by the user
	 *
	 * @param mixed  $value              Existing value (pass-through if not handled).
	 * @param string $metric_key         Metric identifier.
	 * @param int    $target_id          Entity ID.
	 * @param string $target_entity_type Entity type ('fundraiser' or 'user').
	 *
	 * @return mixed Resolved metric value or original $value.
	 */
	public static function get_metric_value( mixed $value, string $metric_key, int $target_id, string $target_entity_type ): mixed {

		if ( 'fundraiser' === $target_entity_type ) {
			$cache = FundraiserCache::get_or_update_cache( $target_id );

			switch ( $metric_key ) {
				case 'donations_total_eur':
					return (float) ( $cache['total_amount'] ?? 0.0 );
				case 'donations_count':
					return (int) ( $cache['completed_count'] ?? 0 );
			}
		}

		// For user entity: aggregate over all fundraisers owned by that ambassador.
		if ( 'user' === $target_entity_type ) {
			$fundraiser_ids = get_posts(
				array(
					'post_type'      => 'fundraiser',
					'author'         => $target_id,
					'posts_per_page' => -1,
					'fields'         => 'ids',
					'post_status'    => 'any',
				)
			);

			if ( empty( $fundraiser_ids ) ) {
				return $value;
			}

			switch ( $metric_key ) {
				case 'donations_total_eur':
					$total = 0.0;
					foreach ( $fundraiser_ids as $fid ) {
						$cache  = FundraiserCache::get_or_update_cache( (int) $fid );
						$total += (float) ( $cache['total_amount'] ?? 0.0 );
					}
					return $total;

				case 'donations_count':
					$count = 0;
					foreach ( $fundraiser_ids as $fid ) {
						$cache  = FundraiserCache::get_or_update_cache( (int) $fid );
						$count += (int) ( $cache['completed_count'] ?? 0 );
					}
					return $count;
			}
		}

		return $value;
	}

	// ===========================
	// Event Handlers
	// ===========================

	/**
	 * Handle donation creation.
	 *
	 * Fires the appropriate trigger based on the initial donation status.
	 *
	 * @param int   $donation_id Donation ID.
	 * @param array $data        Donation data.
	 *
	 * @return void
	 */
	public static function on_donation_created( int $donation_id, array $data ): void {
		$fundraiser_id = (int) ( $data['fundraiser_id'] ?? 0 );
		$status        = $data['status'] ?? 'pending';

		if ( $fundraiser_id <= 0 ) {
			return;
		}

		$context = array(
			'donation_id' => $donation_id,
			'status'      => $status,
			'amount'      => (float) ( $data['amount'] ?? 0 ),
			'currency'    => $data['currency'] ?? 'EUR',
			'source'      => 'donation_created',
		);

		switch ( $status ) {
			case 'completed':
				ScheduleEngine::schedule_rules_for_trigger( 'donation_received', $fundraiser_id, $context );
				self::fire_ambassador_donation_trigger( $fundraiser_id, $context );
				ScheduleEngine::schedule_rules_for_trigger( 'donation_completed', $donation_id, $context );
				break;

			case 'pending':
				ScheduleEngine::schedule_rules_for_trigger( 'donation_pending', $fundraiser_id, $context );
				break;

			case 'failed':
				ScheduleEngine::schedule_rules_for_trigger( 'donation_failed', $fundraiser_id, $context );
				ScheduleEngine::schedule_rules_for_trigger( 'donation_payment_failed', $donation_id, $context );
				break;
		}

		// Donation-level trigger: fires for every new donation regardless of status.
		ScheduleEngine::schedule_rules_for_trigger( 'donation_initiated', $donation_id, $context );
	}

	/**
	 * Handle donation status updates.
	 *
	 * Only fires when the status actually changes to avoid duplicate triggers.
	 *
	 * @param int    $donation_id    Donation ID.
	 * @param array  $data           Updated data.
	 * @param object $donation_before Donation object before update.
	 *
	 * @return void
	 */
	public static function on_donation_updated( int $donation_id, array $data, object $donation_before ): void {
		$fundraiser_id = (int) ( $data['fundraiser_id'] ?? $donation_before->fundraiser_id ?? 0 );
		$new_status    = $data['status'] ?? $donation_before->status ?? null;
		$old_status    = $donation_before->status ?? null;

		if ( $fundraiser_id <= 0 || ! $new_status || $new_status === $old_status ) {
			return; // No status change, nothing to trigger.
		}

		$context = array(
			'donation_id' => $donation_id,
			'status'      => $new_status,
			'status_from' => $old_status,
			'amount'      => (float) ( $donation_before->amount ?? 0 ),
			'currency'    => $donation_before->currency ?? 'EUR',
			'source'      => 'donation_updated',
		);

		switch ( $new_status ) {
			case 'completed':
				ScheduleEngine::schedule_rules_for_trigger( 'donation_completed', $donation_id, $context );
				break;

			case 'failed':
				ScheduleEngine::schedule_rules_for_trigger( 'donation_failed', $fundraiser_id, $context );
				break;

			case 'pending':
				// Pending on update is unusual but possible (e.g. payment reversal).
				ScheduleEngine::schedule_rules_for_trigger( 'donation_pending', $fundraiser_id, $context );
				break;
		}
	}

	/**
	 * Fire the ambassador_fundraiser_donation trigger on the fundraiser owner.
	 *
	 * @param int   $fundraiser_id Fundraiser post ID.
	 * @param array $context       Existing trigger context.
	 *
	 * @return void
	 */
	private static function fire_ambassador_donation_trigger( int $fundraiser_id, array $context ): void {
		$owner_id = (int) get_post_field( 'post_author', $fundraiser_id );

		if ( $owner_id > 0 ) {
			ScheduleEngine::schedule_rules_for_trigger(
				'ambassador_fundraiser_donation',
				$owner_id,
				array_merge( $context, array( 'fundraiser_id' => $fundraiser_id ) )
			);
		}
	}

	// ===========================
	// Webhook Payloads
	// ===========================

	/**
	 * Build webhook payload for fundraiser entity type.
	 *
	 * Adds donation aggregate stats. The base fundraiser identity block is
	 * provided by FundraisersAutomations.
	 *
	 * @param array $payload   Existing payload.
	 * @param int   $target_id Fundraiser post ID.
	 * @param int   $action_id Action ID.
	 *
	 * @return array
	 */
	public static function get_fundraiser_webhook_payload( array $payload, int $target_id, int $action_id ): array {
		if ( ! $target_id ) {
			return $payload;
		}

		$cache = FundraiserCache::get_or_update_cache( $target_id );

		$payload['donations'] = array(
			'total_raised'    => (float) ( $cache['total_amount'] ?? 0.0 ),
			'donations_count' => (int) ( $cache['completed_count'] ?? 0 ),
			'pending_count'   => (int) ( $cache['pending_count'] ?? 0 ),
			'last_donation'   => $cache['last_donation_date'] ?? null,
		);

		return $payload;
	}

	/**
	 * Enrich ambassador (user) webhook payload with aggregate fundraising stats.
	 *
	 * Appended to any user-entity webhook payload. The Accounts module already
	 * fills in payload['user'], so we only add fundraising totals here.
	 *
	 * @param array $payload   Existing payload.
	 * @param int   $target_id Ambassador user ID.
	 * @param int   $action_id Action ID.
	 *
	 * @return array
	 */
	public static function get_ambassador_donation_webhook_payload( array $payload, int $target_id, int $action_id ): array {
		$fundraiser_ids = get_posts(
			array(
				'post_type'      => 'fundraiser',
				'author'         => $target_id,
				'posts_per_page' => -1,
				'fields'         => 'ids',
				'post_status'    => 'publish',
			)
		);

		$total_raised    = 0.0;
		$total_donations = 0;

		foreach ( $fundraiser_ids as $fid ) {
			$cache            = FundraiserCache::get_or_update_cache( (int) $fid );
			$total_raised    += (float) ( $cache['total_amount'] ?? 0.0 );
			$total_donations += (int) ( $cache['completed_count'] ?? 0 );
		}

		$payload['fundraising_stats'] = array(
			'total_raised'     => $total_raised,
			'total_donations'  => $total_donations,
			'fundraiser_count' => count( $fundraiser_ids ),
		);

		return $payload;
	}

	// ===========================
	// Email Action Payloads
	// ===========================

	/**
	 * Resolve email recipient and template variables for fundraiser entity.
	 *
	 * Sends to the fundraiser's owner (post_author). Template variables cover
	 * fundraiser details, stats, and the most recently recorded donation.
	 *
	 * Available {{tokens}}:
	 *   {{fundraiser_title}}, {{fundraiser_url}},
	 *   {{owner_first_name}}, {{owner_last_name}}, {{owner_email}},
	 *   {{target_amount}}, {{total_raised}}, {{donations_count}},
	 *   {{latest_donor_name}}, {{latest_donor_email}},
	 *   {{latest_amount}}, {{latest_currency}}, {{latest_message}}
	 *
	 * @param array $payload   Existing payload (may be pre-filled by another hook).
	 * @param int   $target_id Fundraiser post ID.
	 * @param int   $action_id Action instance ID.
	 *
	 * @return array
	 */
	public static function get_fundraiser_email_payload( array $payload, int $target_id, int $action_id ): array {
		if ( ! $target_id ) {
			return $payload;
		}

		$post = get_post( $target_id );
		if ( ! $post || 'fundraiser' !== $post->post_type ) {
			return $payload;
		}

		$owner = get_userdata( (int) $post->post_author );
		if ( ! $owner ) {
			return $payload;
		}

		$cache         = FundraiserCache::get_or_update_cache( $target_id );
		$target_amount = (float) get_post_meta( $target_id, '_ambh_target_amount', true );

		// Most recent donation for {{latest_*}} tokens.
		$last_donations = DonationsModule::get_by_fundraisers( array( $target_id ) );
		$last           = ! empty( $last_donations ) ? reset( $last_donations ) : null;

		if ( empty( $payload['to'] ) ) {
			$payload['to'] = $owner->user_email;
		}

		$payload['template_vars'] = array_merge(
			$payload['template_vars'] ?? array(),
			array(
				'{{fundraiser_title}}'   => $post->post_title,
				'{{fundraiser_url}}'     => (string) get_permalink( $post->ID ),
				'{{owner_first_name}}'   => $owner->first_name ? $owner->first_name : $owner->display_name,
				'{{owner_last_name}}'    => $owner->last_name,
				'{{owner_email}}'        => $owner->user_email,
				'{{target_amount}}'      => $target_amount > 0 ? number_format( $target_amount, 2 ) : '—',
				'{{total_raised}}'       => number_format( (float) ( $cache['total_amount'] ?? 0.0 ), 2 ),
				'{{donations_count}}'    => (string) ( $cache['completed_count'] ?? 0 ),
				'{{latest_donor_name}}'  => $last ? DonationsModule::get_donor_name_for_display( $last ) : '—',
				'{{latest_donor_email}}' => $last ? DonationsModule::get_donor_email_for_display( $last ) : '—',
				'{{latest_amount}}'      => $last ? number_format( (float) $last->amount, 2 ) : '—',
				'{{latest_currency}}'    => $last ? $last->currency : '—',
				'{{latest_message}}'     => $last ? (string) ( $last->message ?? '' ) : '',
			)
		);

		return $payload;
	}

	/**
	 * Register selectable email recipient roles for donation and user entity types.
	 *
	 * @param array $roles Existing roles map.
	 *
	 * @return array
	 */
	public static function register_email_recipient_roles( array $roles ): array {
		$roles['donation'] = array(
			'donor'            => esc_html__( 'Donor', 'ambassadorhub' ),
			'fundraiser_owner' => esc_html__( 'Fundraiser owner', 'ambassadorhub' ),
			'admin'            => esc_html__( 'Site administrator', 'ambassadorhub' ),
		);
		$roles['user']     = array(
			'user'  => esc_html__( 'Ambassador', 'ambassadorhub' ),
			'admin' => esc_html__( 'Site administrator', 'ambassadorhub' ),
		);
		// Note: all_donors for fundraiser entity is registered by FundraisersAutomations;
		// only the handler lives here.
		return $roles;
	}

	/**
	 * Fan-out to all unique donors who donated to this fundraiser.
	 *
	 * Sends one personalised email per unique donor (deduplicated by email address).
	 * Template vars available: {{donor_name}}, {{donor_email}}, {{fundraiser_title}},
	 * {{fundraiser_url}}, {{total_raised}}, {{donations_count}}.
	 *
	 * @param array $payload   Existing payload (template_vars already set by the
	 *                         generic fundraiser handler).
	 * @param int   $target_id Fundraiser post ID.
	 * @param int   $_action_id Action instance ID.
	 *
	 * @return array
	 */
	public static function get_fundraiser_email_payload_all_donors( array $payload, int $target_id, int $_action_id ): array {
		if ( ! $target_id ) {
			return $payload;
		}

		// Collect all fundraiser IDs in the sub-tree (this fundraiser + all children).
		$all_ids  = array( $target_id );
		$children = get_posts(
			array(
				'post_type'      => FundraisersModule::POST_TYPE,
				'post_parent'    => $target_id,
				'posts_per_page' => -1,
				'post_status'    => 'any',
				'fields'         => 'ids',
			)
		);
		if ( ! empty( $children ) ) {
			foreach ( $children as $child_id ) {
				$all_ids[]     = (int) $child_id;
				$grandchildren = get_posts(
					array(
						'post_type'      => FundraisersModule::POST_TYPE,
						'post_parent'    => (int) $child_id,
						'posts_per_page' => -1,
						'post_status'    => 'any',
						'fields'         => 'ids',
					)
				);
				foreach ( $grandchildren as $gc_id ) {
					$all_ids[] = (int) $gc_id;
				}
			}
		}

		$donations = DonationsModule::get_by_fundraisers( $all_ids, 'completed' );

		$seen = array();
		$list = array();

		foreach ( $donations as $donation ) {
			$email = DonationsModule::get_donor_email_for_display( $donation );
			if ( ! is_email( $email ) || isset( $seen[ $email ] ) ) {
				continue;
			}
			$seen[ $email ] = true;

			$list[] = array(
				'to'            => $email,
				'template_vars' => array(
					'{{donor_name}}'  => DonationsModule::get_donor_name_for_display( $donation ),
					'{{donor_email}}' => $email,
				),
			);
		}

		if ( ! empty( $list ) ) {
			$payload['to_list'] = $list;
		}

		return $payload;
	}

	/**
	 * Resolve email recipient and template variables for donation entity.
	 *
	 * Default recipient is the donor. Template variables cover donation details
	 * and the associated fundraiser.
	 *
	 * Available {{tokens}}:
	 *   {{donor_name}}, {{donor_email}}, {{amount}}, {{currency}}, {{message}},
	 *   {{fundraiser_title}}, {{fundraiser_url}}
	 *
	 * @param array $payload   Existing payload.
	 * @param int   $target_id Donation ID.
	 * @param int   $_action_id Action instance ID.
	 *
	 * @return array
	 */
	public static function get_donation_email_payload( array $payload, int $target_id, int $_action_id ): array {
		if ( ! $target_id ) {
			return $payload;
		}

		$donation = DonationsModule::get( $target_id );
		if ( ! $donation ) {
			return $payload;
		}

		// Default recipient: donor.
		if ( empty( $payload['to'] ) ) {
			$donor_email = DonationsModule::get_donor_email_for_display( $donation );
			if ( is_email( $donor_email ) ) {
				$payload['to'] = $donor_email;
			}
		}

		$fundraiser_id = (int) ( $donation->fundraiser_id ?? 0 );
		$fundraiser    = $fundraiser_id > 0 ? get_post( $fundraiser_id ) : null;

		$payload['template_vars'] = array_merge(
			$payload['template_vars'] ?? array(),
			array(
				'{{donor_name}}'       => DonationsModule::get_donor_name_for_display( $donation ),
				'{{donor_email}}'      => DonationsModule::get_donor_email_for_display( $donation ),
				'{{amount}}'           => number_format( (float) ( $donation->amount ?? 0 ), 2 ),
				'{{currency}}'         => $donation->currency ?? '',
				'{{message}}'          => (string) ( $donation->message ?? '' ),
				'{{fundraiser_title}}' => $fundraiser ? $fundraiser->post_title : '',
				'{{fundraiser_url}}'   => $fundraiser ? (string) get_permalink( $fundraiser->ID ) : '',
			)
		);

		return $payload;
	}

	/**
	 * Override the email recipient to the fundraiser owner for a donation entity.
	 *
	 * Runs after get_donation_email_payload() has filled template vars; only
	 * the `to` address is changed.
	 *
	 * @param array $payload   Existing payload.
	 * @param int   $target_id Donation ID.
	 * @param int   $_action_id Action instance ID.
	 *
	 * @return array
	 */
	public static function get_donation_email_payload_fundraiser_owner( array $payload, int $target_id, int $_action_id ): array {
		if ( ! $target_id ) {
			return $payload;
		}

		$donation      = DonationsModule::get( $target_id );
		$fundraiser_id = (int) ( $donation->fundraiser_id ?? 0 );

		if ( $fundraiser_id <= 0 ) {
			return $payload;
		}

		$fundraiser = get_post( $fundraiser_id );
		if ( ! $fundraiser ) {
			return $payload;
		}

		$owner = get_userdata( (int) $fundraiser->post_author );
		if ( $owner ) {
			$payload['to']            = $owner->user_email;
			$payload['template_vars'] = array_merge(
				$payload['template_vars'] ?? array(),
				array(
					'{{owner_first_name}}' => $owner->first_name ? $owner->first_name : $owner->display_name,
					'{{owner_last_name}}'  => $owner->last_name,
					'{{owner_email}}'      => $owner->user_email,
				)
			);
		}

		return $payload;
	}

	/**
	 * Register template tokens for the automation variable picker.
	 *
	 * Covers the donation, fundraiser, and user entity types that this module handles.
	 *
	 * @param array  $tokens      Existing tokens keyed by '{{token}}'.
	 * @param string $entity_type Entity type being queried.
	 *
	 * @return array
	 */
	public static function register_template_tokens( array $tokens, string $entity_type ): array {
		if ( 'donation' === $entity_type ) {
			return array_merge(
				$tokens,
				array(
					'{{donor_name}}'       => array( 'label' => __( 'Donor full name', 'ambassadorhub' ) ),
					'{{donor_email}}'      => array( 'label' => __( 'Donor email', 'ambassadorhub' ) ),
					'{{amount}}'           => array( 'label' => __( 'Donation amount', 'ambassadorhub' ) ),
					'{{currency}}'         => array( 'label' => __( 'Currency', 'ambassadorhub' ) ),
					'{{message}}'          => array( 'label' => __( 'Donor message', 'ambassadorhub' ) ),
					'{{fundraiser_title}}' => array( 'label' => __( 'Fundraiser title', 'ambassadorhub' ) ),
					'{{fundraiser_url}}'   => array( 'label' => __( 'Fundraiser URL', 'ambassadorhub' ) ),
				)
			);
		}

		if ( 'fundraiser' === $entity_type ) {
			return array_merge(
				$tokens,
				array(
					'{{total_raised}}'       => array( 'label' => __( 'Total raised', 'ambassadorhub' ) ),
					'{{donations_count}}'    => array( 'label' => __( 'Donations count', 'ambassadorhub' ) ),
					'{{latest_donor_name}}'  => array( 'label' => __( 'Latest donor name', 'ambassadorhub' ) ),
					'{{latest_donor_email}}' => array( 'label' => __( 'Latest donor email', 'ambassadorhub' ) ),
					'{{latest_amount}}'      => array( 'label' => __( 'Latest donation amount', 'ambassadorhub' ) ),
					'{{latest_currency}}'    => array( 'label' => __( 'Latest donation currency', 'ambassadorhub' ) ),
					'{{latest_message}}'     => array( 'label' => __( 'Latest donor message', 'ambassadorhub' ) ),
				)
			);
		}

		if ( 'user' === $entity_type ) {
			return array_merge(
				$tokens,
				array(
					'{{total_raised}}'     => array( 'label' => __( 'Total raised (ambassador)', 'ambassadorhub' ) ),
					'{{total_donations}}'  => array( 'label' => __( 'Total donations (ambassador)', 'ambassadorhub' ) ),
					'{{fundraiser_count}}' => array( 'label' => __( 'Number of fundraisers', 'ambassadorhub' ) ),
				)
			);
		}

		return $tokens;
	}

	/**
	 * Resolve email recipient and template variables for user (ambassador) entity.
	 *
	 * Sends to the ambassador's own email address. Template variables include
	 * basic user data and aggregate fundraising stats across all their fundraisers.
	 *
	 * Available {{tokens}}:
	 *   {{first_name}}, {{last_name}}, {{email}},
	 *   {{total_raised}}, {{total_donations}}, {{fundraiser_count}}
	 *
	 * @param array $payload   Existing payload.
	 * @param int   $target_id Ambassador user ID.
	 * @param int   $action_id Action instance ID.
	 *
	 * @return array
	 */
	public static function get_ambassador_email_payload( array $payload, int $target_id, int $action_id ): array {
		if ( ! $target_id ) {
			return $payload;
		}

		$user = get_userdata( $target_id );
		if ( ! $user ) {
			return $payload;
		}

		$fundraiser_ids = get_posts(
			array(
				'post_type'      => 'fundraiser',
				'author'         => $target_id,
				'posts_per_page' => -1,
				'fields'         => 'ids',
				'post_status'    => 'publish',
			)
		);

		$total_raised    = 0.0;
		$total_donations = 0;
		foreach ( $fundraiser_ids as $fid ) {
			$cache            = FundraiserCache::get_or_update_cache( (int) $fid );
			$total_raised    += (float) ( $cache['total_amount'] ?? 0.0 );
			$total_donations += (int) ( $cache['completed_count'] ?? 0 );
		}

		if ( empty( $payload['to'] ) ) {
			$payload['to'] = $user->user_email;
		}

		$payload['template_vars'] = array_merge(
			$payload['template_vars'] ?? array(),
			array(
				'{{first_name}}'       => $user->first_name ? $user->first_name : $user->display_name,
				'{{last_name}}'        => $user->last_name,
				'{{email}}'            => $user->user_email,
				'{{total_raised}}'     => number_format( $total_raised, 2 ),
				'{{total_donations}}'  => (string) $total_donations,
				'{{fundraiser_count}}' => (string) count( $fundraiser_ids ),
			)
		);

		return $payload;
	}
}
