<?php
/**
 * Hyva-compatible Product Button Template
 * Uses Alpine.js instead of jQuery.
 */

use Magento\Framework\Escaper;

/** @var $block \TbiBank\FusionPay\Block\Frontend\ProductButton */
/** @var Escaper $escaper */

if (!$block->isEnabled()) {
    return;
}

$price = $block->getPrice();

if (is_array($price) && isset($price['minPrice'])) {
    $price = number_format($price['minPrice'], 2, '.', '');
} elseif (is_array($price) && isset($price['price'])) {
    $price = number_format($price['price'], 2, '.', '');
} else {
    $price = number_format((float) $price, 2, '.', '');
}

$price = (float) str_replace(',', '', $price);
$labelData = $block->getLabelData();

if ($price > (float) $labelData['maximumVisible']) {
    return;
}

$isInitiallyVisible =
    $price >= (float) $labelData['minimumVisible'];

$standardMin = (float) ($labelData['standardMin'] ?? 0);
$standardInstallment = '';
$tierCount = (int) $labelData['tierCount'];

if (
    $standardMin > 0 &&
    $price >= $standardMin &&
    $tierCount > 0
) {
    $standardMultiplier = 0.0;
    $prevTierLimit = $standardMin;

    /*
     * Match Magento Standard-only behavior:
     * raw price chooses the tier; Standard fee is applied afterward.
     */
    for ($i = 1; $i <= $tierCount; $i++) {
        $currentTierLimit = (float) $labelData["tier{$i}"];
        $currentMultiplier = (float) $labelData["tier{$i}_multiplier"];

        $matchesLowerBound = $i === 1
            ? $price >= $prevTierLimit
            : $price > $prevTierLimit;

        if (
            $matchesLowerBound &&
            $currentTierLimit > 0 &&
            $price <= $currentTierLimit
        ) {
            $standardMultiplier = $currentMultiplier;
            break;
        }

        if ($currentTierLimit > 0) {
            $prevTierLimit = $currentTierLimit;
        }
    }

    if ($standardMultiplier > 0) {
        $standardInstallment = number_format(
            $standardMultiplier * (
                $price + (float) $labelData['standardAnalysisFee']
            ),
            2,
            '.',
            ''
        );
    }
}

$bnplInstallment = number_format(
    ($price + (float) $labelData['bnplAnalysisFee']) / 4,
    2,
    '.',
    ''
);

$bnplLimit = (float) $labelData['bnplMax'];
$hasBnpl = $bnplLimit > 0 && $price <= $bnplLimit;

if (
    $hasBnpl &&
    $standardMin > 0 &&
    $price >= $standardMin
) {
    $mode = 'dual';
} elseif ($hasBnpl) {
    $mode = 'bnpl';
} else {
    $mode = 'standard';
}

$standardTitleDual = (string) __('Pay as little as');
$standardTitleSingle = (string) __('Pay as little as');
$bnplTitleDual = (string) __('Fee-free 4 payments');
$bnplTitleSingle = $bnplTitleDual;
$currency = (string) __('RON');
$monthLabel = (string) __('month');
$monthSuffixDual = '/ ' . $monthLabel;
$monthSuffixSingle = $monthSuffixDual;

$initialTitle =
    $mode === 'bnpl'
        ? $bnplTitleSingle
        : $standardTitleSingle;

$initialAmount =
    $mode === 'bnpl'
        ? $bnplInstallment
        : $standardInstallment;

$initialAmountText =
    number_format((float) $initialAmount, 2, '.', ',') .
    ' ' .
    $currency .
    ' ' .
    ($mode === 'dual'
        ? $monthSuffixDual
        : $monthSuffixSingle);

$tierData = [];

for ($t = 1; $t <= $labelData['tierCount']; $t++) {
    $tierData['t' . $t . 'Limit'] = $labelData["tier{$t}"];
    $tierData['t' . $t] = $labelData["tier{$t}_period"];
    $tierData['f' . $t] = $labelData["tier{$t}_multiplier"];
}

$selectedOption = $block->getConfigValue(
    'tbi_settings/product_page_settings/product_button'
);
$buttonStyle = substr($selectedOption, -1);
$logoUrl = $block->getLogo($buttonStyle);
?>

