<?php
/**
 * AudiencePreview — counts customers/orders matching a set of rules.
 *
 * @package CartCue
 */

namespace CartCue;

defined( 'ABSPATH' ) || exit;

class AudiencePreview {

	/** Hard cap on rows scanned per preview call. */
	private const SCAN_LIMIT = 5000;

	/** Max email addresses to return in preview sample. */
	private const SAMPLE_SIZE = 5;

	private Plugin $plugin;

	public function __construct( Plugin $plugin ) {
		$this->plugin = $plugin;
	}

	/**
	 * @return array{count:int,sample:array<int,string>,scanned:int}
	 */
	public function count( string $trigger_slug, array $rules ): array {
		$resource = $this->resolve_resource( $trigger_slug );

		if ( 'customer' === $resource ) {
			return $this->count_customers( $rules );
		}
		if ( 'cart' === $resource ) {
			return $this->count_carts( $rules );
		}

		return $this->count_orders( $rules );
	}

	private function resolve_resource( string $trigger_slug ): string {
		$customer_triggers = [
			'customer_created',
			'customer_win_back',
			'customer_never_purchased',
			'customer_birthday',
			'manual',
		];
		if ( in_array( $trigger_slug, $customer_triggers, true ) ) {
			return 'customer';
		}
		if ( 0 === strpos( $trigger_slug, 'cart_' ) || 0 === strpos( $trigger_slug, 'checkout_' ) ) {
			return 'cart';
		}

		return 'order';
	}

	/**
	 * @return array{count:int,sample:array<int,string>,scanned:int}
	 */
	private function count_customers( array $rules ): array {
		$page    = 1;
		$per     = 200;
		$matched = 0;
		$sample  = [];
		$scanned = 0;

		while ( $scanned < self::SCAN_LIMIT ) {
			$query = new \WP_User_Query(
				[
					'role'    => 'customer',
					'number'  => $per,
					'paged'   => $page,
					'fields'  => 'ID',
					'orderby' => 'ID',
					'order'   => 'ASC',
				]
			);
			$users = $query->get_results();
			if ( empty( $users ) ) {
				break;
			}

			foreach ( $users as $user_id ) {
				$scanned++;
				$data = $this->customer_context( (int) $user_id );
				if ( $this->check_rules( $rules, $data ) ) {
					$matched++;
					if ( count( $sample ) < self::SAMPLE_SIZE && ! empty( $data['customer_email'] ) ) {
						$sample[] = $data['customer_email'];
					}
				}
				if ( $scanned >= self::SCAN_LIMIT ) {
					break;
				}
			}

			if ( count( $users ) < $per ) {
				break;
			}
			$page++;
		}

		return [ 'count' => $matched, 'sample' => $sample, 'scanned' => $scanned ];
	}

	/**
	 * @return array{count:int,sample:array<int,string>,scanned:int}
	 */
	private function count_orders( array $rules ): array {
		$page    = 1;
		$per     = 100;
		$matched = 0;
		$sample  = [];
		$scanned = 0;

		while ( $scanned < self::SCAN_LIMIT ) {
			$orders = \wc_get_orders(
				[
					'limit'  => $per,
					'page'   => $page,
					'status' => [ 'completed', 'processing' ],
					'return' => 'ids',
				]
			);
			if ( empty( $orders ) ) {
				break;
			}

			foreach ( $orders as $order_id ) {
				$scanned++;
				$order = \wc_get_order( (int) $order_id );
				if ( ! $order ) {
					continue;
				}

				$data = [
					'order_id'       => $order->get_id(),
					'customer_id'    => $order->get_customer_id(),
					'customer_email' => $order->get_billing_email(),
				];
				if ( $this->check_rules( $rules, $data ) ) {
					$matched++;
					if ( count( $sample ) < self::SAMPLE_SIZE && ! empty( $data['customer_email'] ) ) {
						$sample[] = $data['customer_email'];
					}
				}
				if ( $scanned >= self::SCAN_LIMIT ) {
					break;
				}
			}

			if ( count( $orders ) < $per ) {
				break;
			}
			$page++;
		}

		return [ 'count' => $matched, 'sample' => $sample, 'scanned' => $scanned ];
	}

	/**
	 * @return array{count:int,sample:array<int,string>,scanned:int}
	 */
	private function count_carts( array $rules ): array {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery
		$carts = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT id, user_id, email, total FROM %i
				WHERE status = 'active' AND email != ''
				ORDER BY updated_at DESC LIMIT %d",
				DB::carts(),
				self::SCAN_LIMIT
			),
			ARRAY_A
		) ?: [];

		$matched = 0;
		$sample  = [];

		foreach ( $carts as $cart ) {
			$data = [
				'cart_id'        => (int) $cart['id'],
				'customer_id'    => (int) $cart['user_id'],
				'customer_email' => (string) $cart['email'],
				'cart_total'     => $cart['total'],
			];
			if ( $this->check_rules( $rules, $data ) ) {
				$matched++;
				if ( count( $sample ) < self::SAMPLE_SIZE && ! empty( $data['customer_email'] ) ) {
					$sample[] = $data['customer_email'];
				}
			}
		}

		return [ 'count' => $matched, 'sample' => $sample, 'scanned' => count( $carts ) ];
	}

	private function check_rules( array $rules, array $data ): bool {
		foreach ( $rules as $rule_config ) {
			$slug = sanitize_key( $rule_config['slug'] ?? '' );
			$rule = $this->plugin->get_rule( $slug );
			if ( ! $rule ) {
				continue;
			}
			if ( ! $rule->evaluate( $rule_config, $data ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * @return array{customer_id:int,customer_email:string}
	 */
	private function customer_context( int $user_id ): array {
		$user = get_user_by( 'id', $user_id );

		return [
			'customer_id'    => $user_id,
			'customer_email' => $user ? (string) $user->user_email : '',
		];
	}
}
