<?php

/**
 * Vvveb
 *
 * Copyright (C) 2022  Ziadin Givan
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

namespace Vvveb\System\Cart;

use Vvveb\Sql\Tax_typeSQL;
use Vvveb\System\Cache;

class Tax {
	/** @var array<int, array<int, array{tax_rate_id: int|string, name: string, rate: string, type: string, priority: mixed, based: string}>> */
	private array $taxRates = [];

	/**
	 * @param array<string, mixed> $options
	 */
	public static function getInstance(array $options = []): static {
		static $inst = null;

		if ($inst === null) {
			$inst = new static($options);
		}

		return $inst;
	}

	/**
	 * @param array<string, mixed> $options
	 */
	public function __construct(array $options = []) {
	}

	/**
	 * Set tax rules for a specific region.
	 *
	 * @param int|string $country_id
	 * @param int|string $region_id
	 * @param string $based
	 */
	public function setRegionRules(int|string $country_id, int|string $region_id, string $based = 'store'): void {
		$cache = Cache::getInstance();
		$rules = $cache->cache(APP, "tax_rates.$country_id.$region_id.$based", function () use ($country_id, $region_id, $based): array {
			$taxType  = new Tax_typeSQL();
			$taxRules = $taxType->getRegionRules(['country_id' => $country_id, 'region_id' => $region_id, 'based' => $based]);

			return $taxRules['tax_rule'] ?? [];
		}, 259200);

		foreach ($rules as $rate) {
			$this->taxRates[$rate['tax_type_id']][$rate['tax_rate_id']] = [
				'tax_rate_id' => $rate['tax_rate_id'],
				'name'        => $rate['name'],
				'rate'        => $rate['rate'],
				'type'        => $rate['type'],
				'priority'    => $rate['priority'],
				'based'       => $based,
			];
		}
	}

	/**
	 * Calculate and add taxes for a given value.
	 *
	 * @param float|int|string $value
	 * @param int|string $taxTypeId
	 * @param bool $onlyTax When true only the tax amount is returned, otherwise value + tax.
	 * @return float|int
	 */
	public function addTaxes(float|int|string $value, int|string $taxTypeId, bool $onlyTax = false): float|int {
		$amount = 0;

		if (isset($this->taxRates[$taxTypeId])) {
			foreach ($this->taxRates[$taxTypeId] as $taxRate) {
				$rate = (float) $taxRate['rate'];

			if ($taxRate['type'] === 'f') {//fixed
				$amount += $rate;
			} elseif ($taxRate['type'] === 'p') {//percent
					$amount += (($value / 100) * $rate);
				}
			}
		}

		if ($onlyTax) {
			return $amount;
		}

		return $value + $amount;
	}

	/**
	 * Get individual tax rates for a given value and tax type.
	 *
	 * @param float|int|string $value
	 * @param int|string $taxTypeId
	 * @return array<int, array{tax_rate_id: int|string, name: string, rate: string, type: string, amount: float|int}>
	 */
	public function getRates(float|int|string $value, int|string $taxTypeId): array {
		$taxRate_data = [];

		if (isset($this->taxRates[$taxTypeId])) {
			foreach ($this->taxRates[$taxTypeId] as $taxRate) {
				if (isset($taxRate_data[$taxRate['tax_rate_id']])) {
					$amount = $taxRate_data[$taxRate['tax_rate_id']]['amount'];
				} else {
					$amount = 0;
				}
				$rate  = (float) $taxRate['rate'];
				$value = (float) $value;

				if ($taxRate['type'] === 'f') {
					$amount += $rate;
				} elseif ($taxRate['type'] === 'p') {
					$amount += (($value / 100) * $rate);
				}

				$taxRate_data[$taxRate['tax_rate_id']] = [
					'tax_rate_id' => $taxRate['tax_rate_id'],
					'name'         => $taxRate['name'],
					'rate'         => $taxRate['rate'],
					'type'         => $taxRate['type'],
					'amount'       => $amount,
				];
			}
		}

		return $taxRate_data;
	}

	public function clear(): void {
		$this->taxRates = [];
	}
}
