<?php
/**
 * Learns about opt-outs from failed sends.
 *
 * @package TextTango
 */

declare( strict_types=1 );

namespace TextTango\Consent;

use WP_Error;

defined( 'ABSPATH' ) || exit;

/**
 * Turns a `RECIPIENT_SUPPRESSED` refusal into a local suppression (spec §15).
 *
 * This is the whole of the opt-out path, and it is reactive by necessity.
 * TextTango has no inbound-SMS webhook for customer tokens and no SMS
 * suppression endpoint, so a STOP text never reaches this site. The refusal on
 * the next send is the only evidence that arrives.
 *
 * Recording it means that cost is paid once. Every later message to that number
 * is skipped locally, without an API call and without another rejected send.
 */
final class SuppressionListener {

	/**
	 * Register hooks.
	 */
	public function register(): void {
		add_action( 'texttango_send_failed', array( $this, 'maybe_suppress' ), 10, 3 );
	}

	/**
	 * Record a suppression when the API says the recipient opted out.
	 *
	 * @param int      $message_id Log row id.
	 * @param WP_Error $error      Why the send failed.
	 * @param string   $recipient  E.164 recipient.
	 */
	public function maybe_suppress( $message_id, $error, $recipient ): void {
		if ( ! $error instanceof WP_Error || ! is_string( $recipient ) || '' === $recipient ) {
			return;
		}

		$data     = $error->get_error_data();
		$api_code = is_array( $data ) && ! empty( $data['api_code'] ) ? (string) $data['api_code'] : '';

		if ( 'RECIPIENT_SUPPRESSED' !== $api_code
			&& ! Suppressions::is_suppression_error( (string) $error->get_error_code() ) ) {
			return;
		}

		Suppressions::add( $recipient, Suppressions::REASON_OPTED_OUT, Suppressions::SOURCE_API );
	}
}
