<?php

namespace SoftminalB2B\Storefront;

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

use SoftminalB2B\Registration\Applicant;
use SoftminalB2B\Services\Settings;
use SoftminalB2B\Services\TranslationStrings;
use SoftminalB2B\Vite\Vite;

class BulkOrderForm
{
    public const SHORTCODE = 'smb2b_bulk_order';

    private static bool $registered = false;
    private static bool $needsAssets = false;

    public static function init(): void
    {
        if (self::$registered) {
            return;
        }

        self::$registered = true;

        add_shortcode(self::SHORTCODE, [self::class, 'render']);
        add_action('init', [self::class, 'registerBlock']);

        // The shortcode/block sets a flag while rendering; assets are enqueued
        // in the footer only if the flag was set, so no storefront page loads
        // the bundle unless it actually shows the form.
        add_action('wp_enqueue_scripts', [self::class, 'registerAssets']);
        add_action('wp_footer', [self::class, 'enqueueIfUsed']);
    }

    public static function registerBlock(): void
    {
        if (!function_exists('register_block_type')) {
            return;
        }

        register_block_type('smb2b/bulk-order-form', [
            'title'           => __('B2B bulk order form', 'softminal-b2b-wholesale-for-woocommerce'),
            'category'        => 'woocommerce',
            'icon'            => 'editor-table',
            'description'     => __('A fast order form where buyers set quantities across many products at once.', 'softminal-b2b-wholesale-for-woocommerce'),
            'render_callback' => [self::class, 'render'],
            'supports'        => ['html' => false],
        ]);
    }

    public static function registerAssets(): void
    {
        Vite::configure([
            'devServerUrl' => apply_filters('smb2b_vite_dev_server_url', self::devServerUrl()),
            'pluginDir'    => SMB2B_DIR,
            'pluginUrl'    => SMB2B_URL,
            'version'      => SMB2B_VERSION,
        ]);
    }

