<?php
namespace WalletUpPro\Core\Blocks;

use WalletUpPro\Core\Config\PaymentMethodsConfig;
use WalletUpPro\Core\Payments\PaymentRegistry;

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

/**
 * Registers the 4 Wallet Up Gutenberg blocks (Payment Button, Payment QR,
 * Pay Link, Tip Jar) for both Free and Pro builds. Server-side renderers
 * delegate to the existing shortcode for Button/QR/Link, and build a
 * custom multi-amount layout for the Tip Jar.
 *
 * Method availability is dictated by what's compiled into the current
 * build via shortcode_exists() — Free renders no-op for Pro-only methods
 * unless the viewer is an admin, in which case they see an upgrade hint.
 */
class BlockRegistrar
{
    const SCRIPT_HANDLE       = 'walletup-blocks-editor';
    const VIEW_SCRIPT_HANDLE  = 'walletup-blocks-tipjar-view';
    const STYLE_HANDLE        = 'walletup-blocks-style';

    public static function init(): void
    {
        add_action('init', [__CLASS__, 'registerBlocks']);
        add_action('enqueue_block_editor_assets', [__CLASS__, 'enqueueEditorAssets']);
        add_action('wp_enqueue_scripts', [__CLASS__, 'enqueueFrontendAssets']);
    }

    public static function registerBlocks(): void
    {
        $commonAttrs = [
            'method' => ['type' => 'string', 'default' => 'cashapp'],
            'amount' => ['type' => 'string', 'default' => ''],
            'align'  => ['type' => 'string', 'default' => 'center'],
        ];

        register_block_type('walletup/payment-button', [
            'api_version'     => 3,
            'attributes'      => $commonAttrs,
            'supports'        => ['html' => false, 'align' => ['left', 'center', 'right', 'wide']],
            'render_callback' => [__CLASS__, 'renderPaymentButton'],
        ]);

        register_block_type('walletup/payment-qr', [
            'api_version'     => 3,
            'attributes'      => $commonAttrs,
            'supports'        => ['html' => false, 'align' => ['left', 'center', 'right', 'wide']],
            'render_callback' => [__CLASS__, 'renderPaymentQr'],
        ]);

        register_block_type('walletup/pay-link', [
            'api_version'     => 3,
            'attributes'      => array_merge($commonAttrs, [
                'linkText' => ['type' => 'string', 'default' => ''],
            ]),
            'supports'        => ['html' => false],
            'render_callback' => [__CLASS__, 'renderPayLink'],
        ]);

        register_block_type('walletup/tip-jar', [
            'api_version'     => 3,
            'attributes'      => array_merge($commonAttrs, [
                'presets'     => ['type' => 'array',   'default' => [3, 5, 10]],
                'allowCustom' => ['type' => 'boolean', 'default' => true],
                'heading'     => ['type' => 'string',  'default' => ''],
            ]),
            'supports'        => ['html' => false, 'align' => ['left', 'center', 'right', 'wide']],
            'render_callback' => [__CLASS__, 'renderTipJar'],
        ]);
    }

    // -----------------------------------------------------------------------
    // Asset enqueue
    // -----------------------------------------------------------------------

    public static function enqueueEditorAssets(): void
    {
        $base = self::distBaseUrl();
        $path = self::distBasePath();
        $jsRel  = 'index.js';
        $cssRel = 'style.css';
        $version = self::pluginVersion();

        if (file_exists($path . $jsRel)) {
            wp_enqueue_script(
                self::SCRIPT_HANDLE,
                $base . $jsRel,
                ['wp-blocks', 'wp-block-editor', 'wp-components', 'wp-element', 'wp-i18n'],
                $version,
                true
            );

            wp_localize_script(self::SCRIPT_HANDLE, 'walletUpBlocksData', self::buildEditorData());
        }

        if (file_exists($path . $cssRel)) {
            wp_enqueue_style(
                self::STYLE_HANDLE,
                $base . $cssRel,
                [],
                $version
            );
        }
    }

