<?php
/**
 * Hyvä Themes - https://hyva.io
 * Copyright © Hyvä Themes. All rights reserved.
 * See COPYING.txt for license details.
 */

declare(strict_types=1);

use Hyva\Theme\Model\ViewModelRegistry;
use Hyva\Theme\ViewModel\ProductPage;
use Hyva\Theme\ViewModel\ProductPrice;
use Magento\Catalog\Block\Product\AbstractProduct;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Pricing\Price\FinalPrice;
use Magento\Catalog\Pricing\Price\RegularPrice;
use Magento\Catalog\Pricing\Price\TierPrice;
use Magento\Framework\Escaper;
use Magento\Framework\Pricing\Render;
use Magento\Msrp\Pricing\Price\MsrpPrice;
use Magento\Tax\Model\Config;

/** @var AbstractProduct $block */
/** @var Escaper $escaper */
/** @var ViewModelRegistry $viewModels */
/** @var Product $product */
$product = $block->getProduct();

/** @var ProductPrice $productPriceViewModel */
$productPriceViewModel = $viewModels->require(ProductPrice::class);

/** @var ProductPage $productViewModel */
$productViewModel = $viewModels->require(ProductPage::class);

$regularPrice = $productPriceViewModel->getPriceValue(RegularPrice::PRICE_CODE, $product);
$finalPrice = $productPriceViewModel->getPriceValue(FinalPrice::PRICE_CODE, $product);
$tierPrices = $productPriceViewModel->getTierPrices(TierPrice::PRICE_CODE, $product);

if ($productPriceViewModel->displayPriceInclAndExclTax()) {
    $regularPriceExclTax = $productPriceViewModel->getPriceValueExclTax(RegularPrice::PRICE_CODE, $product);
    $finalPriceExclTax = $productPriceViewModel->getPriceValueExclTax(FinalPrice::PRICE_CODE, $product);
}

$displayTax = $productPriceViewModel->displayPriceIncludingTax();
$availability = $product->getIsSalable() ? 'http://schema.org/InStock' : 'http://schema.org/OutOfStock';
$priceValidUntil = $product->getSpecialToDate();

