<?php
/**
 * Transactional Mailer
 *
 * Wires WordPress actions to hard-coded transactional email sends.
 * Custom, rule-based emails live in the Automations module; this class
 * only handles the four core lifecycle messages that always fire:
 *
 *  1. Fundraiser live confirmation   → ambassador
 *  2. Email verification reminder    → ambassador  (pending_verification fundraiser)
 *  3. New-donation notification      → ambassador
 *  4. Donation receipt               → donor
 *
 * Every email body is exposed through a dedicated filter so themes / child
 * plugins can customise subject, body or preview text without patching core.
 *
 * @package AmbassadorHub\Mail
 */

namespace AmbassadorHub\Mail;

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

use AmbassadorHub\Accounts\EmailVerification;
use AmbassadorHub\Donations\DonationsModule;
use AmbassadorHub\Fundraisers\Admin\Settings\SettingsManager as FundraisersSettings;
use AmbassadorHub\Fundraisers\FundraisersModule;

/**
 * Registers WordPress action hooks for transactional email sending.
 */
final class TransactionalMailer {

	/**
	 * Register all transactional email hooks.
	 *
	 * Called once from Plugin::load_components().
	 *
	 * @return void
	 */
	public static function init(): void {
		// 1. Fundraiser goes live → confirm to ambassador.
		add_action( 'ambh_fundraiser_published', array( self::class, 'on_fundraiser_published' ) );

		// 2. Fundraiser updated (published state) → confirmation to ambassador.
		add_action( 'ambh_fundraiser_updated', array( self::class, 'on_fundraiser_updated' ) );

		// 2. Fundraiser held for email verification → resend verification email.
		// Priority 20: runs after FundraisersAutomations (priority 10) has fired its trigger.
		add_action( 'transition_post_status', array( self::class, 'on_post_status_transition' ), 20, 3 );

		// 3 + 4. Donation completed at creation time (free / direct PSP flow).
		add_action( 'ambh_donation_created', array( self::class, 'on_donation_created' ), 30, 2 );

		// 3 + 4. Donation transitions to completed after the fact (normal async PSP flow).
		add_action( 'ambh_donation_updated', array( self::class, 'on_donation_updated' ), 30, 3 );
	}

	// =========================================================================
	// Fundraiser events
	// =========================================================================

