<?php

declare(strict_types=1);
/**
 * Copyright 2022-2025 FOSSBilling
 * Copyright 2011-2021 BoxBilling, Inc.
 * SPDX-License-Identifier: Apache-2.0.
 *
 * @copyright FOSSBilling (https://www.fossbilling.org)
 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0
 */
class Model_ProductTable implements FOSSBilling\InjectionAwareInterface
{
    final public const string CUSTOM = 'custom';
    final public const string LICENSE = 'license';
    final public const string ADDON = 'addon';
    final public const string DOMAIN = 'domain';
    final public const string DOWNLOADABLE = 'downloadable';
    final public const string HOSTING = 'hosting';
    final public const string VPS = 'vps';

    final public const string SETUP_AFTER_ORDER = 'after_order';
    final public const string SETUP_AFTER_PAYMENT = 'after_payment';
    final public const string SETUP_MANUAL = 'manual';

    protected ?Pimple\Container $di = null;

    public function setDi(Pimple\Container $di): void
    {
        $this->di = $di;
    }

    public function getDi(): ?Pimple\Container
    {
        return $this->di;
    }

    public function getUnit(Model_Product $model)
    {
        return $model->unit;
    }

    private function _getPeriodKey(Box_Period $period): string
    {
        $code = $period->getCode();

        try {
            return match ($code) {
                '1W' => 'w',
                '1M' => 'm',
                '3M' => 'q',
                '6M' => 'b',
                '12M' => 'a',
                '1Y' => 'a',
                '2Y' => 'bia',
                '3Y' => 'tria',
            };
        } catch (UnhandledMatchError) {
            throw new FOSSBilling\Exception('Unknown period selected ' . $code);
        }
    }

    public function getPricingArray(Model_Product $model)
    {
        if ($model->product_payment_id) {
            $service = $this->di['mod_service']('product');
            $productPayment = $this->di['db']->load('ProductPayment', $model->product_payment_id);

            return $service->toProductPaymentApiArray($productPayment);
        }

        throw new FOSSBilling\Exception('Product pricing could not be determined. ' . static::class);
    }

    /**
     * Resolved pricing for the initial order line in default currency.
     *
     * @return array{price: float, quantity: int, setup_price: float}
     */
    public function getOrderLineConfig(Model_Product $product, ?array $config = null): array
    {
        $quantity = max(1, (int) ($config['quantity'] ?? 1));

        return [
            'price' => (float) $this->getProductPrice($product, $config),
            'quantity' => $quantity,
            'setup_price' => (float) $this->getProductSetupPrice($product, $config),
        ];
    }

    /**
     * Price for one unit.
     *
     * @return float
     */
    public function getProductSetupPrice(Model_Product $product, ?array $config = null)
    {
        $pp = $this->di['db']->load('ProductPayment', $product->product_payment_id);

        if ($pp->type == Model_ProductPayment::FREE) {
            $price = 0;
        }

        if ($pp->type == Model_ProductPayment::ONCE) {
            $price = (float) $pp->once_setup_price;
        }

        if ($pp->type == Model_ProductPayment::RECURRENT) {
            $period = new Box_Period($config['period']);
            $key = $this->_getPeriodKey($period);
            $price = (float) $pp->{$key . '_setup_price'};
        }

        if (isset($price)) {
            return $price;
        }

        throw new FOSSBilling\Exception('Unknown period selected for setup price');
    }

    /**
     * Price for one unit.
     *
     * @return float
     */
    public function getProductPrice(Model_Product $product, ?array $config = null)
    {
        $pp = $this->di['db']->load('ProductPayment', $product->product_payment_id);

        if ($pp->type == Model_ProductPayment::FREE) {
            $price = 0;
        }

        if ($pp->type == Model_ProductPayment::ONCE) {
            $price = $pp->once_price;
        }

        if ($pp->type == Model_ProductPayment::RECURRENT) {
            if (!isset($config['period'])) {
                throw new FOSSBilling\Exception('Product :id payment type is recurrent, but period was not selected', [':id' => $product->id]);
            }

            $period = new Box_Period($config['period']);
            $key = $this->_getPeriodKey($period);
            $price = $pp->{$key . '_price'};
        }

        if (isset($price)) {
            return $price;
        }

        throw new FOSSBilling\Exception('Unknown Period selected for price');
    }
}
