<?php

declare(strict_types=1);

namespace Numinix\SeekmodoWordPress\Frontend;

use Numinix\SeekmodoWordPress\Blocks\Registry as BlocksRegistry;
use Numinix\SeekmodoWordPress\Bootstrap;
use Numinix\SeekmodoWordPress\Config\Options;

/**
 * Auto-inject PDP / cart recommendation cascades on WooCommerce
 * templates and enqueue the cascade client + vendored web-components.
 *
 * @see seekmodo.com/docs/connectors/recommendations-pdp-cart.md
 */
final class RecommendationsUI
{
    public const SCRIPT_HANDLE = 'seekmodo-recommendations';

    /** @var bool */
    private static $renderedCart = false;

    public static function register(): void
    {
        if (!function_exists('add_action')) {
            return;
        }
        add_action('wp_enqueue_scripts', [self::class, 'enqueue']);
        add_action('woocommerce_after_single_product_summary', [self::class, 'renderPdp'], 25);
        // after_cart is preferred; cart_collaterals covers themes that
        // omit after_cart. Static flag prevents double inject.
        add_action('woocommerce_after_cart', [self::class, 'renderCart'], 20);
        add_action('woocommerce_cart_collaterals', [self::class, 'renderCartFallback'], 5);
        add_filter('woocommerce_add_to_cart_fragments', [self::class, 'cartFragments']);
    }

    public static function enqueue(): void
    {
        if (!self::shouldInject() || !self::isRecoTemplate()) {
            return;
        }

        BlocksRegistry::enqueueSdk();

        $base = defined('SEEKMODO_WP_PLUGIN_URL') ? (string) SEEKMODO_WP_PLUGIN_URL : '';
        $ver  = defined('SEEKMODO_WP_VERSION') ? (string) SEEKMODO_WP_VERSION : '0.0.0';
        $endpoint = function_exists('rest_url')
            ? (string) rest_url('seekmodo/v1/recommendations')
            : '/wp-json/seekmodo/v1/recommendations';

        if (function_exists('wp_register_script') && function_exists('wp_enqueue_script')) {
            wp_register_script(
                self::SCRIPT_HANDLE,
                $base . 'assets/recommendations/seekmodo-recommendations.js',
                [],
                $ver,
                true
            );
            wp_enqueue_script(self::SCRIPT_HANDLE);
            if (function_exists('wp_localize_script')) {
                wp_localize_script(self::SCRIPT_HANDLE, 'seekmodoRecommendations', [
                    'endpoint' => $endpoint,
                ]);
            }
        }
    }

    public static function renderPdp(): void
    {
        if (!self::shouldInject() || !function_exists('is_product') || !is_product()) {
            return;
        }
        $id = function_exists('get_the_ID') ? (int) get_the_ID() : 0;
        if ($id <= 0) {
            return;
        }
        $html = self::placeholderHtml([
            'key'    => 'pdp-cascade',
            'doc_id' => $id,
            'limit'  => 8,
        ]);
        echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- attrs escaped in placeholderHtml
    }

    public static function renderCart(): void
    {
        if (!self::shouldInject() || self::$renderedCart) {
            return;
        }
        self::echoCartPlaceholder();
    }

    /**
     * Fallback when a theme never fires {@see renderCart()} via
     * `woocommerce_after_cart`. Skipped once after_cart has rendered.
     * Runs earlier in the cart template, so we only emit here when
     * after_cart is not hooked by WooCommerce core (rare); otherwise
     * after_cart still wins because we clear and re-render... Actually
     * after_cart runs later, so we defer collaterals output by not
     * marking rendered until shutdown if after_cart exists.
     */
    public static function renderCartFallback(): void
    {
        if (!self::shouldInject() || self::$renderedCart) {
            return;
        }
        // If after_cart will still run, wait for it.
        if (function_exists('has_action') && has_action('woocommerce_after_cart')) {
            return;
        }
        self::echoCartPlaceholder();
    }