// We expect to refactor MSRP Support on the product detail page, which is why we put this code here
// and not in a view model. Once it is a method on a view model, it is part of the public API and
// removing it is a backward compatibility-breaking change.
// So we won't do that. Removing it from the template, however, is backward compatible, so here it is.
if ($msrpPrice = iterator_to_array($product->getPriceInfo()->getPrices())[MsrpPrice::PRICE_CODE] ?? null) {
    if ($msrpPrice->canApplyMsrp($product) && $msrpPrice->isMinimalPriceLessMsrp($product)) {
        if (! ($priceRender = $block->getLayout()->getBlock('product.price.render.default'))) {
            $priceRender = $block->getLayout()->createBlock(
                Render::class,
                'product.price.render.default',
                ['data' => ['price_render_handle' => 'catalog_product_prices']]
            );
        }
        echo /** @noEscape */ $priceRender->render(FinalPrice::PRICE_CODE, $product);
        return;
    }
}
?>
<script>
    function initPrice<?= (int)$product->getId() ?>() {

        <?php /* All four of these keys are used - they are rendered by PHP */ ?>
        const regularPriceInclTaxKey = 'oldPrice',
              regularPriceExclTaxKey = 'baseOldPrice',
              finalPriceInclTaxKey = 'finalPrice',
              finalPriceExclTaxKey = 'basePrice';

        function calculateCustomOptionPrices(activeCustomOptions, customOptionPrices) {
            return activeCustomOptions.reduce((priceAccumulator, activeCustomOptionId) => {
                const customOptionPrice = customOptionPrices[activeCustomOptionId];
                if (customOptionPrice) {
                    return Number.parseFloat(priceAccumulator) + Number.parseFloat(customOptionPrice);
                }
                return priceAccumulator;
            }, 0);
        }

        return {
            regularPriceKey: <?= $displayTax ? 'regularPriceInclTaxKey' : 'regularPriceExclTaxKey' ?>,
            finalPriceKey: <?= $displayTax ? 'finalPriceInclTaxKey' : 'finalPriceExclTaxKey' ?>,
            activeProductsPriceData: false,
            initialFinalPrice: <?= (float)$finalPrice ?>,
            calculatedFinalPrice: false,
            calculatedFinalPriceWithCustomOptions: false,
            initialTierPrices: <?= /** @noEscape */ json_encode($tierPrices) ?>,
            showRegularPriceLabel: <?= ($finalPrice < $regularPrice) ? 'true' : 'false' ?>,
            customOptionPrices: [],
            <?php if ($productPriceViewModel->displayPriceInclAndExclTax()): ?>
            initialBasePrice: <?= (float)$finalPriceExclTax ?>,
            calculatedBasePrice: false,
            customOptionBasePrices: [],
            calculatedBasePriceWithCustomOptions: false,
            <?php endif; ?>
            activeCustomOptions: [],
            qty: 1,
            updateCustomOptionActive(data) {
                let activeCustomOptions = this.activeCustomOptions;
                const customOptionId = data.customOptionId;

                if (data.active) {
                    if (!activeCustomOptions.includes(customOptionId)) {
                        activeCustomOptions.push(data.customOptionId);
                    }
                } else {
                    if (customOptionId && activeCustomOptions.includes(customOptionId)) {
                        let index = activeCustomOptions.indexOf(customOptionId);
                        activeCustomOptions.splice(index, 1);
                    }
                }
                this.calculateFinalPriceWithCustomOptions()
            },
            updateCustomOptionPrices(prices, basePrices) {
                if (prices) {
                    this.customOptionPrices = prices;
                }

                <?php if ($productPriceViewModel->displayPriceInclAndExclTax()): ?>
                if (basePrices) {
                    this.customOptionBasePrices = basePrices;
                }
                <?php endif; ?>

                this.calculateFinalPriceWithCustomOptions();
            },
            calculateFinalPrice() {

                <?php /* Magento\ConfigurableProduct\Block\Product\View\Type\Configurable::getTierPricesByProduct option prices */ ?>
                <?php /* only sets the basePrice key if display "display incl + excl" is set. Other product types always set it. */ ?>
                <?php /* If display is set to excl tax, we need to fall back to 'price' for configurable products. */ ?>
                const getOptionTierPrice = (tierPrice, withTax) => {
                    <?php if ($product->getTypeId() === 'configurable' && $productPriceViewModel->getPriceDisplayType() === Config::DISPLAY_TYPE_EXCLUDING_TAX): ?>
                    return tierPrice['price'];
                    <?php else: ?>
                    return tierPrice[withTax ? 'price' : 'basePrice'];
                    <?php endif; ?>
                }

                const findApplicableTierPrice = (initialPrice, withTax) => {
                    if (this.activeProductsPriceData && this.activeProductsPriceData.tierPrices) {
                        return this.activeProductsPriceData.tierPrices.reduce((acc, tierPrice) => {
                            const tierPriceCandidate = getOptionTierPrice(tierPrice, withTax);
                            if (this.qty >= tierPrice.qty && tierPriceCandidate < acc) {
                                return tierPriceCandidate;
                            }
                            return acc;
                        }, this.activeProductsPriceData[withTax ? finalPriceInclTaxKey : finalPriceExclTaxKey].amount);

                    } else {
                        const key = withTax ? 'price_incl_tax' : 'price_excl_tax';
                        return Object.values(this.initialTierPrices).reduce((acc, tierPrice) => {
                            if (this.qty >= tierPrice.price_qty && tierPrice[key] < acc) {
                                return tierPrice[key];
                            }
                            return acc;
                        }, initialPrice);

                    }
                }

                this.calculatedFinalPrice = findApplicableTierPrice(this.initialFinalPrice, <?= $displayTax ? 'true' : 'false' ?>);
                window.dispatchEvent(new CustomEvent("update-product-final-price", {detail: this.calculatedFinalPrice}));

                <?php if ($productPriceViewModel->displayPriceInclAndExclTax()): ?>
                this.calculatedBasePrice = findApplicableTierPrice(<?= (float) $finalPriceExclTax ?>, false);
                window.dispatchEvent(new CustomEvent("update-product-base-price", {detail: {basePrice: this.calculatedBasePrice}}));
                <?php endif; ?>
            },
            calculatePriceLabelVisibility() {
                this.showRegularPriceLabel =
                    (this.calculatedFinalPrice === this.activeProductsPriceData[this.regularPriceKey].amount) &&
                    this.activeProductsPriceData.isMinimalPrice;
            },
            calculateFinalPriceWithCustomOptions() {
                const finalPrice = this.calculatedFinalPrice || this.initialFinalPrice;
                this.calculatedFinalPriceWithCustomOptions = finalPrice + this.getCustomOptionPrice();
                <?php if ($productPriceViewModel->displayPriceInclAndExclTax()): ?>
                const basePrice = this.calculatedBasePrice || this.initialBasePrice;
                this.calculatedBasePriceWithCustomOptions = basePrice + this.getCustomOptionBasePrice();
                <?php endif; ?>
            },
            getCustomOptionPrice() {
                return calculateCustomOptionPrices(this.activeCustomOptions, this.customOptionPrices);
            },
            <?php if ($productPriceViewModel->displayPriceInclAndExclTax()): ?>
            getCustomOptionBasePrice() {
                return calculateCustomOptionPrices(this.activeCustomOptions, this.customOptionBasePrices);
            },
            <?php endif; ?>
            getFormattedFinalPrice() {
                return hyva.formatPrice(
                    this.calculatedFinalPriceWithCustomOptions ||
                    this.calculatedFinalPrice ||
                    this.initialFinalPrice
                )
            },
            <?php if ($productPriceViewModel->displayPriceInclAndExclTax()): ?>
            getFormattedBasePrice() {
                return hyva.formatPrice(
                    this.calculatedBasePriceWithCustomOptions ||
                    this.calculatedBasePrice ||
                    this.initialBasePrice
                )
            },
            <?php endif; ?>
            isPriceHidden() {
                const finalPrice = this.calculatedFinalPriceWithCustomOptions ||
                    this.calculatedFinalPrice ||
                    this.initialFinalPrice;
                return <?= $product->isSaleable() ? 'false' : 'true' ?> && finalPrice === 0;
            },
            eventListeners: {
                ['@update-prices-<?= (int)$product->getId() ?>.window'](event) {
                    this.activeProductsPriceData = event.detail;

                    this.calculateFinalPrice();
                    this.calculateFinalPriceWithCustomOptions();
                    this.calculatePriceLabelVisibility();
                },
                ['@update-qty-<?= (int)$product->getId() ?>.window'](event) {
                    this.qty = event.detail;
                    this.calculateFinalPrice();
                    this.calculateFinalPriceWithCustomOptions();
                },
                ['@update-custom-option-active.window'](event) {
                    this.updateCustomOptionActive(event.detail);
                },
                ['@update-custom-option-prices.window'](event) {
                    this.updateCustomOptionPrices(event.detail);
                },
                <?php if ($productPriceViewModel->displayPriceInclAndExclTax()): ?>
                ['@update-custom-option-base-prices.window'](event) {
                    this.updateCustomOptionPrices(null, event.detail);
                }
                <?php endif; ?>
            }
        }
    }