    /**
     * Where `npm run dev` serves from, HTTPS-aware. Shared with the My Account
     * Purchase Lists page so both storefront bundles agree in dev.
     */
    public static function devServerUrl(): string
    {
        if (!is_ssl()) {
            return 'http://localhost:4001';
        }

        $host = wp_parse_url(home_url(), PHP_URL_HOST);

        return 'https://' . ($host ?: 'localhost') . ':4001';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public static function render($atts = []): string
    {
        $userId = get_current_user_id();

        // Bulk ordering is for buyers who can see their prices. Guests and
        // pending applicants are shown a prompt, not the form — and a guest
        // PREVIEW must see exactly what a guest sees.
        if (!is_user_logged_in() || ViewAsCustomer::isGuestPreview()) {
            return self::notice(
                __('Please log in to use the bulk order form.', 'softminal-b2b-wholesale-for-woocommerce'),
                function_exists('wc_get_page_permalink') ? wc_get_page_permalink('myaccount') : ''
            );
        }

        if (Applicant::isPending($userId)) {
            return self::notice(
                __('Your trade account is still being reviewed. You will be able to order here once it is approved.', 'softminal-b2b-wholesale-for-woocommerce'),
                ''
            );
        }

        self::$needsAssets = true;

        // Per-placement overrides from the block inspector / shortcode atts. An
        // empty value means "inherit the shop-wide setting". Toggle atts are the
        // tri-state '', 'yes', 'no'.
        $atts = shortcode_atts([
            'category'        => '',
            'intro'           => '',
            'density'         => '',
            'pager_align'     => '',
            'sticky_bar'      => '',
            'hide_oos'        => '',
            'per_page'        => '',
            'show_thumbnails' => '',
            'show_sku'        => '',
            'show_categories' => '',
            'show_savings'    => '',
            'enable_paste'    => '',
            'enable_lists'    => '',
            'accent'          => '',
        ], is_array($atts) ? $atts : [], self::SHORTCODE);

        // Client-side overrides (behaviour + content), merged over the globals.
        $overrides = [
            'display'  => [
                'thumbnails' => self::resolveBool($atts['show_thumbnails'], 'bulk_order_show_thumbnails'),
                'sku'        => self::resolveBool($atts['show_sku'], 'bulk_order_show_sku'),
                'categories' => self::resolveBool($atts['show_categories'], 'bulk_order_show_categories'),
                'savings'    => self::resolveBool($atts['show_savings'], 'bulk_order_show_savings'),
                'paste'      => self::resolveBool($atts['enable_paste'], 'bulk_order_enable_paste'),
                'lists'      => self::resolveBool($atts['enable_lists'], 'bulk_order_enable_lists'),
            ],
            'perPage'  => (int) $atts['per_page'] > 0 ? min(200, max(5, (int) $atts['per_page'])) : (int) Settings::get('bulk_order_page_size'),
            'hideOos'  => in_array($atts['hide_oos'], ['yes', 'no'], true) ? $atts['hide_oos'] === 'yes' : (Settings::get('bulk_order_hide_oos') === 'yes'),
        ];

        $category = sanitize_title((string) $atts['category']);
        if ($category !== '') {
            $overrides['category'] = $category;
        }
        if ((string) $atts['intro'] !== '') {
            $overrides['intro'] = wp_kses_post((string) $atts['intro']);
        }

        // Server-applied chrome: layout classes + a per-placement accent colour
        // (inline, so it overrides the global accent token for this instance).
        $rootClass = self::rootClass($atts['density'], $atts['sticky_bar'], $atts['pager_align']);
        $accent    = sanitize_hex_color((string) $atts['accent']);
        $style     = $accent ? ' style="--sb-pri:' . esc_attr($accent) . '"' : '';

        return '<div id="smb2b-bulk-order" class="' . esc_attr($rootClass) . '"' . $style
            . ' data-overrides="' . esc_attr(wp_json_encode($overrides)) . '"></div>';
    }

    /**
     * Resolve a tri-state override ('yes' | 'no' | '') against its shop-wide
     * setting: an explicit yes/no wins; anything else inherits the setting.
     */
    private static function resolveBool($value, string $settingKey): bool
    {
        if ($value === 'yes') {
            return true;
        }
        if ($value === 'no') {
            return false;
        }

        return Settings::get($settingKey) !== 'no';
    }

    /**
     * Root class for the mount, with whitelisted display modifiers (density,
     * inline vs sticky action bar, pagination alignment). Each accepts a
     * per-placement override; anything else inherits the shop-wide setting.
     * Whitelisted so nothing user-controlled ever reaches the class attribute.
     */
    private static function rootClass(string $density = '', string $sticky = '', string $pager = ''): string
    {
        $classes = ['smb2b-bulk'];

        $d = in_array($density, ['comfortable', 'compact'], true) ? $density : (string) Settings::get('bulk_order_density');
        if ($d === 'compact') {
            $classes[] = 'smb2b-bulk--compact';
        }

        $s = in_array($sticky, ['yes', 'no'], true) ? $sticky : (string) Settings::get('bulk_order_sticky_bar');
        if ($s === 'no') {
            $classes[] = 'smb2b-bulk--inline-bar';
        }

        $p = in_array($pager, ['left', 'center', 'right'], true) ? $pager : (string) Settings::get('bulk_order_pager_align');
        if (in_array($p, ['left', 'right'], true)) {
            $classes[] = 'smb2b-bulk--pager-' . $p;
        }

        return implode(' ', $classes);
    }

    /**
     * Enqueues the bundle in the footer, after render() has run, so it only
     * loads where the form is present.
     */
    public static function enqueueIfUsed(): void
    {
        if (!self::$needsAssets) {
            return;
        }

        $enqueued = Vite::enqueueAsset('smb2b-bulk-order', 'resources/storefront/bulk-order.js', [], true);

        if (!$enqueued) {
            return;
        }

        // Base stylesheet as a real, cacheable file (loaded only here).
        Vite::enqueueStyle('smb2b-bulk-order-base', 'resources/storefront/bulk-order/table.css');
        self::attachInlineStyles();

        Vite::localize('smb2b-bulk-order', 'smb2bBulkOrder', [
            'apiUrl'     => rest_url(SMB2B_API_NAMESPACE),
            'nonce'      => wp_create_nonce('wp_rest'),
            'currency'   => self::currencyFormat(),
            'categories' => self::productCategories(),
            'display'    => self::displayFlags(),
            // Merchant intro note above the table. Re-sanitised on output.
            'intro'      => wp_kses_post((string) Settings::get('bulk_order_intro', '')),
            // The buyer's current cart, so the top pill reflects what's already in it.
            'cart'       => self::cartSnapshot(),
            'i18n'       => TranslationStrings::bulkOrder(),
        ]);

    }

    public static function attachInlineStyles(): void
    {
        $css = self::customCss();
        if ($css !== '') {
            wp_add_inline_style('smb2b-bulk-order-base', $css);
        }
    }

    private static function customCss(): string
    {
        $css = '';

        // A stored hex wins; empty means "match the theme" — resolve the
        // merchant's own theme colour rather than defaulting to ours.
        $stored = (string) Settings::get('bulk_order_accent', '');
        $accent = $stored !== '' ? sanitize_hex_color($stored) : \SoftminalB2B\Support\ThemeColor::accent();
        if ($accent) {
            $css .= ".smb2b-bulk{--sb-pri:{$accent}}\n";
        }

        return $css;
    }


    /**
     * The buyer's current cart contents (count + ex-tax total), so the top pill
     * starts from what's already in the cart. Zeroed if the cart isn't available.
     *
     * @return array{count:int, total:float}
     */
    private static function cartSnapshot(): array
    {
        if (!function_exists('WC') || !WC()->cart) {
            return ['count' => 0, 'total' => 0.0, 'url' => ''];
        }

        return [
            'count' => (int) WC()->cart->get_cart_contents_count(),
            'total' => (float) WC()->cart->get_cart_contents_total(),
            'url'   => function_exists('wc_get_cart_url') ? wc_get_cart_url() : '',
        ];
    }

    /**
     * Admin display toggles for the table, as booleans the client v-ifs against.
     *
     * @return array<string, bool>
     */
    private static function displayFlags(): array
    {
        return [
            'thumbnails' => Settings::get('bulk_order_show_thumbnails', 'yes') !== 'no',
            'sku'        => Settings::get('bulk_order_show_sku', 'yes') !== 'no',
            'categories' => Settings::get('bulk_order_show_categories', 'yes') !== 'no',
            'savings'    => Settings::get('bulk_order_show_savings', 'yes') !== 'no',
            'paste'      => Settings::get('bulk_order_enable_paste', 'yes') !== 'no',
            'lists'      => Settings::get('bulk_order_enable_lists', 'yes') !== 'no',
        ];
    }

    /**
     * WooCommerce currency parts the client formatter needs. wc_price is not
     * available in JS, so the money() helper rebuilds the same format from these.
     *
     * @return array<string, mixed>
     */
    private static function currencyFormat(): array
    {
        return [
            'symbol'      => html_entity_decode(get_woocommerce_currency_symbol(), ENT_QUOTES, 'UTF-8'),
            'position'    => (string) get_option('woocommerce_currency_pos', 'left'),
            'decimals'    => wc_get_price_decimals(),
            'decimalSep'  => wc_get_price_decimal_separator(),
            'thousandSep' => wc_get_price_thousand_separator(),
        ];
    }

    /**
     * Non-empty product categories for the filter dropdown, as {slug, name}.
     *
     * @return array<int, array<string, string>>
     */
    public static function productCategories(): array
    {
        $terms = get_terms([
            'taxonomy'   => 'product_cat',
            'hide_empty' => true,
            'orderby'    => 'name',
        ]);

        if (is_wp_error($terms)) {
            return [];
        }

        $terms = array_filter($terms, static fn($term) => $term->slug !== 'uncategorized');

        return array_values(array_map(
            static fn($term) => ['slug' => $term->slug, 'name' => html_entity_decode($term->name, ENT_QUOTES, 'UTF-8')],
            $terms
        ));
    }

    private static function notice(string $message, string $url): string
    {
        if ($url !== '') {
            return sprintf(
                '<div class="smb2b-bulk__login"><a class="button" href="%s">%s</a></div>',
                esc_url($url),
                esc_html($message)
            );
        }

        return '<div class="smb2b-bulk__login">' . esc_html($message) . '</div>';
    }
}