    public static function enqueueFrontendAssets(): void
    {
        $base = self::distBaseUrl();
        $path = self::distBasePath();
        $cssRel = 'style.css';
        $version = self::pluginVersion();

        if (file_exists($path . $cssRel)) {
            wp_enqueue_style(
                self::STYLE_HANDLE,
                $base . $cssRel,
                [],
                $version
            );
        }

        // Tip Jar view JS — only on pages that actually contain the block.
        if (has_block('walletup/tip-jar')) {
            $viewRel = 'tip-jar-view.js';
            if (file_exists($path . $viewRel)) {
                wp_enqueue_script(
                    self::VIEW_SCRIPT_HANDLE,
                    $base . $viewRel,
                    [],
                    $version,
                    true
                );
            }
        }
    }

    // -----------------------------------------------------------------------
    // Server-side renderers
    // -----------------------------------------------------------------------

    public static function renderPaymentButton(array $attributes): string
    {
        return self::renderViaShortcode($attributes, 'no');
    }

    public static function renderPaymentQr(array $attributes): string
    {
        return self::renderViaShortcode($attributes, 'yes');
    }

    public static function renderPayLink(array $attributes): string
    {
        $html = self::renderViaShortcode($attributes, 'link');
        $linkText = isset($attributes['linkText']) ? (string) $attributes['linkText'] : '';
        if ($linkText === '' || $html === '') {
            return $html;
        }
        // Replace the link's inner text with the custom label, preserving the href.
        return preg_replace_callback(
            '/(<a\b[^>]*>)(.*?)(<\/a>)/is',
            function ($m) use ($linkText) {
                return $m[1] . esc_html($linkText) . $m[3];
            },
            $html,
            1
        );
    }

    public static function renderTipJar(array $attributes): string
    {
        $method = isset($attributes['method']) ? sanitize_key((string) $attributes['method']) : 'cashapp';
        $presets = isset($attributes['presets']) && is_array($attributes['presets'])
            ? array_values(array_filter(array_map('floatval', $attributes['presets']), function ($v) { return $v > 0; }))
            : [3.0, 5.0, 10.0];
        $allowCustom = !empty($attributes['allowCustom']);
        $heading = isset($attributes['heading']) ? trim((string) $attributes['heading']) : '';
        $align = isset($attributes['align']) ? sanitize_key((string) $attributes['align']) : 'center';

        if (!shortcode_exists($method)) {
            return self::renderUpgradeNoticeIfAdmin($method);
        }

        $holder = self::holderFor($method);
        if ($holder === '') {
            return self::renderMissingHolderIfAdmin($method);
        }

        $buttons = '';
        foreach ($presets as $amt) {
            $amtStr = self::formatAmount($amt);
            $buttons .= '<div class="walletup-tipjar-preset-wrap">'
                . do_shortcode(sprintf('[%s amount="%s" scan="no"]', $method, esc_attr($amtStr)))
                . '</div>';
        }

        if ($allowCustom) {
            // Server-side computed URL template with __WUP_AMT__ placeholder.
            // JS substitutes the user's typed amount on click. Works for every
            // method that has a deeplink (Pro + Free), unlike the legacy 3-method
            // JS-side switch which silently failed for crypto/zelle/mobile money.
            $urlTemplate = self::paymentUrlTemplateFor($method, $holder);
            $buttons .= sprintf(
                '<div class="walletup-tipjar-custom">'
                . '<input type="number" min="1" step="0.01" inputmode="decimal" class="walletup-tipjar-custom-input" placeholder="%s" aria-label="%s">'
                . '<button type="button" class="walletup-pay-button walletup-tipjar-custom-btn" data-method="%s" data-holder="%s" data-url-template="%s">%s</button>'
                . '</div>',
                esc_attr__('Amount', 'walletup'),
                esc_attr__('Custom tip amount', 'walletup'),
                esc_attr($method),
                esc_attr($holder),
                esc_attr($urlTemplate),
                esc_html__('Pay', 'walletup')
            );
        }

        $methodLabel = self::methodLabel($method);

        return sprintf(
            '<div class="walletup-tipjar" data-align="%s">%s<div class="walletup-tipjar-buttons">%s</div><small class="walletup-tipjar-hint">%s %s</small></div>',
            esc_attr($align),
            $heading !== '' ? '<h3 class="walletup-tipjar-heading">' . esc_html($heading) . '</h3>' : '',
            $buttons,
            esc_html__('Pays with', 'walletup'),
            esc_html($methodLabel)
        );
    }