	/**
	 * Send a "Your fundraiser is live!" confirmation to the fundraiser author.
	 *
	 * Fires on the `ambh_fundraiser_published` action.
	 *
	 * @param int $post_id Fundraiser post ID.
	 * @return void
	 */
	public static function on_fundraiser_published( int $post_id ): void {
		$post = get_post( $post_id );
		if ( ! $post ) {
			return;
		}

		$ambassador = get_userdata( (int) $post->post_author );
		if ( ! $ambassador || ! is_email( $ambassador->user_email ) ) {
			return;
		}

		$fundraiser_title = get_the_title( $post_id );
		$fundraiser_url   = get_permalink( $post_id );
		$first_name       = $ambassador->first_name ? $ambassador->first_name : $ambassador->display_name;

		/* translators: %s: ambassador first name */
		$subject = sprintf( __( 'Your fundraiser is live, %s!', 'ambassadorhub' ), $first_name );

		$body = '<p>'
			. sprintf(
				/* translators: %s: ambassador first name */
				esc_html__( 'Great news, %s! Your fundraiser is now live and ready to receive donations.', 'ambassadorhub' ),
				esc_html( $first_name )
			)
			. '</p>'
			. '<p><strong>' . esc_html( $fundraiser_title ) . '</strong></p>'
			. '<p style="text-align:center;margin:28px 0;">'
			. '<a href="' . esc_url( $fundraiser_url ) . '" target="_blank" rel="noopener noreferrer"'
			. ' style="display:inline-block;padding:12px 28px;background-color:#0073aa;color:#ffffff;'
			. 'font-family:-apple-system,BlinkMacSystemFont,&quot;Segoe UI&quot;,Roboto,Arial,sans-serif;'
			. 'font-size:15px;font-weight:600;text-decoration:none;border-radius:3px;">'
			. esc_html__( 'View your fundraiser', 'ambassadorhub' )
			. '</a></p>'
			. '<p style="font-size:13px;color:#787c82;">'
			. esc_html__( 'Share the link with your network to start collecting donations.', 'ambassadorhub' )
			. '</p>';

		// If the page is missing a photo or description, nudge the ambassador to complete it.
		$has_image       = has_post_thumbnail( $post_id );
		$has_description = ! empty( trim( $post->post_content ) );

		if ( ! $has_image || ! $has_description ) {
			$create_page_url = FundraisersSettings::get_create_fundraiser_page_url();
			$edit_url        = $create_page_url
				? add_query_arg( 'edit_fundraiser_id', $post_id, $create_page_url )
				: '';

			if ( $edit_url ) {
				if ( ! $has_image && ! $has_description ) {
					$tip_text = esc_html__( 'Your personal page is missing a photo and a description. Donors connect better when they can see who they\'re helping — take a moment to complete your page.', 'ambassadorhub' );
				} elseif ( ! $has_image ) {
					$tip_text = esc_html__( 'Your personal page is missing a photo. Adding one helps donors see who they\'re supporting.', 'ambassadorhub' );
				} else {
					$tip_text = esc_html__( 'Your personal page is missing a description. Tell donors who you are and why this cause matters to you.', 'ambassadorhub' );
				}

				$body .= '<div style="border:1px solid #e2e4e7;border-radius:4px;padding:20px 24px;margin:24px 0;">'
					. '<p style="font-weight:700;margin:0 0 8px;font-size:15px;">'
					. esc_html__( 'Make your page even better', 'ambassadorhub' )
					. '</p>'
					. '<p style="margin:0 0 16px;">'
					. $tip_text
					. '</p>'
					. '<p style="text-align:center;margin:0;">'
					. '<a href="' . esc_url( $edit_url ) . '" target="_blank" rel="noopener noreferrer"'
					. ' style="display:inline-block;padding:12px 28px;background-color:#0073aa;color:#ffffff;'
					. 'font-family:-apple-system,BlinkMacSystemFont,&quot;Segoe UI&quot;,Roboto,Arial,sans-serif;'
					. 'font-size:15px;font-weight:600;text-decoration:none;border-radius:3px;">'
					. esc_html__( 'Complete your page', 'ambassadorhub' )
					. '</a></p>'
					. '</div>';
			}
		}

		$preview = esc_html__( 'Your fundraiser is now live and ready to receive donations.', 'ambassadorhub' );

		/**
		 * Filters the "fundraiser live" email before it is sent.
		 *
		 * @param array{subject:string, body:string, preview:string} $email   Email parts.
		 * @param int                                                 $post_id Fundraiser post ID.
		 */
		$email = apply_filters( 'ambh_email_fundraiser_published', compact( 'subject', 'body', 'preview' ), $post_id );

		MailerService::send( $ambassador->user_email, $email['subject'], $email['body'], $email['preview'] );
	}