<div class="w-full mt-4 fusionpay-hyva-product-button">
    <div
        id="tbi_button_container"
        class="tbi_button_container_ro"
        x-data="{
            showButton: <?= $isInitiallyVisible ? 'true' : 'false' ?>,
            currentPrice: <?= $escaper->escapeJs($price) ?>,
            roStateTimer: null,
            quantityChangeHandler: null,

            init() {
                this.$nextTick(() => {
                    // Safe server-rendered fallback first.
                    this.initializeRoButtonFromDataset();

                    // Configurable products may not dispatch a price event on
                    // initial load. Recalculate from the actual visible price
                    // through the exact same path used after variant changes.
                    requestAnimationFrame(() => {
                        this.updatePriceFromDOM();
                    });
                });

                // Quantity lives outside this Alpine component, so listen on
                // document and reuse the same price/state refresh path.
                this.quantityChangeHandler = (event) => {
                    const target = event.target;

                    if (
                        target &&
                        target.matches &&
                        target.matches('#qty, input[name=qty]')
                    ) {
                        this.updatePriceFromDOM();
                    }
                };

                document.addEventListener(
                    'input',
                    this.quantityChangeHandler
                );
                document.addEventListener(
                    'change',
                    this.quantityChangeHandler
                );
            },

            destroy() {
                if (this.quantityChangeHandler) {
                    document.removeEventListener(
                        'input',
                        this.quantityChangeHandler
                    );
                    document.removeEventListener(
                        'change',
                        this.quantityChangeHandler
                    );
                }

                this.clearRoButtonStateTimer();
            },

            parsePrice(price) {
                if (price.match && price.match(/\.\d{3},\d{2}$/)) {
                    price = price.replace(/\./g, '').replace(/,/g, '.');
                } else if (price.match && price.match(/,\d{3}\.\d{2}$/)) {
                    price = price.replace(/,/g, '');
                } else {
                    if (price.includes(',') && !price.includes('.')) {
                        price = price.replace(/,/g, '.');
                    } else {
                        price = price
                            .replace(/\.(?=\d{3}(\D|$))/g, '')
                            .replace(/,/g, '.');
                    }
                }

                return parseFloat(price) || 0;
            },

            updatePriceFromDOM() {
                const priceElement = document.querySelector(
                    '[data-price-type=finalPrice] .price, ' +
                    '.price-final_price .price, ' +
                    '.price-box .price'
                );

                if (!priceElement) {
                    return;
                }

                let price = priceElement.textContent
                    .trim()
                    .replace(/[^0-9.,]/g, '');

                price = this.parsePrice(price);

                const qtyInput = document.querySelector(
                    '#qty, input[name=qty]'
                );
                let quantity = qtyInput
                    ? parseFloat(qtyInput.value)
                    : 1;

                if (
                    !Number.isFinite(quantity) ||
                    quantity <= 0
                ) {
                    quantity = 1;
                }

                const totalAmount = price * quantity;

                const minAmount =
                    parseFloat(
                        this.$refs.body.getAttribute(
                            'data-min-amount'
                        ) || '0'
                    );
                const maxAmount =
                    parseFloat(
                        this.$refs.body.getAttribute(
                            'data-max-amount'
                        ) || '0'
                    );

                const visible =
                    totalAmount > 0 &&
                    (!minAmount || totalAmount >= minAmount) &&
                    (!maxAmount || totalAmount <= maxAmount);

                if (!visible) {
                    this.clearRoButtonStateTimer();
                    this.showButton = false;
                    return;
                }

                this.showButton = true;
                this.currentPrice = price;
                this.updateRoButtonWithLocalStates(
                    totalAmount
                );
            },

            initializeRoButtonFromDataset() {
                const states = this.buildRoStatesFromDataset();

                if (states.length > 0) {
                    this.applyRoButtonStates(states);
                }
            },

            buildRoStatesFromDataset() {
                return this.getRoStatesForMode(
                    this.$refs.body.getAttribute('data-ro-mode') ||
                        'standard',
                    this.$refs.body.getAttribute(
                        'data-ro-standard-amount'
                    ) || '',
                    this.$refs.body.getAttribute(
                        'data-ro-bnpl-amount'
                    ) || '',
                    ''
                );
            },

            buildRoStates(
                totalAmount,
                standardInstallment,
                fallbackLabel
            ) {
                const mode = this.getRoButtonMode(
                    totalAmount,
                    standardInstallment
                );
                const bnplAmount =
                    this.calculateRoBnplInstallment(totalAmount);

                this.$refs.body.setAttribute(
                    'data-ro-bnpl-amount',
                    bnplAmount || ''
                );

                if (
                    standardInstallment &&
                    Number.isFinite(
                        parseFloat(standardInstallment)
                    )
                ) {
                    this.$refs.body.setAttribute(
                        'data-ro-standard-amount',
                        parseFloat(
                            standardInstallment
                        ).toFixed(2)
                    );
                }

                this.$refs.body.setAttribute(
                    'data-ro-mode',
                    mode
                );
                this.$refs.body.setAttribute(
                    'data-ro-dual-state',
                    mode === 'dual' ? '1' : '0'
                );

                return this.getRoStatesForMode(
                    mode,
                    standardInstallment,
                    bnplAmount,
                    fallbackLabel
                );
            },

            getRoButtonMode(totalAmount, standardInstallment) {
                const standardMin = parseFloat(
                    this.$refs.body.getAttribute(
                        'data-ro-standard-min'
                    ) || '0'
                );
                const bnplLimit = parseFloat(
                    this.$refs.body.getAttribute(
                        'data-hide-bnpl'
                    ) || '0'
                );
                const hasStandard = Number.isFinite(
                    parseFloat(standardInstallment)
                );
                const hasBnpl =
                    bnplLimit > 0 &&
                    Number.isFinite(totalAmount) &&
                    totalAmount <= bnplLimit;

                if (
                    hasStandard &&
                    hasBnpl &&
                    standardMin > 0 &&
                    totalAmount >= standardMin
                ) {
                    return 'dual';
                }

                if (hasBnpl) {
                    return 'bnpl';
                }

                return 'standard';
            },

            calculateRoBnplInstallment(totalAmount) {
                if (!Number.isFinite(totalAmount)) {
                    return '';
                }

                const analysisFee =
                    parseFloat(
                        this.$refs.body.getAttribute(
                            'data-bnpl-analysis-fee'
                        ) || '0'
                    ) || 0;

                return (
                    (parseFloat(totalAmount) + analysisFee) / 4
                ).toFixed(2);
            },

            calculateRoStandardInstallment(totalAmount) {
                if (!Number.isFinite(totalAmount)) {
                    return '';
                }

                const price = parseFloat(totalAmount);
                const standardMin = parseFloat(
                    this.$refs.body.getAttribute(
                        'data-ro-standard-min'
                    ) || '0'
                );
                const tierCount =
                    parseInt(
                        this.$refs.body.getAttribute(
                            'data-tier-count'
                        ) || '0',
                        10
                    ) || 0;

                if (
                    !Number.isFinite(standardMin) ||
                    standardMin <= 0 ||
                    price < standardMin ||
                    tierCount <= 0
                ) {
                    return '';
                }

                let multiplier = 0;
                let prevTierLimit = standardMin;

                /*
                 * Same as Magento Standard-only:
                 * raw price selects the tier, fee is added afterward.
                 */
                for (let i = 1; i <= tierCount; i++) {
                    const currentTierLimit =
                        parseFloat(
                            this.$refs.body.getAttribute(
                                'data-t' + i + '-limit'
                            ) || '0'
                        );
                    const currentMultiplier =
                        parseFloat(
                            this.$refs.body.getAttribute(
                                'data-f' + i
                            ) || '0'
                        );

                    const matchesLowerBound =
                        i === 1
                            ? price >= prevTierLimit
                            : price > prevTierLimit;

                    if (
                        matchesLowerBound &&
                        Number.isFinite(currentTierLimit) &&
                        currentTierLimit > 0 &&
                        price <= currentTierLimit
                    ) {
                        multiplier = currentMultiplier;
                        break;
                    }

                    if (
                        Number.isFinite(currentTierLimit) &&
                        currentTierLimit > 0
                    ) {
                        prevTierLimit = currentTierLimit;
                    }
                }

                if (
                    !Number.isFinite(multiplier) ||
                    multiplier <= 0
                ) {
                    return '';
                }

                const analysisFeeStd =
                    parseFloat(
                        this.$refs.body.getAttribute(
                            'data-standard-analysis-fee'
                        ) || '0'
                    ) || 0;

                return (
                    multiplier *
                    (price + analysisFeeStd)
                ).toFixed(2);
            },

            updateRoButtonWithLocalStates(totalAmount) {
                this.applyRoButtonStates(
                    this.buildRoStates(
                        totalAmount,
                        this.calculateRoStandardInstallment(
                            totalAmount
                        ),
                        ''
                    )
                );
            },

            applyRoButtonStates(states) {
                this.clearRoButtonStateTimer();

                if (!states || !states.length) {
                    return;
                }

                this.setRoButtonModeClass(
                    states.length > 1
                        ? 'dual'
                        : (
                            this.$refs.body.getAttribute(
                                'data-ro-mode'
                            ) || 'standard'
                        )
                );

                let index = 0;
                this.renderRoButtonState(states[index]);

                if (states.length > 1) {
                    this.roStateTimer =
                        setInterval(() => {
                            index =
                                (index + 1) %
                                states.length;

                            const direction =
                                index === 1
                                    ? 'down'
                                    : 'up';

                            this.animateRoButtonState(
                                states[index],
                                direction
                            );
                        }, 3000);
                }
            },

            clearRoButtonStateTimer() {
                if (this.roStateTimer) {
                    clearInterval(this.roStateTimer);
                    this.roStateTimer = null;
                }
            },

            getRoStatesForMode(
                mode,
                standardInstallment,
                bnplAmount,
                fallbackLabel
            ) {
                const states = [];

                if (mode === 'dual') {
                    if (
                        standardInstallment &&
                        Number.isFinite(
                            parseFloat(
                                standardInstallment
                            )
                        )
                    ) {
                        states.push({
                            title:
                                this.$refs.body.getAttribute(
                                    'data-ro-standard-title-dual'
                                ) ||
                                '',
                            amount:
                                this.formatRoAmountText(
                                    standardInstallment,
                                    fallbackLabel,
                                    'dual'
                                )
                        });
                    }

                    if (bnplAmount) {
                        states.push({
                            title:
                                this.$refs.body.getAttribute(
                                    'data-ro-bnpl-title-dual'
                                ) ||
                                '',
                            amount:
                                this.formatRoAmountText(
                                    bnplAmount,
                                    '',
                                    'dual'
                                )
                        });
                    }

                    return states;
                }

                if (mode === 'bnpl') {
                    return [{
                        title:
                            this.$refs.body.getAttribute(
                                'data-ro-bnpl-title-single'
                            ) ||
                            '',
                        amount:
                            this.formatRoAmountText(
                                bnplAmount,
                                '',
                                'single'
                            )
                    }];
                }

                return [{
                    title:
                        this.$refs.body.getAttribute(
                            'data-ro-standard-title-single'
                        ) ||
                        '',
                    amount:
                        this.formatRoAmountText(
                            standardInstallment,
                            fallbackLabel,
                            'single'
                        )
                }];
            },

            formatRoAmountText(
                installment,
                fallbackLabel,
                mode
            ) {
                const numeric = parseFloat(installment);

                if (!Number.isFinite(numeric)) {
                    return fallbackLabel || '';
                }

                const currency =
                    this.$refs.body.getAttribute(
                        'data-currency'
                    ) || 'RON';
                const suffixAttr =
                    mode === 'dual'
                        ? 'data-ro-month-suffix-dual'
                        : 'data-ro-month-suffix-single';
                const suffix =
                    this.$refs.body.getAttribute(
                        suffixAttr
                    ) || '';

                const formattedAmount = numeric
                    .toFixed(2)
                    .replace(/\B(?=(\d{3})+(?!\d))/g, ',');

                return (
                    formattedAmount +
                    ' ' +
                    currency +
                    ' ' +
                    suffix
                );
            },

            setRoButtonModeClass(mode) {
                this.$refs.body.classList.remove(
                    'tbi_button_body_ro--standard',
                    'tbi_button_body_ro--bnpl',
                    'tbi_button_body_ro--dual'
                );
                this.$refs.body.classList.add(
                    'tbi_button_body_ro--' + mode
                );
            },

            renderRoButtonState(state) {
                this.$refs.currentTitle.textContent =
                    state.title || '';
                this.$refs.currentAmount.textContent =
                    state.amount || '';
                this.$refs.nextTitle.textContent = '';
                this.$refs.nextAmount.textContent = '';

                this.resetRoButtonTrack();
            },

            resetRoButtonTrack() {
                const track = this.$refs.track;

                track.classList.add('is-resetting');
                track.classList.remove(
                    'is-animating-up',
                    'is-animating-down',
                    'direction-up',
                    'direction-down'
                );

                void track.offsetHeight;

                requestAnimationFrame(() => {
                    track.classList.remove(
                        'is-resetting'
                    );
                });
            },

            animateRoButtonState(state, direction) {
                const track = this.$refs.track;

                this.$refs.nextTitle.textContent =
                    state.title || '';
                this.$refs.nextAmount.textContent =
                    state.amount || '';

                track.classList.remove(
                    'direction-up',
                    'direction-down',
                    'is-animating-up',
                    'is-animating-down'
                );

                if (direction === 'down') {
                    track.classList.add(
                        'direction-down'
                    );
                } else {
                    track.classList.add(
                        'direction-up'
                    );
                }

                void track.offsetHeight;

                const finishAnimation = (event) => {
                    if (
                        event &&
                        event.propertyName !==
                            'transform'
                    ) {
                        return;
                    }

                    track.removeEventListener(
                        'transitionend',
                        finishAnimation
                    );

                    track.classList.add(
                        'is-resetting'
                    );

                    this.$refs.currentTitle.textContent =
                        state.title || '';
                    this.$refs.currentAmount.textContent =
                        state.amount || '';

                    track.classList.remove(
                        'is-animating-up',
                        'is-animating-down',
                        'direction-up',
                        'direction-down'
                    );

                    void track.offsetHeight;

                    this.$refs.nextTitle.textContent = '';
                    this.$refs.nextAmount.textContent = '';

                    requestAnimationFrame(() => {
                        track.classList.remove(
                            'is-resetting'
                        );
                    });
                };

                track.addEventListener(
                    'transitionend',
                    finishAnimation
                );

                requestAnimationFrame(() => {
                    if (direction === 'down') {
                        track.classList.add(
                            'is-animating-down'
                        );
                    } else {
                        track.classList.add(
                            'is-animating-up'
                        );
                    }
                });
            },

            openModal() {
                window.dispatchEvent(
                    new CustomEvent('tbi-open-modal')
                );
            }
        }"
        @price-updated.window="updatePriceFromDOM()"
        @configurable-selection-changed.window="updatePriceFromDOM()"
    >
        <div
            id="tbi_button"
            @click="openModal()"
            x-show="showButton"
            style="display: block;"
            class="tbi_button1 tbi_button_ro"
            data-bnpl-analysis-fee="<?= $escaper->escapeHtmlAttr($labelData['bnplAnalysisFee']) ?>"
            data-standard-analysis-fee="<?= $escaper->escapeHtmlAttr($labelData['standardAnalysisFee']) ?>"
            data-hide-bnpl="<?= $escaper->escapeHtmlAttr($labelData['bnplMax']) ?>"
            data-period-bnpl="<?= $escaper->escapeHtmlAttr($labelData['bnplPeriod']) ?>"
            data-multiplier-bnpl="<?= $escaper->escapeHtmlAttr($labelData['bnplMultiplier']) ?>"
            data-tier-count="<?= $escaper->escapeHtmlAttr($labelData['tierCount']) ?>"
            <?php foreach ($tierData as $key => $value): ?>
                data-<?= $escaper->escapeHtmlAttr(strtolower(preg_replace('/([a-z])([A-Z])/', '$1-$2', $key))) ?>="<?= $escaper->escapeHtmlAttr($value) ?>"
            <?php endforeach; ?>
        >
            <div
                id="tbi_button_body1"
                x-ref="body"
                class="tbi_button_body1 tbi_button_body_ro tbi_button_body_ro--<?= $escaper->escapeHtmlAttr($mode) ?>"
                data-min-amount="<?= $escaper->escapeHtmlAttr($labelData['minimumVisible']) ?>"
                data-max-amount="<?= $escaper->escapeHtmlAttr($labelData['maximumVisible']) ?>"
                data-ro-standard-min="<?= $escaper->escapeHtmlAttr($labelData['standardMin'] ?? 0) ?>"
                data-hide-bnpl="<?= $escaper->escapeHtmlAttr($labelData['bnplMax']) ?>"
                data-bnpl-analysis-fee="<?= $escaper->escapeHtmlAttr($labelData['bnplAnalysisFee']) ?>"
                data-standard-analysis-fee="<?= $escaper->escapeHtmlAttr($labelData['standardAnalysisFee']) ?>"
                data-tier-count="<?= $escaper->escapeHtmlAttr($labelData['tierCount']) ?>"
                data-ro-mode="<?= $escaper->escapeHtmlAttr($mode) ?>"
                data-ro-dual-state="<?= $mode === 'dual' ? '1' : '0' ?>"
                data-ro-standard-amount="<?= $escaper->escapeHtmlAttr($standardInstallment) ?>"
                data-ro-bnpl-amount="<?= $escaper->escapeHtmlAttr($bnplInstallment) ?>"
                data-ro-standard-title-dual="<?= $escaper->escapeHtmlAttr($standardTitleDual) ?>"
                data-ro-standard-title-single="<?= $escaper->escapeHtmlAttr($standardTitleSingle) ?>"
                data-ro-bnpl-title-dual="<?= $escaper->escapeHtmlAttr($bnplTitleDual) ?>"
                data-ro-bnpl-title-single="<?= $escaper->escapeHtmlAttr($bnplTitleSingle) ?>"
                data-currency="<?= $escaper->escapeHtmlAttr($currency) ?>"
                data-ro-month-suffix-dual="<?= $escaper->escapeHtmlAttr($monthSuffixDual) ?>"
                data-ro-month-suffix-single="<?= $escaper->escapeHtmlAttr($monthSuffixSingle) ?>"
                <?php foreach ($tierData as $key => $value): ?>
                    data-<?= $escaper->escapeHtmlAttr(strtolower(preg_replace('/([a-z])([A-Z])/', '$1-$2', $key))) ?>="<?= $escaper->escapeHtmlAttr($value) ?>"
                <?php endforeach; ?>
            >
                <div class="tbi_button_ro_info">
                    <div class="tbi_button_ro_brand">
                        <img
                            src="<?= $escaper->escapeUrl($logoUrl) ?>"
                            id="tbi_button_logo1"
                            class="tbi_button_ro_logo"
                            alt="TBI Bank"
                        >
                    </div>

                    <div class="tbi_button_ro_divider" aria-hidden="true"></div>

                    <div class="tbi_button_ro_copy">
                        <div class="tbi_button_ro_state_viewport">
                            <div
                                class="tbi_button_ro_state_track"
                                x-ref="track"
                            >
                                <div
                                    class="tbi_button_ro_state tbi_button_ro_state--current"
                                >
                                    <div
                                        class="tbi_button_ro_title"
                                        x-ref="currentTitle"
                                    >
                                        <?= $escaper->escapeHtml($initialTitle) ?>
                                    </div>

                                    <div
                                        class="tbi_button_line--ro-amount"
                                        x-ref="currentAmount"
                                    >
                                        <?= $escaper->escapeHtml($initialAmountText) ?>
                                    </div>
                                </div>

                                <div
                                    class="tbi_button_ro_state tbi_button_ro_state--next"
                                    aria-hidden="true"
                                >
                                    <div
                                        class="tbi_button_ro_title"
                                        x-ref="nextTitle"
                                    ></div>
                                    <div
                                        class="tbi_button_line--ro-amount"
                                        x-ref="nextAmount"
                                    ></div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="tbi_button_ro_details">
                    <?= $escaper->escapeHtml(__('Details here')) ?>
                </div>
            </div>
        </div>
    </div>
</div>
