<?php
/**
 * Gets the Abandoned Cart Recovery data to send to our telemetry server.
 *
 * @package     EDD\Pro\Telemetry
 * @copyright   Copyright (c) 2026, Sandhills Development, LLC
 * @license     https://opensource.org/licenses/gpl-2.0.php GNU Public License
 * @since       3.6.8
 */

namespace EDD\Pro\Telemetry;

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

/**
 * Class CartRecovery
 *
 * @since 3.6.8
 */
class CartRecovery {

	/**
	 * Gets the recovered cart revenue data shaped by gateway and currency.
	 *
	 * Returns an array keyed by gateway → currency → 'lifetime' → ['count', 'total'],
	 * plus an all_gateways → all_currencies aggregate. Returns an empty array if the
	 * ACR tables are not registered or the telemetry filter is disabled.
	 *
	 * @since 3.6.8
	 *
	 * @return array
	 */
	public function get(): array {
		if ( ! $this->can_send_cart_recovery_data() ) {
			return array();
		}

		return $this->get_revenue_by_gateway_currency();
	}

	/**
	 * Gets flat key/value stats for Abandoned Cart Recovery.
	 *
	 * Returns an empty array if the ACR tables are not registered or the
	 * telemetry filter is disabled.
	 *
	 * @since 3.6.8
	 *
	 * @return array
	 */
	public function get_flat_stats(): array {
		if ( ! $this->can_send_cart_recovery_data() ) {
			return array();
		}

		$recovered = $this->get_cart_count_by_status( 'recovered' );
		$abandoned = $this->get_cart_count_by_status( 'abandoned' );
		$total     = $recovered + $abandoned;

		$email_config = $this->get_email_configuration();

		return array(
			'acr_recovered_carts'      => $recovered,
			'acr_abandoned_carts'      => $abandoned,
			'acr_emails_sent'          => $this->get_emails_sent_count(),
			'acr_recovery_rate'        => $total > 0 ? round( ( $recovered / $total ) * 100, 2 ) : 0.0,
			'acr_emails_enabled'       => $email_config['emails_enabled'],
			'acr_emails_with_discount' => $email_config['emails_with_discount'],
		);
	}

	/**
	 * Whether cart recovery telemetry data can be sent.
	 *
	 * Checks that the ACR tables are registered on $wpdb, which indicates
	 * the Cart Recovery feature is active on this site.
	 *
	 * @since 3.6.8
	 *
	 * @return bool
	 */
	private function can_send_cart_recovery_data(): bool {
		global $wpdb;

		return isset( $wpdb->edd_acr_carts );
	}

	/**
	 * Gets the count of carts with a given status.
	 *
	 * @since 3.6.8
	 *
	 * @param string $status The cart status to count.
	 * @return int
	 */
	private function get_cart_count_by_status( string $status ): int {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return (int) $wpdb->get_var(
			$wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->edd_acr_carts} WHERE status = %s", $status )
		);
	}

	/**
	 * Gets the total count of ACR emails that have been sent.
	 *
	 * @since 3.6.8
	 *
	 * @return int
	 */
	private function get_emails_sent_count(): int {
		global $wpdb;

		if ( ! isset( $wpdb->edd_acr_emails ) ) {
			return 0;
		}

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->edd_acr_emails}" );
	}

	/**
	 * Gets configuration counts for ACR email sequences.
	 *
	 * Queries enabled abandoned cart emails once and derives both the total
	 * enabled count and the count that include a discount code.
	 *
	 * @since 3.6.8
	 *
	 * @return array {
	 *     @type int $emails_enabled       Number of enabled ACR emails.
	 *     @type int $emails_with_discount Number of enabled ACR emails that have a discount code set.
	 * }
	 */
	private function get_email_configuration(): array {
		$emails_enabled       = 0;
		$emails_with_discount = 0;

		$emails = edd_get_emails(
			array(
				'context' => 'abandoned_cart',
				'status'  => 1,
				'number'  => 9999,
			)
		);

		if ( empty( $emails ) || ! is_array( $emails ) ) {
			return array(
				'emails_enabled'       => 0,
				'emails_with_discount' => 0,
			);
		}

		foreach ( $emails as $email ) {
			++$emails_enabled;

			$template = $email->get_template();
			if ( $template && $template->get_metadata( 'discount_code' ) ) {
				++$emails_with_discount;
			}
		}

		return array(
			'emails_enabled'       => $emails_enabled,
			'emails_with_discount' => $emails_with_discount,
		);
	}

	/**
	 * Builds the gateway/currency revenue array from recovered carts.
	 *
	 * JOINs the recovered ACR carts against EDD orders and groups by gateway
	 * and currency. Also accumulates an all_gateways → all_currencies aggregate.
	 * No order status filter is applied — a refunded order still represents a
	 * cart that was successfully recovered.
	 *
	 * @since 3.6.8
	 *
	 * @return array
	 */
	private function get_revenue_by_gateway_currency(): array {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$results = $wpdb->get_results(
			"SELECT o.gateway, o.currency, COUNT(*) as count, SUM(o.total) as total
			 FROM {$wpdb->edd_acr_carts} c
			 INNER JOIN {$wpdb->edd_orders} o ON c.order_id = o.id
			 WHERE c.status = 'recovered'
			 GROUP BY o.gateway, o.currency"
		);

		if ( empty( $results ) ) {
			return array();
		}

		$data              = array();
		$aggregate_count   = 0;
		$aggregate_total   = 0.0;

		foreach ( $results as $row ) {
			$gateway  = $row->gateway ? $row->gateway : 'unknown';
			$currency = $row->currency ? $row->currency : 'unknown';

			$data[ $gateway ][ $currency ]['lifetime'] = array(
				'count' => (int) $row->count,
				'total' => (float) $row->total,
			);

			$aggregate_count += (int) $row->count;
			$aggregate_total += (float) $row->total;
		}

		// The all_gateways aggregate mirrors the shape used by Orders.php and gives the telemetry
		// server a single pre-summed row for store-wide recovered revenue without needing to
		// aggregate across gateway/currency rows at query time.
		$data['all_gateways']['all_currencies']['lifetime'] = array(
			'count' => $aggregate_count,
			'total' => $aggregate_total,
		);

		return $data;
	}
}