    /**
     * Refresh cart placement markup after AJAX add-to-cart / fragments.
     *
     * @param array<string,string> $fragments
     * @return array<string,string>
     */
    public static function cartFragments(array $fragments): array
    {
        if (!self::shouldInject()) {
            return $fragments;
        }
        ob_start();
        self::$renderedCart = false;
        self::echoCartPlaceholder();
        $html = (string) ob_get_clean();
        $fragments['div.seekmodo-cart-reco'] = $html;

        return $fragments;
    }

    /**
     * @param array{
     *   key:string,
     *   doc_id?:int|string,
     *   doc_ids?:list<int|string>,
     *   exclude_doc_ids?:list<int|string>,
     *   limit?:int
     * } $row
     */
    public static function placeholderHtml(array $row): string
    {
        $key = isset($row['key']) ? (string) $row['key'] : '';
        if ($key === '') {
            return '';
        }
        $attrs = ['data-seekmodo-placement="' . esc_attr($key) . '"'];
        if (!empty($row['doc_id'])) {
            $attrs[] = 'data-seekmodo-doc-id="' . esc_attr((string) (int) $row['doc_id']) . '"';
        }
        if (!empty($row['doc_ids']) && is_array($row['doc_ids'])) {
            $ids = array_values(array_filter(array_map('intval', $row['doc_ids']), static fn ($id) => $id > 0));
            if ($ids !== []) {
                $attrs[] = 'data-seekmodo-doc-ids="' . esc_attr(implode(',', $ids)) . '"';
            }
        }
        if (!empty($row['exclude_doc_ids']) && is_array($row['exclude_doc_ids'])) {
            $ex = array_values(array_filter(array_map('intval', $row['exclude_doc_ids']), static fn ($id) => $id > 0));
            if ($ex !== []) {
                $attrs[] = 'data-seekmodo-exclude-doc-ids="' . esc_attr(implode(',', $ex)) . '"';
            }
        }
        if (!empty($row['limit'])) {
            $attrs[] = 'data-seekmodo-limit="' . esc_attr((string) (int) $row['limit']) . '"';
        }

        return '<div ' . implode(' ', $attrs) . '></div>' . "\n";
    }

    public static function shouldInject(): bool
    {
        if (!Options::recommendationsEnabled() || !Options::isPaired()) {
            return false;
        }
        if (!Bootstrap::wooCommerceActive()) {
            return false;
        }
        $verticals = Options::enabledVerticals();
        if (!isset($verticals['commerce'])) {
            return false;
        }
        if (function_exists('apply_filters')) {
            return (bool) apply_filters('seekmodo_recommendations_auto_inject', true);
        }

        return true;
    }

    private static function echoCartPlaceholder(): void
    {
        $ids = self::cartProductIds();
        if ($ids === []) {
            echo '<div id="seekmodo-cart-reco" class="seekmodo-cart-reco" data-seekmodo-cart-reco="1"></div>';
            self::$renderedCart = true;

            return;
        }
        $anchors = array_slice($ids, 0, 3);
        $html    = self::placeholderHtml([
            'key'             => 'cart',
            'doc_id'          => $anchors[0],
            'doc_ids'         => $anchors,
            'exclude_doc_ids' => $ids,
            'limit'           => 12,
        ]);
        echo '<div id="seekmodo-cart-reco" class="seekmodo-cart-reco" data-seekmodo-cart-reco="1">';
        echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- attrs escaped in placeholderHtml
        echo '</div>';
        self::$renderedCart = true;
    }

    private static function isRecoTemplate(): bool
    {
        if (function_exists('is_product') && is_product()) {
            return true;
        }
        if (function_exists('is_cart') && is_cart()) {
            return true;
        }

        return false;
    }

    /**
     * @return list<int>
     */
    private static function cartProductIds(): array
    {
        if (!function_exists('WC') || !is_object(WC()) || !isset(WC()->cart) || !is_object(WC()->cart)) {
            return [];
        }
        $cart = WC()->cart;
        if (!method_exists($cart, 'get_cart')) {
            return [];
        }
        $ids = [];
        foreach ((array) $cart->get_cart() as $item) {
            if (!is_array($item)) {
                continue;
            }
            $pid = isset($item['product_id']) ? (int) $item['product_id'] : 0;
            if ($pid > 0) {
                $ids[] = $pid;
            }
        }

        return array_values(array_unique($ids));
    }
}
