<?php

declare(strict_types=1);

namespace BookiFlex\Extensions\Taxation\Models;

use BookiFlex\Core\Contract\Adjustment\AdjustmentMode;
use BookiFlex\Core\Contract\Adjustment\AdjustmentStrategy;
use BookiFlex\Core\Contract\Adjustment\AdjustmentType;
use BookiFlex\Core\Contract\Taxation\TaxableInterface;

defined( 'ABSPATH' ) || exit;

/**
 * Tax rule. The rules described here are applied when using RegularCashier.
 * This cashier operates based on the concept of unit price + taxes.
 * It means that the configured taxes will be maintained or added to the indicated price,
 * and discounts and charges will be applied to this price with taxes included.
 *
 * There are two options: charges and taxes.
 * Taxes operate on the price directly specified for accommodation or service.
 * Charges are based on the price after all taxes have been applied. For example, if the accommodation price is $100
 * and the tax is 10% and it's not included in the price, the price with tax will be $110.
 *
 * And charges will be applied to this $110.
 *
 * $calculation - a function that takes an amount and context and returns a new amount.
 * function (Money $amount, array $context): Money {
 *     ['date', 'checkInDate', 'checkOutDate', 'ratePlan', 'accommodationType', 'adults', 'children'] = $context;
 *    return $amount->multiply(0.1);
 * }
 */
class TaxRule
{
    private string $name;
    private string $entityType; // Entity type (TaxableInterface) to which the tax applies (e.g., 'accommodation', 'service', 'fee')
    private float $rate;
    private string $code;
    private AdjustmentType $type; // tax | charge
    private AdjustmentMode $mode; // percentage | fixed
    private AdjustmentStrategy $strategy; // exclusive | inclusive
    private bool $refundable;
    private $condition;
    private $calculation; // Additional calculation logic

    public function __construct(
        string $name,
        string $entityType,
        float $rate,
        string $code,
        AdjustmentType $type,
        AdjustmentMode $mode,
        AdjustmentStrategy $strategy,
        bool $refundable,
        ?callable $condition = null,
        ?callable $calculation = null
    ) {
        $this->name        = $name;
        $this->entityType  = $entityType;
        $this->rate        = $rate;
        $this->code        = $code;
        $this->mode        = $mode;
        $this->type        = $type;
        $this->strategy    = $strategy;
        $this->refundable  = $refundable;
        $this->condition   = $condition;
        $this->calculation = $calculation;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function appliesTo(TaxableInterface $object): bool
    {
        return is_null($this->condition) || ($this->condition)($object);
    }

    public function calculate(TaxableInterface $object): float
    {
        if ($this->calculation) {
            return (float) ($this->calculation)($object->getTaxableAmount(), $object->getTaxContext());
        }

        return $this->rate;
    }

    public function getCode(): string
    {
        return $this->code;
    }

    public function getEntityType(): string
    {
        return $this->entityType;
    }

    public function getRate(): float
    {
        return $this->rate;
    }

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

    public function getMode(): AdjustmentMode
    {
        return $this->mode;
    }

    public function getStrategy(): AdjustmentStrategy
    {
        return $this->strategy;
    }

    /**
     * If a tax or charge will be cancelled that amount will be refundable
     *
     * @return bool
     */
    public function isRefundable(): bool
    {
        return $this->refundable;
    }
}