	/**
	 * Send a "Your fundraiser has been updated" confirmation to the fundraiser author.
	 *
	 * Fires on the `ambh_fundraiser_updated` action.
	 *
	 * @param int $post_id Fundraiser post ID.
	 * @return void
	 */
	public static function on_fundraiser_updated( int $post_id ): void {
		$post = get_post( $post_id );
		if ( ! $post ) {
			return;
		}

		$ambassador = get_userdata( (int) $post->post_author );
		if ( ! $ambassador || ! is_email( $ambassador->user_email ) ) {
			return;
		}

		$fundraiser_title = get_the_title( $post_id );
		$fundraiser_url   = get_permalink( $post_id );
		$first_name       = $ambassador->first_name ? $ambassador->first_name : $ambassador->display_name;

		/* translators: %s: ambassador first name */
		$subject = sprintf( __( 'Your fundraiser has been edited, %s!', 'ambassadorhub' ), $first_name );

		$body = '<p>'
			. sprintf(
				/* translators: %s: ambassador first name */
				esc_html__( 'Hi %s, we wanted to let you know that your fundraiser has been successfully edited. Your changes are now live.', 'ambassadorhub' ),
				esc_html( $first_name )
			)
			. '</p>'
			. '<p><strong>' . esc_html( $fundraiser_title ) . '</strong></p>'
			. '<p style="text-align:center;margin:28px 0;">'
			. '<a href="' . esc_url( $fundraiser_url ) . '" target="_blank" rel="noopener noreferrer"'
			. ' style="display:inline-block;padding:12px 28px;background-color:#0073aa;color:#ffffff;'
			. 'font-family:-apple-system,BlinkMacSystemFont,&quot;Segoe UI&quot;,Roboto,Arial,sans-serif;'
			. 'font-size:15px;font-weight:600;text-decoration:none;border-radius:3px;">'
			. esc_html__( 'View your fundraiser', 'ambassadorhub' )
			. '</a></p>'
			. '<p style="font-size:13px;color:#787c82;">'
			. esc_html__( 'If you did not make these changes, please contact us immediately.', 'ambassadorhub' )
			. '</p>';

		$preview = esc_html__( 'Your fundraiser has been edited and the changes are now live.', 'ambassadorhub' );

		/**
		 * Filters the "fundraiser updated" email before it is sent.
		 *
		 * @param array{subject:string, body:string, preview:string} $email   Email parts.
		 * @param int                                                 $post_id Fundraiser post ID.
		 */
		$email = apply_filters( 'ambh_email_fundraiser_updated', compact( 'subject', 'body', 'preview' ), $post_id );

		MailerService::send( $ambassador->user_email, $email['subject'], $email['body'], $email['preview'] );
	}

	/**
	 * Re-send the email verification message when a fundraiser is held for verification.
	 *
	 * When an ambassador creates a fundraiser before verifying their email the fundraiser
	 * is saved as `pending_verification`. This hook ensures the verification email is
	 * (re-)sent so the ambassador knows they need to confirm their address to go live.
	 *
	 * Fires on the `transition_post_status` action.
	 *
	 * @param string   $new_status New post status.
	 * @param string   $old_status Old post status.
	 * @param \WP_Post $post       Post object.
	 * @return void
	 */
	public static function on_post_status_transition( string $new_status, string $old_status, \WP_Post $post ): void {
		if ( FundraisersModule::POST_TYPE !== $post->post_type ) {
			return;
		}

		if ( 'pending_verification' !== $new_status || $new_status === $old_status ) {
			return;
		}

		// Delegates to the existing verification service so the token is refreshed
		// and the canonical verification email body is used.
		EmailVerification::send_verification_email( (int) $post->post_author );
	}

	// =========================================================================
	// Donation events
	// =========================================================================

	/**
	 * Send donation emails when a donation is created with `completed` status.
	 *
	 * Covers the free / direct PSP flow where the donation is immediately completed.
	 *
	 * Fires on the `ambh_donation_created` action.
	 *
	 * @param int   $donation_id Donation ID.
	 * @param array $data        Donation data array passed to DonationsModule::create().
	 * @return void
	 */
	public static function on_donation_created( int $donation_id, array $data ): void {
		if ( ( $data['status'] ?? '' ) !== 'completed' ) {
			return;
		}

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

		self::send_donation_emails( $donation );
	}

	/**
	 * Send donation emails when an existing donation transitions to `completed`.
	 *
	 * Covers the normal async PSP flow where the donation starts as `pending` and is
	 * later confirmed by a payment webhook.
	 *
	 * Fires on the `ambh_donation_updated` action.
	 *
	 * @param int    $donation_id     Donation ID.
	 * @param array  $data            Updated fields (may be a partial set).
	 * @param object $donation_before Donation snapshot before the update.
	 * @return void
	 */
	public static function on_donation_updated( int $donation_id, array $data, object $donation_before ): void {
		$new_status = $data['status'] ?? null;
		$old_status = $donation_before->status ?? null;

		if ( 'completed' !== $new_status || $new_status === $old_status ) {
			return;
		}

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

		self::send_donation_emails( $donation );
	}