    // -----------------------------------------------------------------------
    // Helpers
    // -----------------------------------------------------------------------

    private static function renderViaShortcode(array $attributes, string $scan): string
    {
        $method = isset($attributes['method']) ? sanitize_key((string) $attributes['method']) : 'cashapp';
        $amount = isset($attributes['amount']) ? sanitize_text_field((string) $attributes['amount']) : '';

        if (!shortcode_exists($method)) {
            return self::renderUpgradeNoticeIfAdmin($method);
        }

        $atts = sprintf(' scan="%s"', esc_attr($scan));
        if ($amount !== '') {
            $atts .= sprintf(' amount="%s"', esc_attr($amount));
        }
        return do_shortcode("[{$method}{$atts}]");
    }

    private static function renderUpgradeNoticeIfAdmin(string $method): string
    {
        if (!current_user_can('manage_options')) {
            return '';
        }
        // A free method with no registered shortcode is simply turned off — point
        // the admin to enable it, not to upgrade. Only genuinely Pro-only methods
        // get the "available in Pro" upsell.
        if (self::isFreeMethod($method)) {
            return sprintf(
                '<div class="walletup-block-admin-notice">%s <strong>%s</strong> %s <a href="%s">%s</a></div>',
                esc_html__('Wallet Up:', 'walletup'),
                esc_html(self::methodLabel($method)),
                esc_html__('is turned off. Turn it on under', 'walletup'),
                esc_url(admin_url('admin.php?page=wallet-up-payments')),
                esc_html__('Wallet Up → Payments', 'walletup')
            );
        }
        return sprintf(
            '<div class="walletup-block-admin-notice">%s <strong>%s</strong>. %s <a href="%s" target="_blank" rel="noopener">%s</a></div>',
            esc_html__('Wallet Up:', 'walletup'),
            esc_html(self::methodLabel($method)),
            esc_html__('is available in Pro.', 'walletup'),
            esc_url('https://walletup.app/pricing/'),
            esc_html__('Upgrade to Pro', 'walletup')
        );
    }

    private static function isFreeMethod(string $method): bool
    {
        if (class_exists(PaymentMethodsConfig::class) && defined(PaymentMethodsConfig::class . '::FREE_METHOD_IDS')) {
            return in_array($method, (array) PaymentMethodsConfig::FREE_METHOD_IDS, true);
        }
        return in_array($method, ['cashapp', 'paypal', 'venmo'], true);
    }

    private static function renderMissingHolderIfAdmin(string $method): string
    {
        if (!current_user_can('manage_options')) {
            return '';
        }
        return sprintf(
            '<div class="walletup-block-admin-notice">%s <a href="%s">%s</a> %s <strong>%s</strong>.</div>',
            esc_html__('Wallet Up:', 'walletup'),
            esc_url(admin_url('admin.php?page=wallet-up-payments')),
            esc_html__('save your details', 'walletup'),
            esc_html__('for', 'walletup'),
            esc_html(self::methodLabel($method))
        );
    }

    private static function buildEditorData(): array
    {
        $isPremium = defined('WALLET_UP_IS_PRO_BUILD') && WALLET_UP_IS_PRO_BUILD;
        $accounts = (array) get_option('wallet_up_transfer_call', []);

        $allMethods = self::knownMethods();
        $available  = self::availableMethodIds();

        $methods = [];
        foreach ($allMethods as $id => $label) {
            $isPro = !in_array($id, ['cashapp', 'paypal', 'venmo'], true);
            $methods[] = [
                'id'        => $id,
                'label'     => $label,
                'isPro'     => $isPro,
                'available' => in_array($id, $available, true),
                'hasHolder' => !empty($accounts[$id . '_holder']),
            ];
        }

        return [
            'methods'    => $methods,
            'isPremium'  => $isPremium,
            'configUrl'  => admin_url('admin.php?page=wallet-up-payments'),
            'upgradeUrl' => 'https://walletup.app/pricing/',
        ];
    }

