<?php
/**
 * Analytics class
 *
 * @package   PopupMaker
 * @copyright Copyright (c) 2024, Code Atlantic LLC
 */

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

/**
 * Controls the basic analytics methods for Popup Maker
 */
class PUM_Analytics {

	/**
	 * Initializes analytics endpoints and data
	 */
	public static function init() {
		if ( ! self::analytics_enabled() ) {
			return;
		}

		add_action( 'rest_api_init', [ __CLASS__, 'register_endpoints' ] );
		add_action( 'wp_ajax_pum_analytics', [ __CLASS__, 'ajax_request' ] );
		add_action( 'wp_ajax_nopriv_pum_analytics', [ __CLASS__, 'ajax_request' ] );
		add_filter( 'pum_vars', [ __CLASS__, 'pum_vars' ] );
	}

	/**
	 * Checks whether analytics is enabled.
	 *
	 * @return bool
	 */
	public static function analytics_enabled() {
		$disabled = pum_get_option( 'disable_analytics' ) || popmake_get_option( 'disable_popup_open_tracking' );

		return (bool) apply_filters( 'pum_analytics_enabled', ! $disabled );
	}

	/**
	 * Get a list of key pairs for each event type.
	 * Internally used only for meta keys.
	 *
	 * Example returns [[open,opened],[conversion,conversion]].
	 *
	 * Usage examples:
	 * - popup_open_count, popup_last_opened
	 * - popup_conversion_count, popup_last_conversion
	 *
	 * @param string $event Event key.
	 *
	 * @return mixed
	 */
	public static function event_keys( $event ) {
		$keys = [ $event, rtrim( $event, 'e' ) . 'ed' ];

		if ( 'conversion' === $event ) {
			$keys[1] = 'conversion';
		}

		return apply_filters( 'pum_analytics_event_keys', $keys, $event );
	}

	/**
	 * Returns an array of valid event types.
	 *
	 * @return string[]
	 */
	public static function valid_events() {
		return apply_filters( 'pum_analytics_valid_events', [ 'open', 'conversion' ] );
	}

	/**
	 * Track an event.
	 *
	 * This is called by various methods including the ajax & rest api requests.
	 *
	 * Can be used externally such as after purchase tracking.
	 *
	 * @param array $args
	 */
	public static function track( $args = [] ) {
		// TODO: Remove this to support beacon for CTA conversions.
		if ( empty( $args['pid'] ) || $args['pid'] <= 0 ) {
			return;
		}

		$event = sanitize_text_field( $args['event'] );

		$popup = pum_get_popup( $args['pid'] );

		if ( ! pum_is_popup( $popup ) || ! in_array( $event, self::valid_events(), true ) ) {
			return;
		}

		$popup->increase_event_count( $event );

		if ( has_action( 'pum_analytics_' . $event ) ) {
			do_action( 'pum_analytics_' . $event, $popup->ID, $args );
		}

		do_action( 'pum_analytics_event', $args );
	}

	/**
	 * Process ajax requests.
	 *
	 * Only used when WP-JSON Restful API is not available.
	 */
	public static function ajax_request() {

		$args = wp_parse_args(
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$_REQUEST,
			[
				'event'  => null,
				'pid'    => null,
				'method' => null,
			]
		);

		self::track( $args );

		switch ( $args['method'] ) {
			case 'image':
				self::serve_pixel();
				break;

			case 'json':
				self::serve_json();
				break;

			default:
				self::serve_no_content();
				break;
		}
	}

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_Error|mixed
	 */
	public static function analytics_endpoint( WP_REST_Request $request ) {
		$args = $request->get_params();

		if ( ! $args || empty( $args['pid'] ) ) {
			return new WP_Error( 'missing_params', __( 'Missing Parameters.', 'default' ), [ 'status' => 404 ] );
		}

		self::track( $args );

		self::serve_no_content();

		return true;
	}

	/**
	 * @param $param
	 *
	 * @return bool
	 */
	public static function endpoint_absint( $param ) {
		return is_numeric( $param );
	}

	/**
	 * Sanitize eventData parameter (matches Pro's approach).
	 *
	 * Decodes JSON string to array if needed.
	 *
	 * @param mixed $value EventData value (JSON string or array).
	 *
	 * @return array Decoded eventData as array.
	 */
	public static function sanitize_event_data( $value ) {
		// If already an array, return as-is.
		if ( is_array( $value ) ) {
			return $value;
		}

		// Decode JSON string to array (matches Pro's Request::parse_tracking_data).
		if ( is_string( $value ) ) {
			$decoded = json_decode( $value, true );
			return is_array( $decoded ) ? $decoded : [];
		}

		return [];
	}

