<?php

namespace Licenzo\Modules\WooCommerce;

use Licenzo\Model\Product;
use Licenzo\Model\ProductVariation;
use Licenzo\Services\Integrations\ProviderAliasService;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

class WooPriceDisplayService
{
    /**
     * In-request cache: woo product/variation id → billing_cycle string or null.
     *
     * @var array<string,string|null>
     */
    private $cycleCache = [];

    public function register(): void
    {
        if (!class_exists('WooCommerce')) {
            return;
        }

        add_filter('woocommerce_get_price_html', [$this, 'appendCycleToPriceHtml'], 90, 2);
        add_filter('woocommerce_cart_item_price', [$this, 'appendCycleToCartItemPrice'], 90, 3);
        add_filter('woocommerce_cart_item_subtotal', [$this, 'appendCycleToCartItemSubtotal'], 90, 3);
    }

    /**
     * Product page, shop/archive pages, related products.
     *
     * @param string $priceHtml
     * @param \WC_Product $product
     * @return string
     */
    public function appendCycleToPriceHtml($priceHtml, $product): string
    {
        $priceHtml = (string) $priceHtml;

        if (!$product || !is_object($product) || !method_exists($product, 'get_id')) {
            return $priceHtml;
        }

        if ($product->is_type('variable')) {
            return $this->handleVariableProductPriceHtml($priceHtml, $product);
        }

        $productId = $product->is_type('variation') ? (int) $product->get_parent_id() : (int) $product->get_id();
        $variationId = $product->is_type('variation') ? (int) $product->get_id() : 0;

        return $this->maybeAppendSuffix($priceHtml, $productId, $variationId);
    }

    /**
     * Cart page per-item price column.
     *
     * @param string $price
     * @param array<string,mixed> $cartItem
     * @param string $cartItemKey
     * @return string
     */
    public function appendCycleToCartItemPrice($price, $cartItem, $cartItemKey): string
    {
        $price = (string) $price;

        if (!is_array($cartItem)) {
            return $price;
        }

        $productId = (int) ($cartItem['product_id'] ?? 0);
        $variationId = (int) ($cartItem['variation_id'] ?? 0);

        return $this->maybeAppendSuffix($price, $productId, $variationId);
    }

    /**
     * Cart page subtotal column.
     *
     * @param string $subtotal
     * @param array<string,mixed> $cartItem
     * @param string $cartItemKey
     * @return string
     */
    public function appendCycleToCartItemSubtotal($subtotal, $cartItem, $cartItemKey): string
    {
        $subtotal = (string) $subtotal;

        if (!is_array($cartItem)) {
            return $subtotal;
        }

        $productId = (int) ($cartItem['product_id'] ?? 0);
        $variationId = (int) ($cartItem['variation_id'] ?? 0);

        return $this->maybeAppendSuffix($subtotal, $productId, $variationId);
    }

    /**
     * For variable products WooCommerce shows a range like "$12 – $49".
     * Append the cycle only when all mapped variations share the same recurring cycle.
     */
    private function handleVariableProductPriceHtml(string $priceHtml, $product): string
    {
        $productId = (int) $product->get_id();

        $licenzoProduct = Product::whereIn('provider', ProviderAliasService::wooAliases())
            ->where('provider_id', (string) $productId)
            ->with('variations')
            ->first();

        if (!$licenzoProduct || $licenzoProduct->variations->isEmpty()) {
            return $priceHtml;
        }

        $cycles = $licenzoProduct->variations
            ->pluck('billing_cycle')
            ->unique()
            ->filter(function ($cycle) {
                return $this->isRecurringCycle($cycle);
            })
            ->values();

        if ($cycles->isEmpty()) {
            return $priceHtml;
        }

        if ($cycles->count() === 1) {
            $suffix = $this->cycleSuffix($cycles->first());
            return $this->appendSuffixHtml($priceHtml, $suffix);
        }

        // Mixed recurring cycles — don't append (individual variation prices will show their own)
        return $priceHtml;
    }

    private function maybeAppendSuffix(string $priceHtml, int $productId, int $variationId): string
    {
        $cycle = $this->resolveBillingCycle($productId, $variationId);

        if ($cycle === null || !$this->isRecurringCycle($cycle)) {
            return $priceHtml;
        }

        $suffix = $this->cycleSuffix($cycle);

        return $this->appendSuffixHtml($priceHtml, $suffix);
    }

    private function appendSuffixHtml(string $priceHtml, string $suffix): string
    {
        if ($suffix === '' || $priceHtml === '') {
            return $priceHtml;
        }

        $marker = 'licenzo-cycle-suffix';
        if (strpos($priceHtml, $marker) !== false) {
            return $priceHtml;
        }

        return $priceHtml . ' <span class="' . esc_attr($marker) . '">/ ' . esc_html($suffix) . '</span>';
    }

    private function resolveBillingCycle(int $productId, int $variationId): ?string
    {
        $cacheKey = $productId . ':' . $variationId;

        if (array_key_exists($cacheKey, $this->cycleCache)) {
            return $this->cycleCache[$cacheKey];
        }

        if ($productId <= 0 && $variationId <= 0) {
            return $this->cycleCache[$cacheKey] = null;
        }

        $query = Product::whereIn('provider', ProviderAliasService::wooAliases());

        $mapped = $query->where(function ($q) use ($productId, $variationId) {
            if ($productId > 0) {
                $q->where('provider_id', (string) $productId);
            }
            if ($variationId > 0) {
                $q->orWhereHas('variations', function ($vq) use ($variationId) {
                    $vq->where('provider_id', (string) $variationId);
                });
            }
        })->with('variations')->first();

        if (!$mapped) {
            return $this->cycleCache[$cacheKey] = null;
        }

        $variation = $variationId > 0
            ? $mapped->variations->firstWhere('provider_id', (string) $variationId)
            : $mapped->variations->first();

        $cycle = $variation ? ($variation->billing_cycle ?? null) : null;

        return $this->cycleCache[$cacheKey] = $cycle;
    }

    private function isRecurringCycle(?string $cycle): bool
    {
        return !in_array((string) $cycle, [
            ProductVariation::PRICE_CYCLE_ONETIME,
            ProductVariation::PRICE_CYCLE_LIFETIME,
            '',
        ], true);
    }

    private function cycleSuffix(string $cycle): string
    {
        switch ($cycle) {
            case ProductVariation::PRICE_CYCLE_WEEKLY:
                return __('week', 'licenzo');
            case ProductVariation::PRICE_CYCLE_MONTHLY:
                return __('month', 'licenzo');
            case ProductVariation::PRICE_CYCLE_YEARLY:
                return __('year', 'licenzo');
            default:
                return '';
        }
    }
}
