<?php
/**
 * REST API controller for Email Tracking.
 *
 * Handles tracking pixel requests and click tracking
 * for abandoned cart recovery emails.
 *
 * @package EDD\Pro\CartRecovery\REST\Controllers
 * @copyright Copyright (c) 2026, Sandhills Development, LLC
 * @license https://opensource.org/licenses/GPL-3.0 GNU General Public License v3.0
 * @since 3.6.5
 */

namespace EDD\Pro\CartRecovery\REST\Controllers;

// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore

use EDD\Pro\CartRecovery\Tracking\Repository;
use EDD\Utils\Encryption;

/**
 * Email Tracking controller class.
 *
 * @since 3.6.5
 */
final class EmailTracking {

	/**
	 * Tracking repository instance.
	 *
	 * @var Repository
	 */
	private $repository;

	/**
	 * Constructor.
	 *
	 * @since 3.6.5
	 */
	public function __construct() {
		$this->repository = new Repository();
	}

	/**
	 * Tracks an email open event and returns a 1x1 transparent GIF.
	 *
	 * @since 3.6.5
	 * @param \WP_REST_Request $request Request object.
	 * @return \WP_REST_Response Response object containing the tracking pixel.
	 */
	public function track_open( \WP_REST_Request $request ): \WP_REST_Response {
		$email_id = $this->decrypt_open_token( (string) $request->get_param( 'token' ) );

		if ( null !== $email_id ) {
			$this->repository->record_open( $email_id );
		}

		return $this->return_tracking_pixel();
	}

	/**
	 * Tracks an email click event and redirects to the original URL.
	 *
	 * @since 3.6.5
	 * @param \WP_REST_Request $request Request object.
	 * @return void Redirects to the original URL.
	 */
	public function track_click( \WP_REST_Request $request ): void {
		$data = $this->decrypt_click_token( (string) $request->get_param( 'token' ) );

		if ( null !== $data ) {
			$this->repository->record_click( $data['email_id'] );
			edd_redirect( esc_url_raw( $data['url'] ), 302 );
		}

		edd_redirect( home_url(), 302 );
	}

	/**
	 * Decrypts an open-tracking token to an email ID.
	 *
	 * @since 3.6.5
	 * @param string $token The encrypted hex token.
	 * @return int|null The email ID, or null on failure.
	 */
	private function decrypt_open_token( string $token ): ?int {
		if ( empty( $token ) ) {
			return null;
		}

		$decrypted = Encryption::decrypt( $token );

		if ( null === $decrypted || ! is_numeric( $decrypted ) ) {
			return null;
		}

		$email_id = (int) $decrypted;

		return $email_id > 0 ? $email_id : null;
	}

	/**
	 * Decrypts a click-tracking token to an email ID and URL.
	 *
	 * @since 3.6.5
	 * @param string $token The encrypted hex token.
	 * @return array|null Array with 'email_id' (int) and 'url' (string), or null on failure.
	 */
	private function decrypt_click_token( string $token ): ?array {
		if ( empty( $token ) ) {
			return null;
		}

		$decrypted = Encryption::decrypt( $token );

		if ( null === $decrypted ) {
			return null;
		}

		// Split on first pipe: "email_id|url".
		$pipe_pos = strpos( $decrypted, '|' );
		if ( false === $pipe_pos ) {
			return null;
		}

		$email_id_str = substr( $decrypted, 0, $pipe_pos );
		$url          = substr( $decrypted, $pipe_pos + 1 );

		if ( ! is_numeric( $email_id_str ) || empty( $url ) ) {
			return null;
		}

		$email_id = (int) $email_id_str;
		if ( $email_id <= 0 ) {
			return null;
		}

		if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
			return null;
		}

		return array(
			'email_id' => $email_id,
			'url'      => $url,
		);
	}

	/**
	 * Returns a 1x1 transparent GIF.
	 *
	 * @since 3.6.5
	 * @return \WP_REST_Response Response containing transparent GIF.
	 */
	private function return_tracking_pixel(): \WP_REST_Response {
		// Base64-encoded 1x1 transparent GIF.
		$gif_data = base64_decode( 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' );

		$response = new \WP_REST_Response( $gif_data, 200 );
		$response->set_headers(
			array(
				'Content-Type'  => 'image/gif',
				'Cache-Control' => 'no-cache, no-store, must-revalidate',
				'Pragma'        => 'no-cache',
				'Expires'       => '0',
			)
		);

		return $response;
	}
}