	/**
	 * Registers the analytics endpoints
	 */
	public static function register_endpoints() {
		register_rest_route(
			self::get_analytics_namespace(),
			self::get_analytics_route(),
			apply_filters(
				'pum_analytics_rest_route_args',
				[
					'methods'             => [ 'GET', 'POST' ],
					'callback'            => [ __CLASS__, 'analytics_endpoint' ],
					'permission_callback' => '__return_true',
					'args'                => [
						'event'     => [
							'required'    => true,
							'description' => __( 'Event Type', 'popup-maker' ),
							'type'        => 'string',
						],
						'pid'       => [
							'required'            => true,
							'description'         => __( 'Popup ID', 'popup-maker' ),
							'type'                => 'integer',
							'validation_callback' => [ __CLASS__, 'endpoint_absint' ],
							'sanitize_callback'   => 'absint',
						],
						'eventData' => [
							'required'          => false,
							'description'       => __( 'Event metadata (JSON or array)', 'popup-maker' ),
							'sanitize_callback' => [ __CLASS__, 'sanitize_event_data' ],
						],
					],
				]
			)
		);
	}

	/**
	 * Adds our analytics endpoint to pum_vars
	 *
	 * @param array $vars The current pum_vars.
	 * @return array The updates pum_vars
	 */
	public static function pum_vars( $vars = [] ) {
		$vars['analytics_enabled'] = self::analytics_enabled();
		$vars['analytics_route']   = self::get_analytics_route();
		if ( function_exists( 'rest_url' ) ) {
			$vars['analytics_api'] = esc_url_raw( rest_url( self::get_analytics_namespace() ) );
		} else {
			$vars['analytics_api'] = false;
		}
		return $vars;
	}

	/**
	 * Gets the analytics namespace
	 *
	 * If bypass adblockers is enabled, will return random or custom string. If not, returns 'pum/v1'.
	 *
	 * @return string The analytics namespce
	 * @since 1.13.0
	 */
	public static function get_analytics_namespace() {
		$version   = 1;
		$namespace = self::customize_endpoint_value( 'pum' );
		return "$namespace/v$version";
	}

	/**
	 * Gets the analytics route
	 *
	 * If bypass adblockers is enabled, will return random or custom string. If not, returns 'analytics'.
	 *
	 * @return string The analytics route
	 * @since 1.13.0
	 */
	public static function get_analytics_route() {
		$route = 'analytics';
		return self::customize_endpoint_value( $route );
	}

	/**
	 * Customizes the endpoint value given to it
	 *
	 * If bypass adblockers is enabled, will return random or custom string. If not, returns the value given to it.
	 *
	 * @param string $value The value to, potentially, customize.
	 * @return string
	 * @since 1.13.0
	 */
	public static function customize_endpoint_value( $value = '' ) {
		$bypass_adblockers = pum_get_option( 'bypass_adblockers', false );
		if ( true === $bypass_adblockers || 1 === intval( $bypass_adblockers ) ) {
			switch ( pum_get_option( 'adblock_bypass_url_method', 'custom' ) ) {
				case 'custom':
					$value = preg_replace( '/[^a-z0-9]+/', '-', pum_get_option( 'adblock_bypass_custom_filename', $value ) );
					break;
				case 'random':
				default:
					$site_url = get_site_url();
					$value    = md5( $site_url . $value );
					break;
			}
		}
		return $value;
	}

	/**
	 * Creates and returns a 1x1 tracking gif to the browser.
	 */
	public static function serve_pixel() {
		$gif = self::get_file( Popup_Maker::$DIR . 'assets/images/beacon.gif' );
		header( 'Content-Type: image/gif' );
		header( 'Content-Length: ' . strlen( $gif ) );
		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		echo $gif;
		exit;
	}

	/**
	 * @param $path
	 *
	 * @return bool|string
	 */
	public static function get_file( $path ) {

		if ( function_exists( 'realpath' ) ) {
			$path = realpath( $path );
		}

		// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
		if ( ! $path || ! @is_file( $path ) ) {
			return '';
		}

		// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
		return @file_get_contents( $path );
	}

	/**
	 * Returns a 204 no content header.
	 */
	public static function serve_no_content() {
		header( 'HTTP/1.0 204 No Content' );
		header( 'Content-Type: image/gif' );
		header( 'Content-Length: 0' );
		exit;
	}

	/**
	 * Serves a proper json response.
	 *
	 * @param mixed $data
	 */
	public static function serve_json( $data = 0 ) {
		header( 'Content-Type: application/json' );
		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		echo PUM_Utils_Array::safe_json_encode( $data );
		exit;
	}
}
