<?php

declare(strict_types=1);

namespace Australcode\Rewards\Hooks;

use Australcode\Rewards\Domain\Settings\Settings;
use Australcode\Rewards\Domain\Settings\SettingsRepository;
use Australcode\Rewards\Domain\Wallet\CustomerBlock;
use Australcode\Rewards\Domain\Wallet\TransactionType;
use Australcode\Rewards\Service\EarningCalculator;
use Australcode\Rewards\Service\PointsExpiry;
use Australcode\Rewards\Service\RoleEarningResolver;
use Australcode\Rewards\Service\WalletService;
use WC_Order;

defined( 'ABSPATH' ) || exit;

/**
 * Retroactively awards earning points for a customer's past guest orders when they
 * register, matching by billing email (B8, FREE — opt-in via
 * earning_link_guest_orders). Awards the Free base + per-role earning (the order
 * predates the account, so live tier/campaign multipliers do not apply). Idempotent
 * per order via a meta marker; the order itself is left as a guest order (its owner
 * is not reassigned), so the live OrderEarningHook never double-awards it.
 */
final class GuestOrderLinkHook {

	private const LINKED_META = '_acrwd_guest_linked';
	private const MAX_ORDERS  = 50;

	public function __construct(
		private readonly WalletService $wallet,
		private readonly EarningCalculator $calculator,
		private readonly RoleEarningResolver $roles,
		private readonly SettingsRepository $settings,
		private readonly CustomerBlock $blocks,
	) {
	}

	public function register(): void {
		add_action( 'user_register', array( $this, 'on_register' ), 20, 1 );
	}

	public function on_register( int $user_id ): void {
		$settings = $this->settings->get();
		if ( ! $settings->earning_link_guest_orders || ! $settings->earning_enabled ) {
			return;
		}
		if ( ! function_exists( 'wc_get_orders' ) ) {
			return;
		}

		$user = get_userdata( $user_id );
		if ( false === $user || '' === $user->user_email ) {
			return;
		}
		// Don't retroactively credit a blocked customer's guest orders (B6).
		if ( $this->blocks->is_blocked( $user_id ) ) {
			return;
		}

		$orders = wc_get_orders(
			array(
				'customer' => $user->user_email,
				'status'   => $settings->earning_trigger_statuses,
				'limit'    => self::MAX_ORDERS,
			)
		);
		if ( ! is_array( $orders ) ) {
			return;
		}

		foreach ( $orders as $order ) {
			if ( $order instanceof WC_Order ) {
				$this->maybe_link( $order, $user_id, $settings );
			}
		}
	}

	private function maybe_link( WC_Order $order, int $user_id, Settings $settings ): void {
		// Only unattributed guest orders, and only once.
		if ( $order->get_customer_id() > 0 ) {
			return;
		}
		if ( '' !== (string) $order->get_meta( self::LINKED_META ) ) {
			return;
		}

		$points = $this->calculator->points_for_order( $order );
		if ( $points > 0 ) {
			$points = $this->roles->apply_multiplier( $user_id, $points );
		}

		if ( $points > 0 ) {
			$this->wallet->credit(
				$user_id,
				$points,
				TransactionType::Earn,
				/* translators: %d: order number. */
				sprintf( __( 'Earned from guest order #%d', 'australcode-points-rewards' ), $order->get_id() ),
				$order->get_id(),
				PointsExpiry::expires_at( $settings->points_expiration_months )
			);
		}

		$order->update_meta_data( self::LINKED_META, current_time( 'mysql', true ) );
		$order->save();
	}
}