	// =========================================================================
	// Private helpers
	// =========================================================================

	/**
	 * Dispatch both the ambassador notification and the donor receipt for a donation.
	 *
	 * @param object $donation Full donation record from DonationsModule::get().
	 * @return void
	 */
	private static function send_donation_emails( object $donation ): void {
		self::send_ambassador_donation_notification( $donation );
		self::send_donor_confirmation( $donation );
	}

	/**
	 * Notify the fundraiser's ambassador about a new completed donation.
	 *
	 * @param object $donation Full donation record.
	 * @return void
	 */
	private static function send_ambassador_donation_notification( object $donation ): void {
		$fundraiser_id = (int) $donation->fundraiser_id;
		$post          = get_post( $fundraiser_id );

		if ( ! $post ) {
			return;
		}

		$ambassador = get_userdata( (int) $post->post_author );
		if ( ! $ambassador || ! is_email( $ambassador->user_email ) ) {
			return;
		}

		$first_name       = $ambassador->first_name ? $ambassador->first_name : $ambassador->display_name;
		$fundraiser_title = get_the_title( $fundraiser_id );
		$fundraiser_url   = get_permalink( $fundraiser_id );
		$donor_name       = ! empty( $donation->is_anonymous )
			? __( 'Anonymous', 'ambassadorhub' )
			: ( ! empty( $donation->linked_donor_name ) ? $donation->linked_donor_name : __( 'Anonymous', 'ambassadorhub' ) );
		$amount_formatted = number_format_i18n( (float) $donation->amount, 2 );
		$currency         = strtoupper( $donation->currency ? $donation->currency : 'EUR' );

		/* translators: %s: ambassador first name */
		$subject = sprintf( __( 'You received a new donation, %s!', 'ambassadorhub' ), $first_name );

		$body = '<p>'
			. sprintf(
				/* translators: %s: ambassador first name */
				esc_html__( 'Good news, %s! Someone just donated to your fundraiser.', 'ambassadorhub' ),
				esc_html( $first_name )
			)
			. '</p>'
			. '<table style="width:100%;border-collapse:collapse;margin:16px 0;">'
			// Row: Fundraiser.
			. '<tr>'
			. '<td style="padding:8px 0;border-bottom:1px solid #e2e4e7;color:#787c82;font-size:13px;width:40%;">'
			. esc_html__( 'Fundraiser', 'ambassadorhub' )
			. '</td>'
			. '<td style="padding:8px 0;border-bottom:1px solid #e2e4e7;font-weight:600;">'
			. esc_html( $fundraiser_title )
			. '</td>'
			. '</tr>'
			// Row: Donor.
			. '<tr>'
			. '<td style="padding:8px 0;border-bottom:1px solid #e2e4e7;color:#787c82;font-size:13px;">'
			. esc_html__( 'Donor', 'ambassadorhub' )
			. '</td>'
			. '<td style="padding:8px 0;border-bottom:1px solid #e2e4e7;">'
			. esc_html( $donor_name )
			. '</td>'
			. '</tr>'
			// Row: Amount.
			. '<tr>'
			. '<td style="padding:8px 0;color:#787c82;font-size:13px;">'
			. esc_html__( 'Amount', 'ambassadorhub' )
			. '</td>'
			. '<td style="padding:8px 0;font-weight:600;font-size:18px;">'
			. esc_html( $currency . ' ' . $amount_formatted )
			. '</td>'
			. '</tr>'
			. '</table>';

		if ( ! empty( $donation->message ) ) {
			$body .= '<p style="background:#f6f7f7;padding:12px 16px;border-left:3px solid #0073aa;margin:16px 0;font-style:italic;">'
				. esc_html( $donation->message )
				. '</p>';
		}

		$body .= '<p style="text-align:center;margin:28px 0;">'
			. '<a href="' . esc_url( $fundraiser_url ) . '" target="_blank" rel="noopener noreferrer"'
			. ' style="display:inline-block;padding:12px 28px;background-color:#0073aa;color:#ffffff;'
			. 'font-family:-apple-system,BlinkMacSystemFont,&quot;Segoe UI&quot;,Roboto,Arial,sans-serif;'
			. 'font-size:15px;font-weight:600;text-decoration:none;border-radius:3px;">'
			. esc_html__( 'View your fundraiser', 'ambassadorhub' )
			. '</a></p>';

		$preview = sprintf(
			/* translators: 1: currency code, 2: formatted amount */
			__( 'You received a %1$s %2$s donation.', 'ambassadorhub' ),
			$currency,
			$amount_formatted
		);

		/**
		 * Filters the ambassador donation notification email before it is sent.
		 *
		 * @param array{subject:string, body:string, preview:string} $email    Email parts.
		 * @param object                                              $donation Full donation record.
		 */
		$email = apply_filters(
			'ambh_email_ambassador_donation_notification',
			compact( 'subject', 'body', 'preview' ),
			$donation
		);

		MailerService::send( $ambassador->user_email, $email['subject'], $email['body'], $email['preview'] );
	}

