laudis-technologies/fiscal

View on GitHub
src/ScaleRuleCalculation.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php

/*
 * This file is part of the Laudis Fiscal package.
 *
 * (c) Laudis technologies <https://laudis.tech>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Laudis\Fiscal;

use Ds\Vector;

/**
 * @psalm-import-type Rule from Scale
 */
final class ScaleRuleCalculation
{
    private ?ScaleRuleCalculation $previous;
    private ScaleRule $rule;

    public function __construct(?ScaleRuleCalculation $previous, ScaleRule $rule)
    {
        $this->previous = $previous;
        $this->rule = $rule;
    }

    public function calculate(float $value): float
    {
        if ($this->previous === null) {
            $previousLimit = 0.0;
        } else {
            $previousLimit = ScaleRule::LIMIT($this->previous->rule);
        }
        $limit = ScaleRule::LIMIT($this->rule);
        $factor = ScaleRule::FACTOR($this->rule);

        $tbr = max(min($limit, $value) - $previousLimit, 0) * $factor;
        if ($this->previous) {
            $tbr += $this->previous->calculate($value);
        }

        return $tbr;
    }

    /**
     * @return Vector<Rule>
     */
    public function explain(float $value): Vector
    {
        /** @var Vector<Rule> */
        $tbr = new Vector();
        $current = $this;

        do {
            $tbr->push($this->presentRule($current, $value));
            $current = $current->previous;
        } while ($current !== null);

        $tbr->reverse();

        //$aggregation = 0.0;
        //        foreach ($tbr as $i => $results) {
        //            $results['aggregated'] = $aggregation;
        //            $tbr->set($i, $results);
        //            $aggregation += ($results['ruleResult'] ?? 0.0);
        //            $aggregation = is_infinite($aggregation) ? PHP_FLOAT_MAX : $aggregation;
        //        }
        $aggregation = 0.0;
        for ($i = 0; $i < $tbr->count(); ++$i) {
            $results = $tbr->get($i);
            $results['aggregated'] = $aggregation;
            $tbr->set($i, $results);
            $aggregation += ($results['ruleResult'] ?? 0.0);
            $aggregation = is_infinite($aggregation) ? PHP_FLOAT_MAX : $aggregation;
        }

        return $tbr;
    }

    /**
     * @return Rule
     */
    private function presentRule(ScaleRuleCalculation $current, float $value): array
    {
        $limit = $current->rule->getLimit();
        $factor = $current->rule->getFactor();

        $limitId = $limit ? $limit->getId() : null;
        $factorId = $factor ? $factor->getId() : null;
        $limitValue = ScaleRule::LIMIT($current->rule);
        $limitedValue = min($limitValue, $value);
        $previousLimit = $current->previous ? ScaleRule::LIMIT($current->previous->rule) : 0.0;
        $netValue = max($limitedValue - $previousLimit, 0.0);
        $factorValue = ScaleRule::FACTOR($current->rule);
        $ruleResult = $netValue * $factorValue;
        $ruleResult = is_infinite($ruleResult) ? PHP_FLOAT_MAX : $ruleResult;
        $aggregated = 0.0;

        return array_merge(
            compact('limitId', 'factorId', 'limitValue', 'limitedValue', 'aggregated'),
            compact('previousLimit', 'netValue', 'factorValue', 'ruleResult')
        );
    }
}