<?php
/**
 * BookiFlex Payment Extension
 *
 * @package    BookiFlex
 * @subpackage Payment
 * @author     BookiFlex
 * @copyright  Copyright (c) 2025 BookiFlex
 * @license    GPL-2.0-or-later
 *
 * @link       https://bookiflex.com
 */

declare(strict_types=1);

 namespace BookiFlex\Extensions\Payment\Model;

use BookiFlex\Core\Contract\Payment\PaymentAllocationInterface;
use BookiFlex\Core\Model\Identifiable;
use BookiFlex\Core\Model\Reservation\Reservation;
use BookiFlex\Core\Model\Timestamps;
use BookiFlex\Core\Util\DateTimeUtils;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NumberFormatException;
use Brick\Math\Exception\RoundingNecessaryException;
use Brick\Money\Exception\MoneyMismatchException;
use Brick\Money\Exception\UnknownCurrencyException;
use Brick\Money\Money;

defined( 'ABSPATH' ) || exit;

/**
 * Allocate a payment to a reservation, reservation item or penalty.
 */
class PaymentAllocation implements PaymentAllocationInterface
{
    use Timestamps;
    use Identifiable;
    private int $amount;
    private string $currency;

    /**
     * @throws RoundingNecessaryException
     * @throws MoneyMismatchException
     * @throws MathException
     * @throws UnknownCurrencyException
     * @throws NumberFormatException
     */
    public function __construct(
        private readonly PaymentAllocationType $type,
        private readonly Payment $payment,
        private readonly Reservation $reservation,
        Money $amount,
    ) {
        $this->setAmount($amount);
        $this->createdAt = DateTimeUtils::nowUtc();
    }

    public function getType(): PaymentAllocationType
    {
        return $this->type;
    }

    public function getPayment(): Payment
    {
        return $this->payment;
    }

    public function getReservation(): Reservation
    {
        return $this->reservation;
    }

    /**
     * @throws UnknownCurrencyException
     * @throws NumberFormatException
     * @throws RoundingNecessaryException
     */
    public function getAmount(): Money
    {
        return Money::ofMinor($this->amount, $this->currency);
    }

    /**
     * @throws RoundingNecessaryException
     * @throws MoneyMismatchException
     * @throws MathException
     * @throws UnknownCurrencyException
     * @throws NumberFormatException
     */
    public function setAmount(Money $money): void
    {
        if ($money->isGreaterThan($this->payment->getNotAllocatedAmount())) {
            throw new \InvalidArgumentException('Amount is greater than not allocated amount');
        }

        $this->amount   = $money->getMinorAmount()->toInt();
        $this->currency = $money->getCurrency()->getCurrencyCode();
    }
}