</script>
<div x-data="initPrice<?= (int)$product->getId() ?>()"
     x-bind="eventListeners"
     class="price-box price-final_price"
>
    <div x-show="!activeProductsPriceData && !isPriceHidden()">
        <div class="price-container">
            <?php if ($finalPrice < $regularPrice): ?>
                <div class="old-price">
                    <span id="product-price-<?= (int)$product->getId() ?>"
                          class="price-wrapper">
                        <span class="price" x-html="hyva.formatPrice(<?= (float)$regularPrice ?> + getCustomOptionPrice())">
                            <?= /** @noEscape */ $productViewModel->format($regularPrice, false) ?>
                        </span>
                    </span>
                </div>
                <?php if ($productPriceViewModel->displayPriceInclAndExclTax()): ?>
                <div class="old-price-excl-tax">
                    <span>
                        <span><?= $escaper->escapeHtml(__('Excl. Tax')) ?>:</span>
                        <span class="price" x-html="hyva.formatPrice(<?= (float)$regularPriceExclTax ?> + getCustomOptionBasePrice())">
                            <?= /** @noEscape */ $productViewModel->format($regularPriceExclTax, false) ?>
                        </span>
                    </span>
                </div>
                <?php endif; ?>
            <?php endif; ?>

            <div class="final-price" itemprop="offers" itemscope="" itemtype="https://schema.org/Offer">
                <span class="price-label">
                    <?= ($product->canConfigure() && is_int($product->getPrice()))
                        ? $escaper->escapeHtml(__('As low as')) . ':'
                        : '' ?>
                </span>
                <span id="product-price-<?= (int)$product->getId() ?>"
                      class="price-wrapper">
                    <span class="price" x-html="getFormattedFinalPrice()">
                        <?= /** @noEscape */ $productViewModel->format($finalPrice, false) ?>
                    </span>
                </span>
                <meta itemprop="price" content="<?= $escaper->escapeHtmlAttr($finalPrice) ?>">
                <meta itemprop="priceCurrency"
                      content="<?= $escaper->escapeHtmlAttr($productViewModel->getCurrencyData()['code']) ?>">
                <?php if ($priceValidUntil): ?>
                    <meta
                        itemprop="priceValidUntil"
                        content="<?= $escaper->escapeHtmlAttr(date('Y-m-d', strtotime($priceValidUntil))) ?>"
                    >
                <?php endif; ?>
                <link itemprop="availability" href="<?= $escaper->escapeHtmlAttr($availability) ?>">
            </div>
            <?php if ($productPriceViewModel->displayPriceInclAndExclTax()): ?>
                <div class="final-price-excl-tax">
                    <span>
                        <span><?= $escaper->escapeHtml(__('Excl. Tax')) ?>:</span>
                        <span class="price" x-html="getFormattedBasePrice()">
                            <?= /** @noEscape */ $productViewModel->format($finalPriceExclTax, false) ?>
                        </span>
                    </span>
                </div>
            <?php endif; ?>
        </div>
    </div>
    <template x-if="activeProductsPriceData &&
        activeProductsPriceData.oldPrice &&
        activeProductsPriceData[finalPriceKey].amount < activeProductsPriceData[regularPriceKey].amount
    ">
        <div class="old-price flex mr-2">
            <span id="product-price-<?= (int)$product->getId() ?>"
                  class="price-wrapper">
                <span class="price" x-html="hyva.formatPrice(activeProductsPriceData[regularPriceKey].amount + getCustomOptionPrice())"></span>
            </span>
        </div>
    </template>
    <template x-if="activeProductsPriceData">
        <div class="final-price">
            <?php if ($product->canConfigure() && is_int($product->getPrice())): ?>
                <span class="price-label" x-show="showRegularPriceLabel">
                    <?= $escaper->escapeHtml(__('As low as')) ?>:
                </span>
            <?php endif; ?>
            <span id="product-price-<?= (int)$product->getId() ?>"
                  class="price-wrapper">
                <span class="price" x-html="getFormattedFinalPrice()"></span>
            </span>
        </div>
    </template>
    <?php if ($productPriceViewModel->displayPriceInclAndExclTax()): ?>
    <template x-if="activeProductsPriceData">
        <div class="price-excl-tax">
            <span><?= $escaper->escapeHtml(__('Excl. Tax')) ?>:</span>
            <span>
                <span class="price" x-html="getFormattedBasePrice()"></span>
            </span>
        </div>
    </template>
    <?php endif; ?>
</div>