	/**
	 * Send a donation receipt to the donor.
	 *
	 * Silently skipped when the donation is anonymous or has no valid email address.
	 *
	 * @param object $donation Full donation record.
	 * @return void
	 */
	private static function send_donor_confirmation( object $donation ): void {
		// Respect the donor's anonymous flag — no receipt for anonymous donations.
		if ( ! empty( $donation->is_anonymous ) ) {
			return;
		}

		$donor_email = $donation->linked_donor_email ?? '';
		if ( ! is_email( $donor_email ) ) {
			return;
		}

		$fundraiser_id    = (int) $donation->fundraiser_id;
		$fundraiser_title = get_the_title( $fundraiser_id );
		$fundraiser_url   = get_permalink( $fundraiser_id );
		$first_name       = $donation->linked_donor_first_name ? $donation->linked_donor_first_name : ( $donation->linked_donor_name ? $donation->linked_donor_name : __( 'there', 'ambassadorhub' ) );
		$amount_formatted = number_format_i18n( (float) $donation->amount, 2 );
		$currency         = strtoupper( $donation->currency ? $donation->currency : 'EUR' );

		/* translators: %s: donor first name */
		$subject = sprintf( __( 'Thank you for your donation, %s!', 'ambassadorhub' ), $first_name );

		$body = '<p>'
			. sprintf(
				/* translators: %s: donor first name */
				esc_html__( 'Hi %s,', 'ambassadorhub' ),
				esc_html( $first_name )
			)
			. '</p>'
			. '<p>'
			. esc_html__( 'Thank you for your generous donation. Your contribution makes a real difference!', 'ambassadorhub' )
			. '</p>'
			. '<table style="width:100%;border-collapse:collapse;margin:16px 0;">'
			// Row: Fundraiser.
			. '<tr>'
			. '<td style="padding:8px 0;border-bottom:1px solid #e2e4e7;color:#787c82;font-size:13px;width:40%;">'
			. esc_html__( 'Fundraiser', 'ambassadorhub' )
			. '</td>'
			. '<td style="padding:8px 0;border-bottom:1px solid #e2e4e7;font-weight:600;">'
			. esc_html( $fundraiser_title )
			. '</td>'
			. '</tr>'
			// Row: Amount.
			. '<tr>'
			. '<td style="padding:8px 0;color:#787c82;font-size:13px;">'
			. esc_html__( 'Donation amount', 'ambassadorhub' )
			. '</td>'
			. '<td style="padding:8px 0;font-weight:600;font-size:18px;">'
			. esc_html( $currency . ' ' . $amount_formatted )
			. '</td>'
			. '</tr>'
			. '</table>'
			. '<p style="text-align:center;margin:28px 0;">'
			. '<a href="' . esc_url( $fundraiser_url ) . '" target="_blank" rel="noopener noreferrer"'
			. ' style="display:inline-block;padding:12px 28px;background-color:#0073aa;color:#ffffff;'
			. 'font-family:-apple-system,BlinkMacSystemFont,&quot;Segoe UI&quot;,Roboto,Arial,sans-serif;'
			. 'font-size:15px;font-weight:600;text-decoration:none;border-radius:3px;">'
			. esc_html__( 'View the fundraiser', 'ambassadorhub' )
			. '</a></p>';

		$preview = sprintf(
			/* translators: 1: currency code, 2: formatted amount, 3: fundraiser title */
			__( 'Your %1$s %2$s donation to %3$s has been received — thank you!', 'ambassadorhub' ),
			$currency,
			$amount_formatted,
			$fundraiser_title
		);

		/**
		 * Filters the donor donation receipt email before it is sent.
		 *
		 * @param array{subject:string, body:string, preview:string} $email    Email parts.
		 * @param object                                              $donation Full donation record.
		 */
		$email = apply_filters(
			'ambh_email_donor_confirmation',
			compact( 'subject', 'body', 'preview' ),
			$donation
		);

		MailerService::send( $donor_email, $email['subject'], $email['body'], $email['preview'] );
	}

