<?php

namespace Licenzo\Modules\EDD;

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

if (!defined('ABSPATH')) {
    exit;
}

class EddPriceDisplayService
{
    /**
     * @var array<string,string|null>
     */
    private $cycleCache = [];

    public function register(): void
    {
        if (!function_exists('EDD')) {
            return;
        }

        add_filter('edd_download_price', [$this, 'appendCycleToPrice'], 90, 3);
        add_filter('edd_purchase_link_args', [$this, 'appendCycleToPurchaseButton'], 90, 1);
        add_filter('edd_cart_item_price', [$this, 'appendCycleToCartItemPrice'], 90, 3);
    }

    /**
     * Append "/month", "/year", etc. to the displayed download price.
     */
    public function appendCycleToPrice($price, $downloadId, $priceId)
    {
        $resolvedPriceId = ($priceId !== false && $priceId !== null) ? (int) $priceId : null;
        $cycle = $this->resolveBillingCycle((int) $downloadId, $resolvedPriceId);

        if ($cycle !== null && $this->isRecurringCycle($cycle)) {
            $suffix = $this->cycleSuffix($cycle);
            $price = $this->appendSuffixHtml((string) $price, $suffix);
        }

        return $price;
    }

    /**
     * Append cycle label to the purchase button price display.
     */
    public function appendCycleToPurchaseButton(array $args): array
    {
        $downloadId = (int) ($args['download_id'] ?? 0);
        $priceId = isset($args['price_id']) ? (int) $args['price_id'] : null;

        $cycle = $this->resolveBillingCycle($downloadId, $priceId);

        if ($cycle !== null && $this->isRecurringCycle($cycle) && isset($args['price'])) {
            $suffix = $this->cycleSuffix($cycle);
            if ($suffix !== '' && strpos((string) $args['price'], 'licenzo-cycle-suffix') === false) {
                $args['price'] = $args['price'] . ' / ' . $suffix;
            }
        }

        return $args;
    }

    /**
     * Append cycle to cart item price display.
     */
    public function appendCycleToCartItemPrice($price, $item, $key)
    {
        $downloadId = (int) ($item['id'] ?? 0);
        $priceId = isset($item['options']['price_id']) ? (int) $item['options']['price_id'] : null;

        $cycle = $this->resolveBillingCycle($downloadId, $priceId);

        if ($cycle !== null && $this->isRecurringCycle($cycle)) {
            $suffix = $this->cycleSuffix($cycle);
            $price = $this->appendSuffixHtml((string) $price, $suffix);
        }

        return $price;
    }

    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 $downloadId, ?int $priceId): ?string
    {
        $cacheKey = $downloadId . ':' . ($priceId ?? 'null');

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

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

        $mapped = Product::whereIn('provider', ProviderAliasService::eddAliases())
            ->where(function ($q) use ($downloadId, $priceId) {
                $q->where('provider_id', (string) $downloadId);
                if ($priceId !== null) {
                    $q->orWhereHas('variations', function ($vq) use ($priceId) {
                        $vq->where('provider_id', (string) $priceId);
                    });
                }
            })
            ->with('variations')
            ->first();

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

        $variation = $priceId !== null
            ? $mapped->variations->firstWhere('provider_id', (string) $priceId)
            : $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 '';
        }
    }
}
