<?php

declare(strict_types=1);

namespace Australcode\Rewards\Service;

use Australcode\Rewards\Domain\Settings\SettingsRepository;

defined( 'ABSPATH' ) || exit;

/**
 * Resolves a customer-level earning multiplier from their user roles (FREE).
 *
 * Applied by OrderEarningHook on top of the per-product/category override, and
 * stacks with the Pro tier and campaign multipliers. A customer with several
 * configured roles gets the highest of their multipliers; roles without a
 * configured multiplier contribute nothing (default 1.0). The final value is
 * filterable via `acrwd_role_earning_multiplier` for programmatic rules.
 */
final class RoleEarningResolver {

	public function __construct(
		private readonly SettingsRepository $settings,
	) {
	}

	public function apply_multiplier( int $user_id, int $points ): int {
		return (int) floor( $points * $this->multiplier_for_user( $user_id ) );
	}

	public function multiplier_for_user( int $user_id ): float {
		$user  = $user_id > 0 ? get_userdata( $user_id ) : false;
		$roles = ( false !== $user ) ? array_map( 'strval', (array) $user->roles ) : array();

		$multiplier = self::resolve( $this->settings->get()->role_earning_multipliers, $roles );

		/**
		 * Filters the role-based earning multiplier for a customer.
		 *
		 * @param float $multiplier Resolved multiplier (1.0 = no change).
		 * @param int   $user_id    Customer user id.
		 * @param string[] $roles   The customer's role slugs.
		 */
		return (float) apply_filters( 'acrwd_role_earning_multiplier', $multiplier, $user_id, $roles );
	}

	/**
	 * Pure resolution: the highest positive multiplier among the user's roles
	 * that have one configured, else 1.0 (no change).
	 *
	 * @param array<string,float> $map   Role slug => multiplier.
	 * @param string[]            $roles The user's role slugs.
	 */
	public static function resolve( array $map, array $roles ): float {
		$max = 0.0;
		foreach ( $roles as $role ) {
			$factor = $map[ $role ] ?? 0.0;
			if ( $factor > $max ) {
				$max = $factor;
			}
		}

		return $max > 0 ? $max : 1.0;
	}
}