    /**
     * Methods this build can register/render right now. Free always returns
     * the three free IDs; Pro returns whatever PaymentMethodsConfig exposes.
     */
    private static function availableMethodIds(): array
    {
        if (class_exists(PaymentMethodsConfig::class) && method_exists(PaymentMethodsConfig::class, 'getAvailableMethodIds')) {
            return (array) PaymentMethodsConfig::getAvailableMethodIds();
        }
        return ['cashapp', 'paypal', 'venmo'];
    }

    /**
     * Editor needs to list ALL methods (so users see what's locked behind Pro)
     * regardless of whether the current build can render them. Pulled from
     * PaymentMethodsConfig::PAYMENT_METHODS; falls back to the free triplet.
     */
    private static function knownMethods(): array
    {
        if (class_exists(PaymentMethodsConfig::class) && defined(PaymentMethodsConfig::class . '::PAYMENT_METHODS')) {
            $labels = [];
            foreach (PaymentMethodsConfig::PAYMENT_METHODS as $id => $cfg) {
                $labels[$id] = isset($cfg['name']) ? (string) $cfg['name'] : ucfirst($id);
            }
            if (!empty($labels)) {
                return $labels;
            }
        }
        return [
            'cashapp' => 'Cash App',
            'paypal'  => 'PayPal.me',
            'venmo'   => 'Venmo',
        ];
    }

    private static function methodLabel(string $id): string
    {
        $known = self::knownMethods();
        return $known[$id] ?? ucfirst($id);
    }

    private static function holderFor(string $methodId): string
    {
        $accounts = (array) get_option('wallet_up_transfer_call', []);
        $key = $methodId . '_holder';
        return isset($accounts[$key]) ? trim((string) $accounts[$key]) : '';
    }

    /**
     * Build a URL template for the Tip Jar custom-amount button. The amount
     * slot contains a stable placeholder (__WUP_AMT__) so the frontend JS can
     * substitute the user-entered value without needing to re-know each
     * payment method's URL grammar. Returns '' if no usable template exists
     * (e.g. method doesn't support amount-bearing URLs).
     */
    private static function paymentUrlTemplateFor(string $method, string $holder): string
    {
        if ($holder === '' || !class_exists(\WalletUpPro\Core\Onboarding\PaymentPageGenerator::class)) {
            return '';
        }
        try {
            $tpl = \WalletUpPro\Core\Onboarding\PaymentPageGenerator::buildPaymentUrl($method, $holder, '__WUP_AMT__');
        } catch (\Throwable $e) {
            return '';
        }
        if ($tpl === '' || strpos($tpl, '__WUP_AMT__') === false) {
            // Placeholder either didn't survive (URL-encoder mangled it) or
            // method has no amount-bearing URL — JS will show fallback notice.
            return '';
        }
        return $tpl;
    }

    private static function formatAmount(float $amount): string
    {
        if (fmod($amount, 1) === 0.0) {
            return (string) (int) $amount;
        }
        return rtrim(rtrim(number_format($amount, 2, '.', ''), '0'), '.');
    }

    private static function distBaseUrl(): string
    {
        if (defined('WALLET_UP_PRO_URL')) {
            return rtrim(WALLET_UP_PRO_URL, '/') . '/walup-react/dist/blocks/';
        }
        return plugins_url('walup-react/dist/blocks/', dirname(__DIR__, 2) . '/wallet-up.php');
    }

    private static function distBasePath(): string
    {
        if (defined('WALLET_UP_PRO_PATH')) {
            return rtrim(WALLET_UP_PRO_PATH, '/') . '/walup-react/dist/blocks/';
        }
        return dirname(__DIR__, 2) . '/walup-react/dist/blocks/';
    }

    private static function pluginVersion(): string
    {
        if (defined('WALLET_UP_PRO_VERSION')) {
            return WALLET_UP_PRO_VERSION;
        }
        return '5.6.0';
    }
}