	// =========================================================================
	// Team invitation
	// =========================================================================

	/**
	 * Send a team invitation email.
	 *
	 * Sends different copy depending on whether the invitee already has a WP
	 * account ('join') or is new ('new').
	 *
	 * @param string $to   Recipient e-mail address.
	 * @param array{
	 *     first_name:  string,
	 *     sender_name: string,
	 *     team_name:   string,
	 *     invite_url:  string,
	 *     type:        string,
	 * } $data Invitation data.
	 *
	 * @return void
	 */
	public static function send_invite_email( string $to, array $data ): void {
		$first_name  = $data['first_name'];
		$sender_name = $data['sender_name'];
		$team_name   = $data['team_name'];
		$invite_url  = $data['invite_url'];
		$type        = $data['type'];
		$site_name   = get_bloginfo( 'name' );

		/* translators: %s: team name */
		$subject = sprintf( __( 'You have been invited to join %s', 'ambassadorhub' ), $team_name );

		$btn_label = 'join' === $type
			? __( 'Join the team', 'ambassadorhub' )
			: __( 'Create your account', 'ambassadorhub' );

		$btn_style = 'display:inline-block;padding:12px 28px;background-color:#0073aa;color:#ffffff;'
			. 'font-family:-apple-system,BlinkMacSystemFont,&quot;Segoe UI&quot;,Roboto,Arial,sans-serif;'
			. 'font-size:15px;font-weight:600;text-decoration:none;border-radius:3px;';

		$greeting = $first_name
			/* translators: %s: invitee first name */
			? sprintf( __( 'Dear %s,', 'ambassadorhub' ), $first_name )
			: __( 'Hi there,', 'ambassadorhub' );

		$body = '<p>' . esc_html( $greeting ) . '</p>'
			. '<p>'
			. esc_html(
				sprintf(
					/* translators: 1: sender name, 2: team name, 3: site name */
					__( '%1$s has invited you to join %2$s on %3$s.', 'ambassadorhub' ),
					$sender_name,
					$team_name,
					$site_name
				)
			)
			. '</p>'
			. '<p style="text-align:center;margin:28px 0;">'
			. '<a href="' . esc_url( $invite_url ) . '" target="_blank" rel="noopener noreferrer"'
			. ' style="' . esc_attr( $btn_style ) . '">'
			. esc_html( $btn_label )
			. '</a></p>'
			. '<p style="font-size:13px;color:#787c82;">'
			. esc_html__( 'This invitation link expires in 7 days.', 'ambassadorhub' )
			. '</p>';

		$preview = sprintf(
			/* translators: 1: sender name, 2: team name */
			__( '%1$s has invited you to join %2$s.', 'ambassadorhub' ),
			$sender_name,
			$team_name
		);

		/**
		 * Filters the member invitation email before it is sent.
		 *
		 * @param array{subject:string, body:string, preview:string} $email Email parts.
		 * @param array                                               $data  Invitation data.
		 */
		$email = apply_filters( 'ambh_email_member_invite', compact( 'subject', 'body', 'preview' ), $data );

		MailerService::send( $to, $email['subject'], $email['body'], $email['preview'] );
	}
}
