<?php
/**
 * Plugin Name: BrewmyApp — Mobile App Builder for WooCommerce
 * Description: Turn your WooCommerce store into a native mobile app. OTP login (Email & WhatsApp), in-app payments, push notifications, and seamless app-to-store integration — no coding required.
 * Version: 2.6.2
 * Tested up to: 7.0
 * Requires at least: 5.8
 * Requires PHP: 7.4
 * WC requires at least: 8.0
 * WC tested up to: 10.7.0
 * Requires Plugins: woocommerce
 * Author: BrewmyApp
 * Author URI: https://www.brewmyapp.io
 * License: GPL-2.0-or-later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: brewmyapp-mobile-app-builder-for-woocommerce
 * Domain Path: /languages
 */

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

define('BREWMYAPP_VERSION', '2.6.2');

// Declare HPOS compatibility for WooCommerce validation checks.
add_action('before_woocommerce_init', function () {
    if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) {
        \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true);
    }
});

// ──────────────────────────────────────────────
// AUTOLOAD (Firebase JWT)
// ──────────────────────────────────────────────
$brewmyapp_autoload = __DIR__ . '/vendor/autoload.php';
if (file_exists($brewmyapp_autoload)) {
    try {
        require_once $brewmyapp_autoload;
    } catch (\Throwable $e) {
        // Prevent hard activation failures when a packaged vendor build
        // was generated against an incompatible PHP platform.
        add_action('admin_notices', function () use ($e) {
            if (!current_user_can('activate_plugins')) {
                return;
            }

            printf(
                '<div class="notice notice-error"><p>%s</p></div>',
                esc_html(sprintf(
                    __('BrewmyApp dependency load failed: %s', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                    $e->getMessage()
                ))
            );
        });
    }
}

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

// ──────────────────────────────────────────────
// ADMIN LANGUAGE SWITCHER (plugin admin UI only)
// ──────────────────────────────────────────────

/**
 * Supported plugin-admin languages: WordPress locale => native label.
 * Mirrors the 11 languages offered on the brewmyapp.io dashboard.
 */
function brewmyapp_admin_locales()
{
    return array(
        'en_US' => 'English',
        'ar'    => 'العربية',
        'de_DE' => 'Deutsch',
        'es_ES' => 'Español',
        'fr_FR' => 'Français',
        'hi_IN' => 'हिन्दी',
        'it_IT' => 'Italiano',
        'ja'    => '日本語',
        'nl_NL' => 'Nederlands',
        'pt_BR' => 'Português',
        'zh_CN' => '中文',
    );
}

/**
 * The current admin's chosen plugin locale (per-user).
 * Empty string = follow the site/user WordPress locale.
 */
function brewmyapp_get_admin_locale()
{
    $loc = (string) get_user_meta(get_current_user_id(), 'brewmyapp_admin_locale', true);
    return array_key_exists($loc, brewmyapp_admin_locales()) ? $loc : '';
}

// Persist a language change (?brewmyapp_set_lang=xx + nonce), then redirect
// back to the same page without the query args.
add_action('admin_init', function () {
    if (!isset($_GET['brewmyapp_set_lang']) || !current_user_can('manage_options')) {
        return;
    }
    $nonce = isset($_GET['_bma_lang_nonce']) ? sanitize_text_field(wp_unslash($_GET['_bma_lang_nonce'])) : '';
    if (!wp_verify_nonce($nonce, 'brewmyapp_set_lang')) {
        return;
    }
    $loc = sanitize_text_field(wp_unslash($_GET['brewmyapp_set_lang']));
    if (array_key_exists($loc, brewmyapp_admin_locales())) {
        update_user_meta(get_current_user_id(), 'brewmyapp_admin_locale', $loc);
    }
    wp_safe_redirect(remove_query_arg(array('brewmyapp_set_lang', '_bma_lang_nonce')));
    exit;
});

// Apply the chosen locale to THIS plugin's textdomain in the admin only, so it
// never changes the rest of wp-admin. Registered before the textdomain loads.
add_filter('plugin_locale', function ($locale, $domain) {
    if ($domain === 'brewmyapp-mobile-app-builder-for-woocommerce' && is_admin()) {
        $chosen = brewmyapp_get_admin_locale();
        if ($chosen) {
            return $chosen;
        }
    }
    return $locale;
}, 10, 2);

// Load bundled translations from /languages.
add_action('init', function () {
    load_plugin_textdomain(
        'brewmyapp-mobile-app-builder-for-woocommerce',
        false,
        dirname(plugin_basename(__FILE__)) . '/languages'
    );
});

/**
 * Render the admin language <select> (used in the plugin page header).
 * Submits via GET; the admin_init handler above persists and redirects.
 */
function brewmyapp_render_language_switcher()
{
    $locales = brewmyapp_admin_locales();
    $current = brewmyapp_get_admin_locale();
    if ($current === '') {
        $wp = function_exists('determine_locale') ? determine_locale() : get_locale();
        $current = array_key_exists($wp, $locales) ? $wp : 'en_US';
    }

    $base = remove_query_arg(array('brewmyapp_set_lang', '_bma_lang_nonce'));

    echo '<form method="get" class="bma-lang-switch" style="margin:0;display:inline-flex;align-items:center;">';
    // Preserve existing query args (e.g. page=...) so GET doesn't drop them.
    $parts = wp_parse_url($base);
    if (!empty($parts['query'])) {
        parse_str($parts['query'], $qs);
        foreach ($qs as $k => $v) {
            echo '<input type="hidden" name="' . esc_attr($k) . '" value="' . esc_attr($v) . '" />';
        }
    }
    echo '<input type="hidden" name="_bma_lang_nonce" value="' . esc_attr(wp_create_nonce('brewmyapp_set_lang')) . '" />';
    echo '<label class="screen-reader-text" for="brewmyapp_set_lang">' . esc_html__('Language', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</label>';
    echo '<span class="dashicons dashicons-translation" style="font-size:16px;width:16px;height:16px;color:#3f3f46;margin-right:4px;"></span>';
    echo '<select name="brewmyapp_set_lang" id="brewmyapp_set_lang" onchange="this.form.submit()" style="font-size:13px;font-weight:600;color:#3f3f46;border:1px solid #e4e4e7;border-radius:8px;background:#fff;padding:7px 8px;cursor:pointer;">';
    foreach ($locales as $code => $label) {
        echo '<option value="' . esc_attr($code) . '"' . selected($code, $current, false) . '>' . esc_html($label) . '</option>';
    }
    echo '</select>';
    echo '</form>';
}

/**
 * Check whether JWT classes are available.
 *
 * @return bool
 */
function brewmyapp_jwt_available()
{
    return class_exists('Firebase\\JWT\\JWT') && class_exists('Firebase\\JWT\\Key');
}

// ──────────────────────────────────────────────
// CONSTANTS & JWT HELPERS
// ──────────────────────────────────────────────
if (!defined('BREWMYAPP_JWT_SECRET')) {
    $brewmyapp_salt = defined('AUTH_KEY') ? AUTH_KEY : (defined('SECURE_AUTH_KEY') ? SECURE_AUTH_KEY : null);
    if (!$brewmyapp_salt && function_exists('wp_salt')) {
        $brewmyapp_salt = wp_salt();
    }
    if (!$brewmyapp_salt) {
        $brewmyapp_salt = 'change-me-bma-jwt-secret-' . md5(__FILE__ . microtime(true));
    }
    define('BREWMYAPP_JWT_SECRET', $brewmyapp_salt);
}

if (!defined('BREWMYAPP_JWT_EXPIRY')) {
    define('BREWMYAPP_JWT_EXPIRY', DAY_IN_SECONDS * 7);
}

/**
 * Get the JWT secret in use.
 *
 * @return string
 */
function brewmyapp_get_jwt_secret()
{
    $saved = get_option('brewmyapp_jwt_secret');
    if (is_string($saved) && $saved !== '') {
        return $saved;
    }

    if (defined('BREWMYAPP_JWT_SECRET')) {
        return BREWMYAPP_JWT_SECRET;
    }

    if (defined('AUTH_KEY')) {
        return AUTH_KEY;
    }

    if (defined('SECURE_AUTH_KEY')) {
        return SECURE_AUTH_KEY;
    }

    $salt = function_exists('wp_salt') ? wp_salt() : null;
    return $salt ? $salt : 'change-me-bma-jwt-secret';
}

/**
 * Get the JWT token expiry in seconds.
 *
 * @return int
 */
function brewmyapp_get_jwt_expiry()
{
    $saved = (int) get_option('brewmyapp_jwt_expiry');
    if ($saved > 0) {
        return $saved;
    }

    if (defined('BREWMYAPP_JWT_EXPIRY')) {
        return (int) BREWMYAPP_JWT_EXPIRY;
    }

    return DAY_IN_SECONDS * 7;
}

// ──────────────────────────────────────────────
// OTP HELPERS (Email)
// ──────────────────────────────────────────────
function brewmyapp_otp_rate_key($email)
{
    $normalized = strtolower(trim((string) $email));
    return 'brewmyapp_otp_rate_' . hash('sha256', $normalized);
}

function brewmyapp_generate_numeric_otp($length = 6)
{
    $length = max(4, min(8, (int) $length));
    $min = (int) pow(10, $length - 1);
    $max = (int) pow(10, $length) - 1;
    return (string) wp_rand($min, $max);
}

function brewmyapp_otp_email_key($email)
{
    $normalized = strtolower(trim((string) $email));
    return 'brewmyapp_otp_' . hash('sha256', $normalized);
}

function brewmyapp_hash_otp($email, $otp)
{
    $email = strtolower(trim((string) $email));
    $otp = trim((string) $otp);
    return hash_hmac('sha256', $email . '|' . $otp, brewmyapp_get_jwt_secret());
}

// ──────────────────────────────────────────────
// PHONE / WHATSAPP HELPERS
// ──────────────────────────────────────────────
function brewmyapp_normalize_phone($phone)
{
    $phone = preg_replace('/[^0-9+]/', '', (string) $phone);
    if ($phone === '') {
        return '';
    }

    if ($phone[0] !== '+') {
        $phone = '+' . ltrim($phone, '+');
    }

    return $phone;
}

function brewmyapp_find_user_by_phone($phone)
{
    $phone = brewmyapp_normalize_phone($phone);
    if (!$phone) {
        return null;
    }

    $query = new WP_User_Query(array(
        'number' => 1,
        'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
            array(
                'key' => 'billing_phone',
                'value' => $phone,
                'compare' => '=',
            ),
        ),
    ));

    $users = $query->get_results();
    return $users ? $users[0] : null;
}

function brewmyapp_otp_phone_rate_key($phone)
{
    $normalized = brewmyapp_normalize_phone($phone);
    return 'brewmyapp_otp_phone_rate_' . hash('sha256', $normalized);
}

function brewmyapp_otp_phone_key($phone)
{
    $normalized = brewmyapp_normalize_phone($phone);
    return 'brewmyapp_otp_phone_' . hash('sha256', $normalized);
}

function brewmyapp_hash_otp_phone($phone, $otp)
{
    $phone = brewmyapp_normalize_phone($phone);
    $otp = trim((string) $otp);
    return hash_hmac('sha256', $phone . '|' . $otp, brewmyapp_get_jwt_secret());
}

// ──────────────────────────────────────────────
// API LOGGING (database-based)
// ──────────────────────────────────────────────

/**
 * Write an API log entry to the database.
 *
 * @param string   $route   The REST route.
 * @param int      $status  HTTP status code.
 * @param int|null $user_id WordPress user ID.
 * @param array    $extra   Optional extra data.
 */
function brewmyapp_log_api($route, $status, $user_id = null, $extra = array())
{
    $logs = get_option('brewmyapp_api_logs', array());
    if (!is_array($logs)) {
        $logs = array();
    }

    array_unshift($logs, array(
        'timestamp' => gmdate('c'),
        'route' => $route,
        'status' => (int) $status,
        'user_id' => $user_id,
        'extra' => $extra,
    ));

    // Keep only last 500 entries.
    $logs = array_slice($logs, 0, 500);
    update_option('brewmyapp_api_logs', $logs, false);
}

/**
 * Read all API log entries from the database.
 *
 * @return array
 */
function brewmyapp_get_api_logs()
{
    $logs = get_option('brewmyapp_api_logs', array());
    return is_array($logs) ? $logs : array();
}

/**
 * Filter logs to email OTP entries only.
 *
 * @param array $entries Log entries.
 * @return array
 */
function brewmyapp_filter_email_logs(array $entries)
{
    return array_values(array_filter($entries, function ($entry) {
        $route = $entry['route'] ?? '';
        if (!$route) {
            return false;
        }
        if (stripos($route, 'whatsapp') !== false) {
            return false;
        }
        return stripos($route, '/otp/') !== false;
    }));
}

/**
 * Filter logs to WhatsApp OTP sent entries only.
 *
 * @param array $entries Log entries.
 * @return array
 */
function brewmyapp_filter_whatsapp_logs(array $entries)
{
    return array_values(array_filter($entries, function ($entry) {
        $route = (string) ($entry['route'] ?? '');
        $status = (int) ($entry['status'] ?? 0);
        return stripos($route, '/whatsapp/otp/request') !== false && $status === 200;
    }));
}

// ──────────────────────────────────────────────
// PAYMENT TOKEN HELPERS
// ──────────────────────────────────────────────
function brewmyapp_generate_payment_token($order_id, $customer_id, $expiry = 3600)
{
    $expires_at = time() + $expiry;
    $payload = base64_encode("{$order_id}:{$customer_id}:{$expires_at}");
    $secret = brewmyapp_get_jwt_secret();
    $signature = hash_hmac('sha256', $payload, $secret);
    return $payload . '.' . $signature;
}

function brewmyapp_verify_payment_token($token)
{
    $parts = explode('.', $token);
    if (count($parts) !== 2) {
        return false;
    }

    $payload = $parts[0];
    $signature = $parts[1];
    $secret = brewmyapp_get_jwt_secret();
    $expected = hash_hmac('sha256', $payload, $secret);

    if (!hash_equals($expected, $signature)) {
        return false;
    }

    $decoded = base64_decode($payload);
    $data = explode(':', $decoded);
    if (count($data) !== 3) {
        return false;
    }

    $order_id = (int) $data[0];
    $customer_id = (int) $data[1];
    $expires_at = (int) $data[2];

    if (time() > $expires_at) {
        return false;
    }

    return array(
        'order_id' => $order_id,
        'customer_id' => $customer_id,
        'expires_at' => $expires_at,
    );
}

// ──────────────────────────────────────────────
// BREWMYAPP BACKEND — per-store secret + plugin API helper
// ──────────────────────────────────────────────
/**
 * Resolve the BrewmyApp backend base URL.
 */
function brewmyapp_get_backend_url()
{
    return defined('BREWMYAPP_BACKEND_URL') ? BREWMYAPP_BACKEND_URL : 'https://api.brewmyapp.io';
}

/**
 * Get (minting once) this store's per-store shared secret. Sent to the backend
 * in the connect nonce payload and used as X-BMA-Store-Secret on /api/plugin/wp/*
 * calls so the plugin can read its own subscription / preview data by domain.
 */
function brewmyapp_get_plugin_secret()
{
    $secret = get_option('brewmyapp_plugin_secret', '');
    if (!is_string($secret) || strlen($secret) < 32) {
        $secret = bin2hex(random_bytes(32));
        update_option('brewmyapp_plugin_secret', $secret, false);
    }
    return $secret;
}

/**
 * Call a BrewmyApp /api/plugin/wp/* endpoint with this store's secret.
 *
 * @param string $path           e.g. '/subscription' or '/preview'
 * @param int    $cache_seconds  transient cache TTL (0 = no cache)
 * @return array|WP_Error  Decoded JSON body (array) on 2xx, WP_Error otherwise.
 */
function brewmyapp_plugin_api($path, $cache_seconds = 120)
{
    $cache_key = 'bma_plugin_api_' . md5($path);
    if ($cache_seconds > 0) {
        $cached = get_transient($cache_key);
        if (is_array($cached)) {
            return $cached;
        }
    }

    $response = wp_remote_post(
        brewmyapp_get_backend_url() . '/api/plugin/wp' . $path,
        array(
            'method' => 'POST',
            'timeout' => 12,
            'blocking' => true,
            'sslverify' => true,
            'headers' => array(
                'Content-Type' => 'application/json',
                'X-BMA-Store-Secret' => brewmyapp_get_plugin_secret(),
            ),
            'body' => wp_json_encode(array(
                'domain' => rtrim(home_url('/'), '/'),
            )),
        )
    );

    if (is_wp_error($response)) {
        return $response;
    }

    $code = (int) wp_remote_retrieve_response_code($response);
    $data = json_decode(wp_remote_retrieve_body($response), true);
    if ($code < 200 || $code >= 300 || !is_array($data)) {
        return new WP_Error('brewmyapp_plugin_api', 'Request failed', array('status' => $code, 'data' => $data));
    }

    if ($cache_seconds > 0) {
        set_transient($cache_key, $data, $cache_seconds);
    }
    return $data;
}

/**
 * Is this store connected to a BrewmyApp account?
 *
 * Fast path: the local `brewmyapp_connected_at` flag (set when the backend
 * verifies a connect nonce). Auto-detect path: if the flag is missing but we
 * have a minted plugin secret, ask the backend (/api/plugin/wp/status by
 * domain + secret). This lets us "bypass" the connect step when the store is
 * already attached — e.g. connected from another device, or the local flag was
 * lost — and self-heal the flag. Result is cached briefly to avoid hammering.
 *
 * @param bool $force Re-check the backend even if cached/flagged (Refresh button).
 */
function brewmyapp_is_connected($force = false)
{
    if (!$force && (int) get_option('brewmyapp_connected_at', 0) > 0) {
        return true;
    }

    $secret = get_option('brewmyapp_plugin_secret', '');
    if (!is_string($secret) || strlen($secret) < 32) {
        // No secret yet → never connected from this install.
        return (int) get_option('brewmyapp_connected_at', 0) > 0;
    }

    $cache_key = 'bma_conn_status';
    if ($force) {
        delete_transient($cache_key);
    } else {
        $cached = get_transient($cache_key);
        if ($cached === 'yes') {
            return true;
        }
        if ($cached === 'no') {
            return false;
        }
    }

    $status = brewmyapp_plugin_api('/status', 0);
    if (!is_wp_error($status) && !empty($status['connected'])) {
        if ((int) get_option('brewmyapp_connected_at', 0) <= 0) {
            update_option('brewmyapp_connected_at', time(), false);
        }
        // Remember the address we're connected under, so a later change is
        // detectable locally (see brewmyapp_detect_domain_change).
        update_option('brewmyapp_site_url', home_url('/'), false);
        set_transient($cache_key, 'yes', 300);
        return true;
    }

    set_transient($cache_key, 'no', $force ? 5 : 60);
    return (int) get_option('brewmyapp_connected_at', 0) > 0;
}

/**
 * Detect a WordPress site-address change on a connected store.
 *
 * The store's identity on BrewmyApp is its per-store secret (stable, in
 * wp_options), NOT its domain — but the domain is what every request carries.
 * When a merchant changes Settings → Site Address (or migrates the site), the
 * plugin keeps the same secret but now reports a new domain. We remember the
 * address we connected under (`brewmyapp_site_url`); comparing it to the live
 * `home_url()` detects the change LOCALLY — no dependence on the backend's
 * fast-path, which would otherwise never notice.
 *
 * @return string|false The new address if it changed, else false.
 */
function brewmyapp_detect_domain_change()
{
    // Only meaningful for a store that has actually connected and holds a secret.
    if ((int) get_option('brewmyapp_connected_at', 0) <= 0) {
        return false;
    }
    $secret = get_option('brewmyapp_plugin_secret', '');
    if (!is_string($secret) || strlen($secret) < 32) {
        return false;
    }
    $stored = (string) get_option('brewmyapp_site_url', '');
    if ($stored === '') {
        // Connected before this field existed — there's nothing to compare against
        // yet. Backfill it with the current address so the NEXT change is
        // detectable, instead of leaving these installs permanently blind.
        update_option('brewmyapp_site_url', home_url('/'), false);
        return false;
    }
    $current = home_url('/');
    if (untrailingslashit($stored) === untrailingslashit($current)) {
        return false; // unchanged
    }
    return $current;
}

/**
 * Tell the backend immediately when the site address actually changes.
 *
 * Without this, a change is only noticed the next time an administrator happens
 * to load a wp-admin page (see the admin_notices handler below) — on a site
 * nobody logs into, the backend would never learn the new domain and the app
 * would keep pointing at the old one. Hooking the option write closes that gap:
 * one uncached /status POST carries { domain: <new> } + the stable secret, so
 * resolveWooStoreBySecret records pendingDomain right away.
 *
 * Fired for both home and siteurl; the throttle transient shared with the notice
 * keeps a change to both from double-posting.
 */
function brewmyapp_on_site_address_changed($old_value, $new_value)
{
    if (untrailingslashit((string) $old_value) === untrailingslashit((string) $new_value)) {
        return;
    }
    if ((int) get_option('brewmyapp_connected_at', 0) <= 0) {
        return; // never connected — nothing to reconcile
    }
    // Bust caches keyed to the old address before we re-announce.
    delete_transient('bma_conn_status');
    if (get_transient('bma_domain_change_pinged') !== 'yes') {
        brewmyapp_plugin_api('/status', 0);
        set_transient('bma_domain_change_pinged', 'yes', 300);
    }
}
add_action('update_option_home', 'brewmyapp_on_site_address_changed', 10, 2);
add_action('update_option_siteurl', 'brewmyapp_on_site_address_changed', 10, 2);

/**
 * Deep-link to the store's own dashboard page, where the confirm-domain-change
 * banner actually renders. The plugin doesn't persist a store id, so read it
 * from the cached /preview response; fall back to the store list if unavailable.
 */
function brewmyapp_dashboard_store_url()
{
    $base = rtrim(brewmyapp_get_frontend_url(), '/');
    $preview = brewmyapp_plugin_api('/preview', 300);
    if (!is_wp_error($preview) && !empty($preview['storeId'])) {
        return $base . '/dashboard/stores/' . rawurlencode($preview['storeId']);
    }
    return $base . '/dashboard/stores';
}

/**
 * Admin notice: prompt the merchant to confirm a detected site-address change in
 * the BrewmyApp dashboard. Also nudges the backend to record the pending change
 * by forcing one /status call from the new address.
 */
add_action('admin_notices', function () {
    if (!current_user_can('manage_options')) {
        return;
    }
    $new_domain = brewmyapp_detect_domain_change();
    if ($new_domain === false) {
        return;
    }

    // Tell the backend once (a no-cache /status posts { domain: new } + secret, so
    // resolveWooStoreBySecret records pendingDomain). Throttled so we don't call
    // it on every admin page load. `0` = fresh call, matching brewmyapp_is_connected.
    if (get_transient('bma_domain_change_pinged') !== 'yes') {
        brewmyapp_plugin_api('/status', 0);
        set_transient('bma_domain_change_pinged', 'yes', 300);
    }

    printf(
        '<div class="notice notice-warning"><p><strong>%s</strong><br>%s<br><a href="%s" target="_blank" rel="noopener" class="button button-primary" style="margin-top:8px;">%s</a></p></div>',
        esc_html__('BrewmyApp: your store address changed', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        esc_html(
            sprintf(
                /* translators: %s: new site address */
                __('Your site now loads at %s. Confirm the move in your BrewmyApp dashboard so your mobile app follows to the new address.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                $new_domain
            )
        ),
        esc_url(brewmyapp_dashboard_store_url()),
        esc_html__('Confirm in dashboard', 'brewmyapp-mobile-app-builder-for-woocommerce')
    );
});

// ──────────────────────────────────────────────
// EMAIL OTP SEND HELPER — uses BrewmyApp backend (Brevo)
// ──────────────────────────────────────────────
/**
 * Send an OTP email via the BrewmyApp backend (Brevo).
 * Returns true on success, false on failure.
 */
function brewmyapp_send_otp_email($toEmail, $otp)
{
    $backend_url = defined('BREWMYAPP_BACKEND_URL') ? BREWMYAPP_BACKEND_URL : 'https://api.brewmyapp.io';
    $site_name = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
    $site_url = rtrim(home_url('/'), '/');

    $response = wp_remote_post(
        $backend_url . '/api/auth/email/send-otp-wp',
        array(
            'method' => 'POST',
            'timeout' => 15,
            'blocking' => true,
            'sslverify' => true,
            'headers' => array('Content-Type' => 'application/json'),
            'body' => wp_json_encode(array(
                'email' => $toEmail,
                'otp' => $otp,
                'storeName' => $site_name,
                'siteUrl' => $site_url,
            )),
        )
    );

    if (is_wp_error($response)) {
        return false;
    }

    return (int) wp_remote_retrieve_response_code($response) === 200;
}

// ──────────────────────────────────────────────
// WHATSAPP SEND HELPER
// ──────────────────────────────────────────────
function brewmyapp_send_whatsapp_otp($toPhone, $otp)
{
    $token = defined('BREWMYAPP_WA_TOKEN') ? BREWMYAPP_WA_TOKEN : get_option('brewmyapp_wa_token');
    $template_name = defined('BREWMYAPP_WA_OTP_TEMPLATE') ? BREWMYAPP_WA_OTP_TEMPLATE : get_option('brewmyapp_wa_otp_template');
    $template_lang = defined('BREWMYAPP_WA_TEMPLATE_LANG') ? BREWMYAPP_WA_TEMPLATE_LANG : (get_option('brewmyapp_wa_template_lang') ? get_option('brewmyapp_wa_template_lang') : 'en_US');
    $whatsapp_url = defined('BREWMYAPP_WA_SEND_URL') ? BREWMYAPP_WA_SEND_URL : get_option('brewmyapp_wa_send_url');

    $toPhone = brewmyapp_normalize_phone($toPhone);
    if (empty($token)) {
        return new WP_Error(
            'brewmyapp_whatsapp_not_configured',
            __('WhatsApp API not configured.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 500)
        );
    }

    if (!empty($template_name)) {
        $body = array(
            'messaging_product' => 'whatsapp',
            'to' => $toPhone,
            'type' => 'template',
            'template' => array(
                'name' => $template_name,
                'language' => array('code' => $template_lang),
                'components' => array(
                    array(
                        'type' => 'body',
                        'parameters' => array(array('type' => 'text', 'text' => (string) $otp)),
                    ),
                    array(
                        'type' => 'button',
                        'sub_type' => 'url',
                        'index' => '0',
                        'parameters' => array(array('type' => 'text', 'text' => (string) $otp)),
                    ),
                ),
            ),
        );
    } else {
        $body = array(
            'messaging_product' => 'whatsapp',
            'to' => $toPhone,
            'type' => 'text',
            /* translators: %s: the OTP code */
            'text' => array('body' => sprintf(__('Your OTP is: %s (valid for 10 minutes).', 'brewmyapp-mobile-app-builder-for-woocommerce'), (string) $otp)),
        );
    }

    $res = wp_remote_post($whatsapp_url, array(
        'headers' => array(
            'Authorization' => 'Bearer ' . $token,
            'Content-Type' => 'application/json',
        ),
        'timeout' => 20,
        'body' => wp_json_encode($body),
    ));

    if (is_wp_error($res)) {
        return $res;
    }

    $code = (int) wp_remote_retrieve_response_code($res);
    $raw = (string) wp_remote_retrieve_body($res);

    if ($code < 200 || $code >= 300) {
        return new WP_Error('brewmyapp_whatsapp_send_failed', __('WhatsApp send failed.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array(
            'status' => 502,
            'code' => $code,
            'body' => substr($raw, 0, 1000),
        ));
    }

    return true;
}

// ══════════════════════════════════════════════
// JWT VERIFICATION HELPER
// ══════════════════════════════════════════════

/**
 * Verify a JWT Bearer token from the Authorization header.
 * Returns the WP user_id on success, or WP_Error on failure.
 *
 * @param WP_REST_Request $request The REST request.
 * @return int|WP_Error
 */
function brewmyapp_verify_jwt_from_request(WP_REST_Request $request)
{
    if (!brewmyapp_jwt_available()) {
        return new WP_Error(
            'brewmyapp_jwt_unavailable',
            __('JWT dependencies are not available on this server.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 500)
        );
    }

    $auth = $request->get_header('Authorization');
    if (!$auth || stripos($auth, 'Bearer ') !== 0) {
        return new WP_Error(
            'brewmyapp_missing_token',
            __('Authorization header with Bearer token required.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 401)
        );
    }

    $token = trim(substr($auth, 7));
    if (!$token) {
        return new WP_Error(
            'brewmyapp_empty_token',
            __('Token is empty.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 401)
        );
    }

    try {
        $decoded = JWT::decode($token, new Key(brewmyapp_get_jwt_secret(), 'HS256'));
        $user_id = $decoded->data->user_id ?? null;
        if (!$user_id) {
            return new WP_Error(
                'brewmyapp_invalid_token',
                __('Token payload missing user_id.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                array('status' => 401)
            );
        }
        $user = get_user_by('ID', $user_id);
        if (!$user) {
            return new WP_Error(
                'brewmyapp_user_not_found',
                __('User not found.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                array('status' => 401)
            );
        }
        return (int) $user_id;
    } catch (\Exception $e) {
        return new WP_Error(
            'brewmyapp_jwt_error',
            /* translators: %s: error message from JWT library */
            sprintf(__('Invalid or expired token: %s', 'brewmyapp-mobile-app-builder-for-woocommerce'), $e->getMessage()),
            array('status' => 401)
        );
    }
}

// ══════════════════════════════════════════════
// PERMISSION CALLBACK HELPERS
// ══════════════════════════════════════════════

/**
 * Permission callback: require valid JWT.
 * Stores the authenticated user_id in the request for callback use.
 *
 * @param WP_REST_Request $request The REST request.
 * @return true|WP_Error
 */
function brewmyapp_permission_jwt(WP_REST_Request $request)
{
    $user_id = brewmyapp_verify_jwt_from_request($request);
    if (is_wp_error($user_id)) {
        // nosemgrep: scanner.php.wp.security.rest-route.permission-callback.incorrect-return
        return $user_id;
    }
    $request->set_param('_brewmyapp_user_id', (int) $user_id);
    return true;
}

/**
 * Permission callback: require valid JWT + manage_options capability.
 *
 * @param WP_REST_Request $request The REST request.
 * @return true|WP_Error
 */
function brewmyapp_permission_admin(WP_REST_Request $request)
{
    $user_id = brewmyapp_verify_jwt_from_request($request);
    if (is_wp_error($user_id)) {
        // nosemgrep: scanner.php.wp.security.rest-route.permission-callback.incorrect-return
        return $user_id;
    }
    if (!user_can($user_id, 'manage_options')) {
        return new WP_Error(
            'brewmyapp_forbidden',
            __('Administrator access required.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 403)
        );
    }
    $request->set_param('_brewmyapp_user_id', (int) $user_id);
    return true;
}

/**
 * Permission callback: require valid JWT + manage_woocommerce capability.
 *
 * @param WP_REST_Request $request The REST request.
 * @return true|WP_Error
 */
function brewmyapp_permission_woo_admin(WP_REST_Request $request)
{
    $user_id = brewmyapp_verify_jwt_from_request($request);
    if (is_wp_error($user_id)) {
        // nosemgrep: scanner.php.wp.security.rest-route.permission-callback.incorrect-return
        return $user_id;
    }
    if (!user_can($user_id, 'manage_woocommerce')) {
        return new WP_Error(
            'brewmyapp_forbidden',
            __('Admin access required.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 403)
        );
    }
    $request->set_param('_brewmyapp_user_id', (int) $user_id);
    return true;
}

/**
 * Permission callback: accepts JWT (like brewmyapp_permission_woo_admin) OR
 * WooCommerce consumer key/secret via Basic auth.
 * Used for server-to-server calls from the BrewmyApp backend.
 *
 * @param WP_REST_Request $request The REST request.
 * @return true|WP_Error
 */
function brewmyapp_permission_woo_admin_or_basic(WP_REST_Request $request)
{
    // 1) Try JWT first
    $user_id = brewmyapp_verify_jwt_from_request($request);
    if (!is_wp_error($user_id)) {
        if (user_can($user_id, 'manage_woocommerce')) {
            $request->set_param('_brewmyapp_user_id', (int) $user_id);
            return true;
        }
        return new WP_Error(
            'brewmyapp_forbidden',
            __('Admin access required.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 403)
        );
    }

    // 2) Fall back to WooCommerce consumer key/secret via Basic auth
    $auth_header = $request->get_header('Authorization');
    if (!$auth_header || stripos($auth_header, 'Basic ') !== 0) {
        return new WP_Error(
            'brewmyapp_unauthorized',
            __('Authentication required (JWT or Basic).', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 401)
        );
    }

    $decoded = base64_decode(substr($auth_header, 6));
    if (!$decoded || strpos($decoded, ':') === false) {
        return new WP_Error(
            'brewmyapp_unauthorized',
            __('Invalid Basic auth credentials.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 401)
        );
    }

    list($consumer_key, $consumer_secret) = explode(':', $decoded, 2);

    if (!function_exists('wc_api_hash')) {
        return new WP_Error(
            'brewmyapp_error',
            __('WooCommerce is not active.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 500)
        );
    }

    global $wpdb;
    $key_data = $wpdb->get_row(
        $wpdb->prepare(
            "SELECT key_id, user_id, consumer_secret, permissions FROM {$wpdb->prefix}woocommerce_api_keys WHERE consumer_key = %s",
            wc_api_hash($consumer_key)
        )
    ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery

    if (!$key_data || !hash_equals($key_data->consumer_secret, $consumer_secret)) {
        return new WP_Error(
            'brewmyapp_unauthorized',
            __('Invalid WooCommerce API credentials.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 401)
        );
    }

    if (!user_can($key_data->user_id, 'manage_woocommerce')) {
        return new WP_Error(
            'brewmyapp_forbidden',
            __('Admin access required.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 403)
        );
    }

    $request->set_param('_brewmyapp_user_id', (int) $key_data->user_id);
    return true;
}

/**
 * Permission callback: accepts JWT (admin) OR WooCommerce consumer
 * key/secret via Basic auth bound to an admin user.
 *
 * Used by push-notification endpoints called by the BrewmyApp dashboard
 * via the backend (server-to-server), where no merchant JWT is available.
 *
 * @param WP_REST_Request $request The REST request.
 * @return true|WP_Error
 */
function brewmyapp_permission_admin_or_basic(WP_REST_Request $request)
{
    // 1) Prefer JWT (matches in-WP admin actions)
    $user_id = brewmyapp_verify_jwt_from_request($request);
    if (!is_wp_error($user_id)) {
        if (user_can($user_id, 'manage_options')) {
            $request->set_param('_brewmyapp_user_id', (int) $user_id);
            return true;
        }
        return new WP_Error(
            'brewmyapp_forbidden',
            __('Administrator access required.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 403)
        );
    }

    // 2) Fall back to WooCommerce consumer key/secret via Basic auth, but
    //    require the underlying WP user to have `manage_options` (admin)
    //    so we do not let a non-admin WC API key drive admin endpoints.
    $auth_header = $request->get_header('Authorization');
    if (!$auth_header || stripos($auth_header, 'Basic ') !== 0) {
        return new WP_Error(
            'brewmyapp_unauthorized',
            __('Authentication required (JWT or Basic).', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 401)
        );
    }

    $decoded = base64_decode(substr($auth_header, 6));
    if (!$decoded || strpos($decoded, ':') === false) {
        return new WP_Error(
            'brewmyapp_unauthorized',
            __('Invalid Basic auth credentials.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 401)
        );
    }

    list($consumer_key, $consumer_secret) = explode(':', $decoded, 2);

    if (!function_exists('wc_api_hash')) {
        return new WP_Error(
            'brewmyapp_error',
            __('WooCommerce is not active.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 500)
        );
    }

    global $wpdb;
    $key_data = $wpdb->get_row(
        $wpdb->prepare(
            "SELECT key_id, user_id, consumer_secret, permissions FROM {$wpdb->prefix}woocommerce_api_keys WHERE consumer_key = %s",
            wc_api_hash($consumer_key)
        )
    ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery

    if (!$key_data || !hash_equals($key_data->consumer_secret, $consumer_secret)) {
        return new WP_Error(
            'brewmyapp_unauthorized',
            __('Invalid WooCommerce API credentials.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 401)
        );
    }

    if (!user_can($key_data->user_id, 'manage_options')) {
        return new WP_Error(
            'brewmyapp_forbidden',
            __('Administrator access required.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('status' => 403)
        );
    }

    $request->set_param('_brewmyapp_user_id', (int) $key_data->user_id);
    return true;
}

// ══════════════════════════════════════════════
// EXPO PUSH NOTIFICATION SENDER
// ══════════════════════════════════════════════

/**
 * Send push notifications via the Expo Push API.
 * Chunks messages in batches of 100 (Expo's limit).
 *
 * @param array  $push_tokens Array of Expo push token strings.
 * @param string $title       Notification title.
 * @param string $body        Notification body.
 * @param array  $data        Optional data payload for deep linking.
 * @return array  { sent: int, failed: int, errors: array }
 */
function brewmyapp_send_expo_push(array $push_tokens, $title, $body, $data = array())
{
    if (empty($push_tokens)) {
        return array('sent' => 0, 'failed' => 0, 'errors' => array());
    }

    $push_tokens = array_values(array_unique(array_filter($push_tokens)));

    $message_template = array(
        'title' => $title,
        'body' => $body,
        'data' => !empty($data) ? $data : new \stdClass(),
        'sound' => 'default',
        'channelId' => 'default',
    );

    // If the caller attached a banner image (data.imageUrl), expose it via
    // richContent.image so iOS shows it as an attachment and Android renders
    // a big-picture style notification. mutableContent lets the iOS
    // notification service extension download the attachment.
    if (is_array($data) && !empty($data['imageUrl']) && is_string($data['imageUrl'])) {
        $message_template['richContent'] = array('image' => $data['imageUrl']);
        $message_template['mutableContent'] = true;
    }

    $sent = 0;
    $failed = 0;
    $errors = array();

    // Send one token at a time to avoid PUSH_TOO_MANY_EXPERIENCE_IDS errors
    // when tokens from different Expo projects/builds are mixed together.
    foreach ($push_tokens as $token) {
        $message = array_merge($message_template, array('to' => $token));

        $response = wp_remote_post('https://exp.host/--/api/v2/push/send', array(
            'headers' => array(
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ),
            'body' => wp_json_encode(array($message)),
            'timeout' => 30,
        ));

        if (is_wp_error($response)) {
            $failed++;
            $errors[] = $response->get_error_message();
            continue;
        }

        $resp_body = wp_remote_retrieve_body($response);
        $resp_data = json_decode($resp_body, true);

        if (isset($resp_data['data']) && is_array($resp_data['data'])) {
            $ticket = $resp_data['data'][0] ?? null;
            if ($ticket && isset($ticket['status']) && $ticket['status'] === 'ok') {
                $sent++;
            } else {
                $failed++;
                $error_msg = $ticket['message'] ?? __('Unknown error', 'brewmyapp-mobile-app-builder-for-woocommerce');
                $errors[] = $error_msg;
                // Auto-remove tokens that are no longer registered
                if (isset($ticket['details']['error']) && $ticket['details']['error'] === 'DeviceNotRegistered') {
                    brewmyapp_remove_push_token($token);
                }
            }
        } elseif (isset($resp_data['errors']) && is_array($resp_data['errors'])) {
            // Handle top-level Expo errors (e.g. invalid token format)
            $failed++;
            $errors[] = $resp_data['errors'][0]['message'] ?? __('Expo API error.', 'brewmyapp-mobile-app-builder-for-woocommerce');
        } else {
            $failed++;
            $errors[] = __('Invalid response from Expo.', 'brewmyapp-mobile-app-builder-for-woocommerce');
        }
    }

    return array('sent' => $sent, 'failed' => $failed, 'errors' => $errors);
}

/**
 * Remove a specific push token from all users.
 *
 * @param string $token_to_remove The Expo push token to remove.
 */
function brewmyapp_remove_push_token($token_to_remove)
{
    $users = get_users(array('meta_key' => 'brewmyapp_expo_push_tokens')); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
    foreach ($users as $user) {
        $tokens_data = get_user_meta($user->ID, 'brewmyapp_expo_push_tokens', true);
        if (!is_array($tokens_data)) {
            continue;
        }
        $filtered = array_values(array_filter($tokens_data, function ($entry) use ($token_to_remove) {
            return ($entry['token'] ?? '') !== $token_to_remove;
        }));
        if (count($filtered) !== count($tokens_data)) {
            update_user_meta($user->ID, 'brewmyapp_expo_push_tokens', $filtered);
        }
    }
}

/**
 * Get all Expo push tokens for a given user.
 *
 * @param int $user_id WordPress user ID.
 * @return array Array of token strings.
 */
/**
 * Close one back-in-stock subscription, keeping the demand signal.
 *
 * The waiting list (`brewmyapp_back_in_stock_subs`) used to simply lose the
 * product id once the alert went out, which made it a queue: it could answer
 * "who is waiting right now" and nothing else. The merchant-facing question is
 * the opposite one — "who ever wanted this?" — so the id moves to a second meta
 * key, `brewmyapp_back_in_stock_notified`, a map of product_id => unix time.
 *
 * The waiting list keeps its original shape (a flat array of ints) so an older
 * BrewmyApp backend, or a site that downgrades the plugin, still reads it.
 *
 * @param int   $user_id
 * @param int   $product_id
 * @param array $subs       current waiting list, already loaded by the caller
 */
function brewmyapp_close_back_in_stock_sub($user_id, $product_id, $subs)
{
    $subs = array_values(array_diff((array) $subs, array((int) $product_id)));
    update_user_meta($user_id, 'brewmyapp_back_in_stock_subs', $subs);

    $notified = get_user_meta($user_id, 'brewmyapp_back_in_stock_notified', true);
    if (!is_array($notified)) {
        $notified = array();
    }
    $notified[(int) $product_id] = time();
    update_user_meta($user_id, 'brewmyapp_back_in_stock_notified', $notified);
}

/**
 * Every user who has ever tapped "Notify Me", as
 * [ user_id => array('waiting' => int[], 'notified' => array(id => ts)) ].
 *
 * One get_users() pass with both meta keys, because the send handler's
 * "load every user with the waiting key, then filter in PHP" shape is the thing
 * that does not scale, and this endpoint would repeat it per product.
 */
function brewmyapp_collect_back_in_stock_subs()
{
    $users = get_users(array(
        'fields'     => array('ID', 'display_name', 'user_email'),
        'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
            'relation' => 'OR',
            array('key' => 'brewmyapp_back_in_stock_subs', 'compare' => 'EXISTS'),
            array('key' => 'brewmyapp_back_in_stock_notified', 'compare' => 'EXISTS'),
        ),
    ));

    $out = array();
    foreach ($users as $u) {
        $waiting = get_user_meta($u->ID, 'brewmyapp_back_in_stock_subs', true);
        $notified = get_user_meta($u->ID, 'brewmyapp_back_in_stock_notified', true);
        if (!is_array($waiting)) {
            $waiting = array();
        }
        if (!is_array($notified)) {
            $notified = array();
        }
        if (empty($waiting) && empty($notified)) {
            continue;
        }
        $out[(int) $u->ID] = array(
            'id'       => (int) $u->ID,
            'name'     => $u->display_name,
            'email'    => $u->user_email,
            'waiting'  => array_map('intval', $waiting),
            'notified' => $notified,
        );
    }
    return $out;
}

function brewmyapp_get_user_push_tokens($user_id)
{
    $tokens_data = get_user_meta($user_id, 'brewmyapp_expo_push_tokens', true);
    if (!is_array($tokens_data)) {
        return array();
    }
    return array_column($tokens_data, 'token');
}

// ══════════════════════════════════════════════
// WC REST API — Render shortcodes & allow video/audio in descriptions
// ══════════════════════════════════════════════

/**
 * The WooCommerce REST API processes descriptions through the_content filter
 * which converts [embed] / [video] shortcodes to <video> tags, but then
 * wp_kses_post strips those tags. This filter re-renders from the raw
 * description so the mobile app receives proper <video>/<audio>/<iframe> HTML.
 */
add_filter('woocommerce_rest_prepare_product_object', 'brewmyapp_render_description_shortcodes', 10, 3);
function brewmyapp_render_description_shortcodes($response, $product, $request)
{
    // Only modify descriptions for requests coming through our app proxy.
    // Other REST clients (POS, Zapier, other plugins) should get standard WC output.
    $route = $request->get_route();
    $ua = $request->get_header('user_agent');
    $is_bma = strpos($route, '/bma/v1/') === 0
        || ($ua && strpos($ua, 'BrewmyApp') !== false)
        || $request->get_header('X-BMA-App');
    if (!$is_bma) {
        return $response;
    }

    $data = $response->get_data();

    // Always re-render from raw description to recover stripped shortcodes
    $raw_desc = $product->get_description();
    $raw_short_desc = $product->get_short_description();

    if (!empty($raw_desc)) {
        $data['description'] = brewmyapp_render_media_shortcodes($raw_desc);
    }
    if (!empty($raw_short_desc)) {
        $data['short_description'] = brewmyapp_render_media_shortcodes($raw_short_desc);
    }

    $response->set_data($data);
    return $response;
}

/**
 * Convert [embed], [video], and other shortcodes to proper HTML.
 *
 * WordPress's [embed] shortcode relies on WP_Embed which may fail or get
 * stripped by wp_kses_post in REST context. We manually convert [embed]
 * shortcodes containing video file URLs to <video> tags, then run
 * do_shortcode() for everything else ([video], [audio], etc.).
 */
function brewmyapp_render_media_shortcodes($content)
{
    // Video file extensions to recognise inside [embed] shortcodes
    $video_ext = 'mp4|m4v|webm|ogv|flv|mov|wmv';

    // Convert [embed]<video-url>[/embed] → <video> tag
    $content = preg_replace_callback(
        '#\[embed\](https?://[^\s\[\]]+\.(?:' . $video_ext . ')(?:\?[^\s\[\]]*)?)\[/embed\]#i',
        function ($m) {
            $url = esc_url($m[1]);
            return '<video controls preload="metadata" src="' . $url . '" style="width:100%;max-width:100%;"></video>';
        },
        $content
    );

    // Convert bare [embed]<audio-url>[/embed] → <audio> tag
    $audio_ext = 'mp3|ogg|wav|flac|aac|wma|m4a';
    $content = preg_replace_callback(
        '#\[embed\](https?://[^\s\[\]]+\.(?:' . $audio_ext . ')(?:\?[^\s\[\]]*)?)\[/embed\]#i',
        function ($m) {
            $url = esc_url($m[1]);
            return '<audio controls preload="metadata" src="' . $url . '"></audio>';
        },
        $content
    );

    // For any remaining [embed] (YouTube, Vimeo, etc.) try WordPress oEmbed
    if (false !== strpos($content, '[embed]')) {
        global $wp_embed;
        if ($wp_embed) {
            $content = $wp_embed->run_shortcode($content);
        }
    }

    // Process remaining shortcodes ([video], [audio], etc.)
    $content = do_shortcode($content);

    return $content;
}

/**
 * Allow <video>, <audio>, <source>, and <iframe> tags through wp_kses_post
 * when serving WooCommerce REST API responses.
 */
add_filter('wp_kses_allowed_html', 'brewmyapp_allow_media_tags_in_rest', 10, 2);
function brewmyapp_allow_media_tags_in_rest($tags, $context)
{
    if ('post' !== $context) {
        return $tags;
    }
    // Only extend during REST API requests
    if (!defined('REST_REQUEST') || !REST_REQUEST) {
        return $tags;
    }

    $tags['video'] = array(
        'src' => true,
        'width' => true,
        'height' => true,
        'controls' => true,
        'autoplay' => true,
        'loop' => true,
        'muted' => true,
        'preload' => true,
        'poster' => true,
        'class' => true,
        'id' => true,
        'style' => true,
    );
    $tags['audio'] = array(
        'src' => true,
        'controls' => true,
        'autoplay' => true,
        'loop' => true,
        'muted' => true,
        'preload' => true,
        'class' => true,
        'id' => true,
        'style' => true,
    );
    $tags['source'] = array(
        'src' => true,
        'type' => true,
        'media' => true,
    );
    $tags['iframe'] = array(
        'src' => true,
        'width' => true,
        'height' => true,
        'frameborder' => true,
        'allowfullscreen' => true,
        'allow' => true,
        'class' => true,
        'id' => true,
        'style' => true,
        'title' => true,
    );

    return $tags;
}

// ══════════════════════════════════════════════
// WOOCOMMERCE REST API — FRIENDLY ERROR MESSAGES
// ══════════════════════════════════════════════

/**
 * Intercept WooCommerce REST API responses for customer creation (POST /wc/v3/customers)
 * and replace cryptic error codes with user-friendly messages.
 *
 * This also covers other common WooCommerce REST API errors so the mobile app
 * always receives clear, actionable feedback.
 */
add_filter('rest_request_after_callbacks', function ($response, $handler, $request) {
    // Only process error responses.
    if (!is_wp_error($response) && !($response instanceof WP_REST_Response && $response->is_error())) {
        return $response;
    }

    // Only rewrite errors for requests originating from the BrewmyApp mobile app.
    // Other REST clients (admin dashboards, Zapier, POS, etc.) should receive
    // standard WooCommerce error codes they may depend on.
    $ua = $request->get_header('user_agent');
    $is_bma = ($ua && strpos($ua, 'BrewmyApp') !== false)
        || $request->get_header('X-BMA-App');
    if (!$is_bma) {
        return $response;
    }

    // Determine the route.
    $route = $request->get_route();

    // ── Customer creation: POST /wc/v3/customers ──
    if (preg_match('#^/wc/v[0-9]+/customers$#', $route) && $request->get_method() === 'POST') {
        return brewmyapp_friendly_customer_error($response);
    }

    // ── Order creation: POST /wc/v3/orders ──
    if (preg_match('#^/wc/v[0-9]+/orders$#', $route) && $request->get_method() === 'POST') {
        return brewmyapp_friendly_order_error($response);
    }

    return $response;
}, 10, 3);

/**
 * Map WooCommerce customer-creation error codes to user-friendly messages.
 *
 * @param WP_Error|WP_REST_Response $response The original error response.
 * @return WP_Error
 */
function brewmyapp_friendly_customer_error($response)
{
    $code = '';
    $message = '';
    $status = 400;

    if (is_wp_error($response)) {
        $code = $response->get_error_code();
        $message = $response->get_error_message();
        $data = $response->get_error_data();
        $status = isset($data['status']) ? (int) $data['status'] : 400;
    } elseif ($response instanceof WP_REST_Response) {
        $data = $response->get_data();
        $code = isset($data['code']) ? $data['code'] : '';
        $message = isset($data['message']) ? $data['message'] : '';
        $status = $response->get_status();
    }

    // Map known error codes to friendly messages.
    $friendly_messages = array(
        'registration-error-email-exists' => __('An account with this email address already exists. Please sign in instead.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'registration-error-username-exists' => __('An account with this username already exists. Please choose a different username or sign in.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'registration-error-invalid-email' => __('Please enter a valid email address.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'registration-error-empty-email' => __('Email address is required to create an account.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'registration-error-empty-username' => __('Username is required to create an account.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'woocommerce_rest_invalid_email' => __('Please enter a valid email address.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'woocommerce_rest_customer_invalid_email' => __('Please enter a valid email address.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'existing_user_email' => __('An account with this email address already exists. Please sign in instead.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'existing_user_login' => __('An account with this username already exists. Please choose a different username or sign in.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'empty_user_login' => __('Username cannot be empty.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'user_login_too_long' => __('Username is too long. Please choose a shorter one.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'invalid_email' => __('Please enter a valid email address.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'empty_email' => __('Email address is required.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'woocommerce_rest_cannot_create' => __('Unable to create account. Please try again later.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
    );

    // Also check if the message itself contains "email already exists" patterns
    // (some WooCommerce versions use different codes).
    $lower_msg = strtolower($message);
    $is_email_exists = strpos($lower_msg, 'email') !== false && (
        strpos($lower_msg, 'already') !== false ||
        strpos($lower_msg, 'exists') !== false ||
        strpos($lower_msg, 'in use') !== false ||
        strpos($lower_msg, 'is registered') !== false
    );

    if (isset($friendly_messages[$code])) {
        $friendly = $friendly_messages[$code];
    } elseif ($is_email_exists) {
        $friendly = __('An account with this email address already exists. Please sign in instead.', 'brewmyapp-mobile-app-builder-for-woocommerce');
    } else {
        // Return the original response as-is for unmapped errors.
        return $response;
    }

    // Determine the appropriate status code for email/username exists errors.
    $exists_codes = array(
        'registration-error-email-exists',
        'registration-error-username-exists',
        'existing_user_email',
        'existing_user_login',
    );
    if (in_array($code, $exists_codes, true) || $is_email_exists) {
        $status = 409; // 409 Conflict is more appropriate for "already exists".
    }

    return new WP_Error(
        $code ? $code : 'brewmyapp_registration_error',
        $friendly,
        array('status' => $status)
    );
}

/**
 * Map WooCommerce order-creation error codes to user-friendly messages.
 *
 * @param WP_Error|WP_REST_Response $response The original error response.
 * @return WP_Error|WP_REST_Response
 */
function brewmyapp_friendly_order_error($response)
{
    $code = '';
    $message = '';
    $status = 400;

    if (is_wp_error($response)) {
        $code = $response->get_error_code();
        $message = $response->get_error_message();
        $data = $response->get_error_data();
        $status = isset($data['status']) ? (int) $data['status'] : 400;
    } elseif ($response instanceof WP_REST_Response) {
        $data = $response->get_data();
        $code = isset($data['code']) ? $data['code'] : '';
        $message = isset($data['message']) ? $data['message'] : '';
        $status = $response->get_status();
    }

    $friendly_messages = array(
        'woocommerce_rest_invalid_product_id' => __('One or more products in your order are no longer available.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'woocommerce_rest_invalid_customer_id' => __('Customer account not found. Please sign in again.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'woocommerce_rest_cannot_create_order' => __('Unable to create your order. Please try again.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
    );

    if (isset($friendly_messages[$code])) {
        return new WP_Error($code, $friendly_messages[$code], array('status' => $status));
    }

    return $response;
}

// ══════════════════════════════════════════════
// REST API ENDPOINTS
// ══════════════════════════════════════════════

/**
 * Force no-cache HTTP headers on all bma/v1/* REST responses.
 * Prevents hosting-level caches (Hostinger LiteSpeed, WP Engine,
 * SiteGround SG Optimizer, Cloudflare, etc.) from serving stale data.
 */
add_filter('rest_post_dispatch', function ($response, $server, $request) {
    $route = $request->get_route();
    // Only bypass cache for our own REST namespace.
    // Do NOT touch /wc/store/ — WooCommerce Blocks on the website rely on
    // browser caching for those responses; disabling it degrades page speed.
    if (strpos($route, '/bma/v1/') === 0) {
        $response->header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
        $response->header('Pragma', 'no-cache');
        $response->header('Expires', '0');
        $response->header('X-BMA-Cache', 'bypass');
    }
    return $response;
}, 10, 3);

/**
 * Bust WP & WC object cache for a post before reading it.
 * Forces wc_get_order() / wc_get_product() to re-read from the database
 * even when the host has persistent object caching (Redis, Memcached).
 */
function brewmyapp_clean_post_cache($post_id)
{
    clean_post_cache($post_id);
    // WooCommerce data stores keep their own in-memory cache.
    // Clearing the WC product cache ensures stock data is fresh.
    if (function_exists('wc_delete_product_transients')) {
        wc_delete_product_transients($post_id);
    }
    // WC 3.6+ caches product objects in WC_Cache_Helper
    wp_cache_delete('product-' . $post_id, 'products');
    wp_cache_delete($post_id, 'posts');
}

add_action('rest_api_init', function () {



    // ── Ping / Health Check ──────────────────
    register_rest_route('bma/v1', '/ping', array( // nosemgrep: audit.php.wp.security.rest-route.permission-callback.return-true
        'methods' => 'GET',
        'permission_callback' => '__return_true',
        'callback' => function () {
            return array(
                'active' => true,
                'plugin' => 'brewmyapp',
                'version' => BREWMYAPP_VERSION,
            );
        },
    ));

    // ── Iframe-nonce verification (called by BrewmyApp backend) ──
    // The Connect panel mints a single-use nonce, stores identity + WC keys
    // in a transient, and points the merchant (in a new tab) at the
    // BrewmyApp frontend's /connect/wordpress page. After the merchant
    // signs in there, the BrewmyApp backend calls back to this endpoint to
    // pull the payload and complete the attach — the nonce is the auth,
    // since only this WP install knows what was stashed under it. On a
    // successful read we also flip `brewmyapp_connected_at` so the panel
    // can show its "Connected" state without us needing a separate webhook.
    register_rest_route('bma/v1', '/verify-iframe-nonce', array( // nosemgrep: audit.php.wp.security.rest-route.permission-callback.return-true
        'methods' => 'GET',
        'permission_callback' => '__return_true',
        'callback' => function (WP_REST_Request $request) {
            $nonce = (string) $request->get_param('nonce');
            if (!$nonce || strlen($nonce) < 16 || !ctype_xdigit($nonce)) {
                return new WP_REST_Response(array('valid' => false), 400);
            }

            $key = 'bma_iframe_nonce_' . $nonce;
            $payload = get_transient($key);
            if (!$payload || !is_array($payload) || empty($payload['email'])) {
                return new WP_REST_Response(array('valid' => false), 404);
            }

            // One-time use — delete before returning so a replayed request
            // cannot succeed even if the response is intercepted.
            delete_transient($key);

            // Mark this site as connected. The Connect panel reads this to
            // switch from "Connect" to "Connected — open dashboard" without
            // needing its own webhook from BrewmyApp.
            update_option('brewmyapp_connected_at', time(), false);
            // Record the address we connected under. A later change to this
            // (Settings → Site Address / migration) is then detectable locally.
            update_option('brewmyapp_site_url', home_url('/'), false);
            // A fresh connect from the current address supersedes any earlier
            // change notice.
            delete_option('brewmyapp_domain_change_flag');

            $response = array(
                'valid' => true,
                'email' => (string) $payload['email'],
                'first_name' => (string) ($payload['first_name'] ?? ''),
                'last_name' => (string) ($payload['last_name'] ?? ''),
                'username' => (string) ($payload['username'] ?? ''),
                'display_name' => (string) ($payload['display_name'] ?? ''),
                'site_name' => (string) ($payload['site_name'] ?? ''),
                'phone' => (string) ($payload['phone'] ?? ''),
            );

            if (!empty($payload['consumer_key']) && !empty($payload['consumer_secret'])) {
                $response['consumer_key'] = (string) $payload['consumer_key'];
                $response['consumer_secret'] = (string) $payload['consumer_secret'];
                $response['key_permissions'] = (string) ($payload['key_permissions'] ?? 'read_write');
            }

            // Always deliver the per-store secret (pulled fresh from options so
            // re-connects still hand it to the backend even after the transient
            // payload is gone). The backend persists it encrypted.
            $response['plugin_secret'] = !empty($payload['plugin_secret'])
                ? (string) $payload['plugin_secret']
                : brewmyapp_get_plugin_secret();

            return new WP_REST_Response($response, 200);
        },
    ));

    // ── WhatsApp Configuration Status ────────
    register_rest_route('bma/v1', '/whatsapp/status', [
        'methods' => 'GET',
        'permission_callback' => 'brewmyapp_permission_admin',
        'callback' => function () {
            $token = defined('BREWMYAPP_WA_TOKEN') ? BREWMYAPP_WA_TOKEN : get_option('brewmyapp_wa_token', '');
            $template_name = defined('BREWMYAPP_WA_OTP_TEMPLATE') ? BREWMYAPP_WA_OTP_TEMPLATE : get_option('brewmyapp_wa_otp_template', '');
            $template_lang = defined('BREWMYAPP_WA_TEMPLATE_LANG') ? BREWMYAPP_WA_TEMPLATE_LANG : (get_option('brewmyapp_wa_template_lang', '') ?: 'en_US');
            $whatsapp_url = defined('BREWMYAPP_WA_SEND_URL') ? BREWMYAPP_WA_SEND_URL : get_option('brewmyapp_wa_send_url', '');

            $has_token = !empty($token);
            $has_url = !empty($whatsapp_url);
            $has_template = !empty($template_name);
            $configured = $has_token && $has_url;

            $missing = [];
            if (!$has_token)
                $missing[] = 'WA_TOKEN';
            if (!$has_url)
                $missing[] = 'Message Send URL';
            if (!$has_template)
                $missing[] = 'OTP Template Name (optional but recommended)';

            return [
                'configured' => $configured,
                'hasToken' => $has_token,
                'hasUrl' => $has_url,
                'hasTemplate' => $has_template,
                'templateLang' => $template_lang,
                'missing' => $missing,
            ];
        },
    ]);

    // ── Logs: Store Login ────────────────────
    register_rest_route('bma/v1', '/logs/store-login', array(
        'methods' => 'GET',
        'permission_callback' => 'brewmyapp_permission_woo_admin',
        'callback' => function (WP_REST_Request $req) {
            $page = max(1, (int) $req->get_param('page'));
            $per_page = (int) $req->get_param('per_page');
            $per_page = $per_page > 0 ? min(100, $per_page) : 100;

            $entries = brewmyapp_get_api_logs();
            $total = count($entries);
            $total_pages = $per_page > 0 ? (int) ceil($total / $per_page) : 1;

            $offset = ($page - 1) * $per_page;
            $paged = array_slice($entries, $offset, $per_page);

            $items = array_map(function ($entry, $index) use ($offset) {
                return array(
                    'id' => $offset + $index + 1,
                    'data' => $entry,
                );
            }, $paged, array_keys($paged));

            return array(
                'page' => $page,
                'per_page' => $per_page,
                'total' => $total,
                'total_pages' => $total_pages,
                'items' => $items,
            );
        },
    ));

    // ── Media library ───
    // The designer's image picker lets a merchant place an image they have
    // ALREADY uploaded to WordPress, instead of re-uploading it.
    //
    // This route exists because the media library is unreachable any other way:
    // the backend's WooCommerce client is pinned to /wp-json/wc/v3, and
    // attachments live at /wp-json/wp/v2/media — a different namespace that
    // WooCommerce consumer keys do not authenticate. Rather than ask every
    // merchant to mint an application password, the plugin (already installed
    // and already trusted for Basic auth) exposes it here.
    //
    // Read-only, images only, and behind the same server-to-server permission
    // as the other backend-called routes.
    register_rest_route('bma/v1', '/media', array(
        'methods' => 'GET',
        'permission_callback' => 'brewmyapp_permission_woo_admin_or_basic',
        'callback' => function (WP_REST_Request $req) {
            $page = max(1, (int) $req->get_param('page'));
            $per_page = (int) $req->get_param('per_page');
            $per_page = $per_page > 0 ? min(100, $per_page) : 40;
            $search = trim((string) $req->get_param('search'));

            $args = array(
                'post_type' => 'attachment',
                'post_status' => 'inherit',
                'post_mime_type' => 'image',
                'posts_per_page' => $per_page,
                'paged' => $page,
                'orderby' => 'date',
                'order' => 'DESC',
            );
            if ($search !== '') {
                $args['s'] = $search;
            }

            $query = new WP_Query($args);
            $images = array();

            foreach ($query->posts as $post) {
                $full = wp_get_attachment_image_src($post->ID, 'full');
                if (!$full) {
                    continue;
                }
                // medium is what the picker's grid actually renders. Falling
                // back to full is correct but heavy, so it is only a fallback —
                // some older uploads have no generated sizes.
                $thumb = wp_get_attachment_image_src($post->ID, 'medium');

                $images[] = array(
                    'id' => (string) $post->ID,
                    'url' => $full[0],
                    'thumb' => $thumb ? $thumb[0] : $full[0],
                    'width' => (int) $full[1],
                    'height' => (int) $full[2],
                    // The alt text the merchant already wrote for SEO is the
                    // best alt text we have for the app, so carry it across.
                    'alt' => (string) get_post_meta($post->ID, '_wp_attachment_image_alt', true),
                    'title' => get_the_title($post->ID),
                );
            }

            return array(
                'page' => $page,
                'per_page' => $per_page,
                'total' => (int) $query->found_posts,
                'total_pages' => (int) $query->max_num_pages,
                'images' => $images,
            );
        },
    ));

    // ── Cart: Get authenticated cart token ───
    // Returns a WC Store API Cart-Token + Nonce tied to the logged-in user's
    // persistent cart. The mobile app uses these for all cart operations.
    register_rest_route('bma/v1', '/cart/token', array(
        'methods' => 'POST',
        'permission_callback' => 'brewmyapp_permission_jwt',
        'callback' => function (WP_REST_Request $req) {
            $user_id = (int) $req->get_param('_brewmyapp_user_id');

            // Set the current WordPress user so WooCommerce associates the
            // Store API cart session with this user's persistent cart.
            wp_set_current_user($user_id); // phpcs:ignore Generic.PHP.ForbiddenFunctions.Discouraged
    
            // Build an internal REST request to the WC Store API cart endpoint
            $cart_request = new WP_REST_Request('GET', '/wc/store/v1/cart');
            $cart_request->set_header('Nonce', wp_create_nonce('wc_store_api'));

            $response = rest_do_request($cart_request);
            $server = rest_get_server();
            $data = $server->response_to_data($response, false);

            // Enrich cart items with parent_id for variation products.
            if (!empty($data['items']) && is_array($data['items'])) {
                foreach ($data['items'] as &$item) {
                    if (!isset($item['parent_id'])) {
                        $item['parent_id'] = (int) wp_get_post_parent_id($item['id']);
                    }
                }
                unset($item);
            }

            // Extract Cart-Token and Nonce from response headers
            $headers = $response->get_headers();
            $cart_token = isset($headers['Cart-Token']) ? $headers['Cart-Token'] : '';
            $nonce = isset($headers['Nonce']) ? $headers['Nonce'] : '';

            if (!$cart_token) {
                // Fallback: generate nonce if header missing (unlikely)
                $nonce = wp_create_nonce('wc_store_api');
            }

            return array(
                'cart_token' => $cart_token,
                'nonce' => $nonce,
                'cart' => $data,
            );
        },
    ));

    // ── Cart: Proxy Store API ────────────────
    // Generic proxy for WC Store API cart endpoints (add-item, remove-item,
    // update-item, etc.) that handles user authentication via JWT.
    register_rest_route('bma/v1', '/cart/proxy', array(
        'methods' => 'POST',
        'permission_callback' => 'brewmyapp_permission_jwt',
        'callback' => function (WP_REST_Request $req) {
            $user_id = (int) $req->get_param('_brewmyapp_user_id');
            wp_set_current_user($user_id); // phpcs:ignore Generic.PHP.ForbiddenFunctions.Discouraged
    
            $action = sanitize_text_field($req->get_param('action'));
            $cart_token = sanitize_text_field($req->get_header('Cart-Token'));
            $body = $req->get_json_params();
            unset($body['action']); // Remove 'action' from forwarded body
    
            // Map action to Store API path + method
            $routes = array(
                'get' => array('GET', '/wc/store/v1/cart'),
                'add-item' => array('POST', '/wc/store/v1/cart/add-item'),
                'remove-item' => array('POST', '/wc/store/v1/cart/remove-item'),
                'update-item' => array('POST', '/wc/store/v1/cart/update-item'),
                'apply-coupon' => array('POST', '/wc/store/v1/cart/apply-coupon'),
                'remove-coupon' => array('POST', '/wc/store/v1/cart/remove-coupon'),
            );

            // ── Special action: clear entire cart ──
            if ($action === 'clear') {
                $nonce = wp_create_nonce('wc_store_api');

                // 1. Get current cart items to obtain their keys
                $get_req = new WP_REST_Request('GET', '/wc/store/v1/cart');
                $get_req->set_header('Nonce', $nonce);
                if ($cart_token) {
                    $get_req->set_header('Cart-Token', $cart_token);
                }
                $get_resp = rest_do_request($get_req);
                $server = rest_get_server();
                $cart_data = $server->response_to_data($get_resp, false);
                $items = isset($cart_data['items']) ? $cart_data['items'] : array();

                // 2. Remove each item via Store API
                foreach ($items as $item) {
                    if (empty($item['key'])) {
                        continue;
                    }
                    $rm = new WP_REST_Request('POST', '/wc/store/v1/cart/remove-item');
                    $rm->set_header('Nonce', $nonce);
                    if ($cart_token) {
                        $rm->set_header('Cart-Token', $cart_token);
                    }
                    $rm->set_param('key', $item['key']);
                    rest_do_request($rm);
                }

                // 3. Persist the now-empty cart to user meta
                $blog_id = get_current_blog_id();
                if (function_exists('WC') && WC()->cart) {
                    $cart_for_session = WC()->cart->get_cart_for_session();
                    update_user_meta(
                        $user_id,
                        '_woocommerce_persistent_cart_' . $blog_id,
                        array('cart' => $cart_for_session)
                    );
                } else {
                    update_user_meta(
                        $user_id,
                        '_woocommerce_persistent_cart_' . $blog_id,
                        array('cart' => array())
                    );
                }

                // 4. Return the empty cart
                $final = new WP_REST_Request('GET', '/wc/store/v1/cart');
                $final->set_header('Nonce', $nonce);
                if ($cart_token) {
                    $final->set_header('Cart-Token', $cart_token);
                }
                $final_resp = rest_do_request($final);
                $final_data = $server->response_to_data($final_resp, false);

                $headers = $final_resp->get_headers();
                $new_cart_token = isset($headers['Cart-Token']) ? $headers['Cart-Token'] : $cart_token;
                $new_nonce = isset($headers['Nonce']) ? $headers['Nonce'] : $nonce;

                return array(
                    'cart_token' => $new_cart_token,
                    'nonce' => $new_nonce,
                    'cart' => $final_data,
                );
            }

            if (!isset($routes[$action])) {
                return new WP_Error(
                    'brewmyapp_invalid_action',
                    __('Invalid cart action.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                    array('status' => 400)
                );
            }

            list($method, $path) = $routes[$action];

            $inner = new WP_REST_Request($method, $path);
            $nonce = wp_create_nonce('wc_store_api');
            $inner->set_header('Nonce', $nonce);

            if ($cart_token) {
                $inner->set_header('Cart-Token', $cart_token);
            }

            // Forward body params for POST requests
            if ('POST' === $method && !empty($body)) {
                $inner->set_body(wp_json_encode($body));
                $inner->set_header('Content-Type', 'application/json');
                // Also set individual params for WC Store API compatibility
                foreach ($body as $key => $value) {
                    $inner->set_param($key, $value);
                }
            }

            $response = rest_do_request($inner);
            $server = rest_get_server();
            $data = $server->response_to_data($response, false);

            // Enrich cart items with parent_id for variation products.
            // The WC Store API does not include parent_id in cart items,
            // but the mobile app needs it for stock validation.
            if (!empty($data['items']) && is_array($data['items'])) {
                foreach ($data['items'] as &$item) {
                    if (!isset($item['parent_id'])) {
                        $item['parent_id'] = (int) wp_get_post_parent_id($item['id']);
                    }
                }
                unset($item); // break reference
            }

            // Force-save persistent cart after SUCCESSFUL mutating operations.
            // rest_do_request() modifies the cart in-memory but does not
            // trigger shutdown hooks that persist the cart to user meta.
            if ('POST' === $method && !$response->is_error() && function_exists('WC') && WC()->cart) {
                $cart_for_session = WC()->cart->get_cart_for_session();
                $blog_id = get_current_blog_id();
                update_user_meta(
                    $user_id,
                    '_woocommerce_persistent_cart_' . $blog_id,
                    array('cart' => $cart_for_session)
                );
            }

            // Forward updated cart-token and nonce in response
            $headers = $response->get_headers();
            $new_cart_token = isset($headers['Cart-Token']) ? $headers['Cart-Token'] : $cart_token;
            $new_nonce = isset($headers['Nonce']) ? $headers['Nonce'] : $nonce;

            $result = array(
                'cart_token' => $new_cart_token,
                'nonce' => $new_nonce,
                'cart' => $data,
            );

            // Forward errors from Store API
            if ($response->is_error()) {
                $status = $response->get_status();
                return new WP_REST_Response($result, $status);
            }

            return $result;
        },
    ));

    // ── Email OTP: Request ───────────────────
    register_rest_route('bma/v1', '/otp/request', array( // nosemgrep: audit.php.wp.security.rest-route.permission-callback.return-true
        'methods' => 'POST',
        'permission_callback' => '__return_true',
        'callback' => function (WP_REST_Request $req) {
            $email = sanitize_email($req->get_param('email'));
            if (!$email) {
                brewmyapp_log_api('/bma/v1/otp/request [POST] - MISSING EMAIL', 400, null, array('channel' => 'email'));
                return new WP_Error('brewmyapp_missing_fields', __('Email required.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }

            $user = get_user_by('email', $email);
            if (!$user) {
                brewmyapp_log_api('/bma/v1/otp/request [POST] - EMAIL NOT FOUND', 404, null, array('channel' => 'email'));
                return new WP_Error(
                    'brewmyapp_not_registered',
                    __('This email is not registered. Please sign up to create an account.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                    array('status' => 404)
                );
            }

            $rate_key = brewmyapp_otp_rate_key($email);
            if (get_transient($rate_key)) {
                brewmyapp_log_api('/bma/v1/otp/request [POST] - RATE LIMITED', 429, $user->ID, array('channel' => 'email'));
                return new WP_Error(
                    'brewmyapp_rate_limited',
                    __('Please wait before requesting another OTP.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                    array('status' => 429)
                );
            }

            $otp = brewmyapp_generate_numeric_otp(6);
            $otp_hash = brewmyapp_hash_otp($email, $otp);

            $otp_key = brewmyapp_otp_email_key($email);
            set_transient($otp_key, array(
                'hash' => $otp_hash,
                'created' => time(),
                'tries' => 0,
            ), 10 * MINUTE_IN_SECONDS);

            $sent = brewmyapp_send_otp_email($email, $otp);
            if (!$sent) {
                delete_transient($otp_key);
                brewmyapp_log_api('/bma/v1/otp/request [POST] - MAIL FAILED', 500, $user->ID, array('channel' => 'email'));
                return new WP_Error('brewmyapp_mail_failed', __('Unable to send OTP email. Please try again or contact support.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 500));
            }

            // Set rate limiter only after successful send
            set_transient($rate_key, 1, 60);

            brewmyapp_log_api('/bma/v1/otp/request [POST] - SENT', 200, $user->ID, array('channel' => 'email'));
            return array('success' => true);
        },
    ));

    // ── Email OTP: Verify ────────────────────
    register_rest_route('bma/v1', '/otp/verify', array( // nosemgrep: audit.php.wp.security.rest-route.permission-callback.return-true
        'methods' => 'POST',
        'permission_callback' => '__return_true',
        'callback' => function (WP_REST_Request $req) {
            $email = sanitize_email($req->get_param('email'));
            $otp = sanitize_text_field($req->get_param('otp'));

            if (!$email || !$otp) {
                brewmyapp_log_api('/bma/v1/otp/verify [POST] - MISSING FIELDS', 400, null, array('channel' => 'email'));
                return new WP_Error('brewmyapp_missing_fields', __('Email and OTP required.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }

            // App-review bypass: a fixed OTP for one designated customer email,
            // honored before the normal transient check so reviewers can log in.
            if (brewmyapp_otp_bypass_allows($email, $otp)) {
                $user = get_user_by('email', $email);
                if ($user) {
                    if (!brewmyapp_jwt_available()) {
                        return new WP_Error('brewmyapp_jwt_unavailable', __('JWT dependencies are not available on this server.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 500));
                    }
                    $payload = array(
                        'iat' => time(),
                        'exp' => time() + brewmyapp_get_jwt_expiry(),
                        'data' => array('user_id' => $user->ID),
                    );
                    $token = JWT::encode($payload, brewmyapp_get_jwt_secret(), 'HS256');
                    brewmyapp_log_api('/bma/v1/otp/verify [POST] - BYPASS SUCCESS', 200, $user->ID, array('channel' => 'email'));
                    return array(
                        'token' => $token,
                        'user' => array('id' => $user->ID, 'email' => $user->user_email),
                    );
                }
            }

            $otp_key = brewmyapp_otp_email_key($email);
            $record = get_transient($otp_key);
            if (!$record || empty($record['hash'])) {
                brewmyapp_log_api('/bma/v1/otp/verify [POST] - EXPIRED', 401, null, array('channel' => 'email'));
                return new WP_Error('brewmyapp_otp_invalid', __('OTP invalid or expired.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 401));
            }

            $tries = (int) ($record['tries'] ?? 0);
            if ($tries >= 5) {
                delete_transient($otp_key);
                brewmyapp_log_api('/bma/v1/otp/verify [POST] - LOCKED', 429, null, array('channel' => 'email'));
                return new WP_Error(
                    'brewmyapp_otp_locked',
                    __('Too many attempts. Request a new OTP.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                    array('status' => 429)
                );
            }

            $expected = (string) $record['hash'];
            $actual = brewmyapp_hash_otp($email, $otp);
            if (!hash_equals($expected, $actual)) {
                $record['tries'] = $tries + 1;
                set_transient($otp_key, $record, 10 * MINUTE_IN_SECONDS);
                brewmyapp_log_api('/bma/v1/otp/verify [POST] - INVALID', 401, null, array('channel' => 'email'));
                return new WP_Error('brewmyapp_otp_invalid', __('OTP invalid or expired.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 401));
            }

            delete_transient($otp_key);

            $user = get_user_by('email', $email);
            if (!$user) {
                brewmyapp_log_api('/bma/v1/otp/verify [POST] - USER NOT FOUND', 401, null, array('channel' => 'email'));
                return new WP_Error('brewmyapp_invalid', __('Invalid user.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 401));
            }

            $payload = array(
                'iat' => time(),
                'exp' => time() + brewmyapp_get_jwt_expiry(),
                'data' => array('user_id' => $user->ID),
            );

            if (!brewmyapp_jwt_available()) {
                return new WP_Error(
                    'brewmyapp_jwt_unavailable',
                    __('JWT dependencies are not available on this server.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                    array('status' => 500)
                );
            }

            $token = JWT::encode($payload, brewmyapp_get_jwt_secret(), 'HS256');

            brewmyapp_log_api('/bma/v1/otp/verify [POST] - SUCCESS', 200, $user->ID, array('channel' => 'email'));
            return array(
                'token' => $token,
                'user' => array('id' => $user->ID, 'email' => $user->user_email),
            );
        },
    ));

    // ── WhatsApp OTP: Request ────────────────
    register_rest_route('bma/v1', '/whatsapp/otp/request', array( // nosemgrep: audit.php.wp.security.rest-route.permission-callback.return-true
        'methods' => 'POST',
        'permission_callback' => '__return_true',
        'callback' => function (WP_REST_Request $req) {
            $phone = sanitize_text_field($req->get_param('phone'));
            $phone = brewmyapp_normalize_phone($phone);
            if (!$phone) {
                brewmyapp_log_api('/bma/v1/whatsapp/otp/request [POST] - MISSING PHONE', 400, null, array('channel' => 'whatsapp'));
                return new WP_Error('brewmyapp_missing_fields', __('Phone required.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }

            $user = brewmyapp_find_user_by_phone($phone);
            if (!$user) {
                brewmyapp_log_api('/bma/v1/whatsapp/otp/request [POST] - PHONE NOT FOUND', 404, null, array('channel' => 'whatsapp'));
                return new WP_Error(
                    'brewmyapp_not_registered',
                    __('This phone number is not registered. Please sign up to create an account.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                    array('status' => 404)
                );
            }

            $rate_key = brewmyapp_otp_phone_rate_key($phone);
            if (get_transient($rate_key)) {
                brewmyapp_log_api('/bma/v1/whatsapp/otp/request [POST] - RATE LIMITED', 429, $user->ID, array('channel' => 'whatsapp'));
                return new WP_Error(
                    'brewmyapp_rate_limited',
                    __('Please wait before requesting another OTP.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                    array('status' => 429)
                );
            }

            $otp = brewmyapp_generate_numeric_otp(6);
            $otp_hash = brewmyapp_hash_otp_phone($phone, $otp);

            $otp_key = brewmyapp_otp_phone_key($phone);
            set_transient($otp_key, array(
                'hash' => $otp_hash,
                'created' => time(),
                'tries' => 0,
            ), 10 * MINUTE_IN_SECONDS);

            $sent = brewmyapp_send_whatsapp_otp($phone, $otp);
            if (is_wp_error($sent)) {
                delete_transient($otp_key);
                brewmyapp_log_api('/bma/v1/whatsapp/otp/request [POST] - SEND FAILED', 502, $user->ID, array(
                    'channel' => 'whatsapp',
                    'error' => $sent->get_error_code(),
                ));
                return $sent;
            }

            // Set rate limiter only after successful send
            set_transient($rate_key, 1, 60);

            brewmyapp_log_api('/bma/v1/whatsapp/otp/request [POST] - SENT', 200, $user->ID, array('channel' => 'whatsapp'));
            return array('success' => true);
        },
    ));

    // ── WhatsApp OTP: Verify ─────────────────
    register_rest_route('bma/v1', '/whatsapp/otp/verify', array( // nosemgrep: audit.php.wp.security.rest-route.permission-callback.return-true
        'methods' => 'POST',
        'permission_callback' => '__return_true',
        'callback' => function (WP_REST_Request $req) {
            $phone = sanitize_text_field($req->get_param('phone'));
            $otp = sanitize_text_field($req->get_param('otp'));
            $phone = brewmyapp_normalize_phone($phone);

            if (!$phone || !$otp) {
                brewmyapp_log_api('/bma/v1/whatsapp/otp/verify [POST] - MISSING FIELDS', 400, null, array('channel' => 'whatsapp'));
                return new WP_Error('brewmyapp_missing_fields', __('Phone and OTP required.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }

            // App-review bypass: resolve the phone's user and, if its email is the
            // designated bypass customer and the fixed OTP matches, log in directly.
            $bypass_user = brewmyapp_find_user_by_phone($phone);
            if ($bypass_user && brewmyapp_otp_bypass_allows($bypass_user->user_email, $otp)) {
                if (!brewmyapp_jwt_available()) {
                    return new WP_Error('brewmyapp_jwt_unavailable', __('JWT dependencies are not available on this server.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 500));
                }
                $payload = array(
                    'iat' => time(),
                    'exp' => time() + brewmyapp_get_jwt_expiry(),
                    'data' => array('user_id' => $bypass_user->ID),
                );
                $token = JWT::encode($payload, brewmyapp_get_jwt_secret(), 'HS256');
                brewmyapp_log_api('/bma/v1/whatsapp/otp/verify [POST] - BYPASS SUCCESS', 200, $bypass_user->ID, array('channel' => 'whatsapp'));
                return array(
                    'token' => $token,
                    'user' => array('id' => $bypass_user->ID, 'email' => $bypass_user->user_email),
                );
            }

            $otp_key = brewmyapp_otp_phone_key($phone);
            $record = get_transient($otp_key);
            if (!$record || empty($record['hash'])) {
                brewmyapp_log_api('/bma/v1/whatsapp/otp/verify [POST] - EXPIRED', 401, null, array('channel' => 'whatsapp'));
                return new WP_Error('brewmyapp_otp_invalid', __('OTP invalid or expired.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 401));
            }

            $tries = (int) ($record['tries'] ?? 0);
            if ($tries >= 5) {
                delete_transient($otp_key);
                brewmyapp_log_api('/bma/v1/whatsapp/otp/verify [POST] - LOCKED', 429, null, array('channel' => 'whatsapp'));
                return new WP_Error(
                    'brewmyapp_otp_locked',
                    __('Too many attempts. Request a new OTP.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                    array('status' => 429)
                );
            }

            $expected = (string) $record['hash'];
            $actual = brewmyapp_hash_otp_phone($phone, $otp);
            if (!hash_equals($expected, $actual)) {
                $record['tries'] = $tries + 1;
                set_transient($otp_key, $record, 10 * MINUTE_IN_SECONDS);
                brewmyapp_log_api('/bma/v1/whatsapp/otp/verify [POST] - INVALID', 401, null, array('channel' => 'whatsapp'));
                return new WP_Error('brewmyapp_otp_invalid', __('OTP invalid or expired.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 401));
            }

            delete_transient($otp_key);

            $user = brewmyapp_find_user_by_phone($phone);
            if (!$user) {
                brewmyapp_log_api('/bma/v1/whatsapp/otp/verify [POST] - USER NOT FOUND', 401, null, array('channel' => 'whatsapp'));
                return new WP_Error('brewmyapp_invalid', __('Invalid user.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 401));
            }

            $payload = array(
                'iat' => time(),
                'exp' => time() + brewmyapp_get_jwt_expiry(),
                'data' => array('user_id' => $user->ID),
            );

            if (!brewmyapp_jwt_available()) {
                return new WP_Error(
                    'brewmyapp_jwt_unavailable',
                    __('JWT dependencies are not available on this server.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                    array('status' => 500)
                );
            }

            $token = JWT::encode($payload, brewmyapp_get_jwt_secret(), 'HS256');

            brewmyapp_log_api('/bma/v1/whatsapp/otp/verify [POST] - SUCCESS', 200, $user->ID, array('channel' => 'whatsapp'));
            return array(
                'token' => $token,
                'user' => array('id' => $user->ID, 'email' => $user->user_email),
            );
        },
    ));

    // ── Email OTP: Test (sends a test email to verify wp_mail works) ──
    register_rest_route('bma/v1', '/otp/test-email', array(
        'methods' => 'POST',
        'permission_callback' => 'brewmyapp_permission_admin',
        'callback' => function (WP_REST_Request $req) {
            $to = sanitize_email($req->get_param('email'));
            if (!$to) {
                // Default to site admin email
                $to = get_option('admin_email');
            }
            if (!$to) {
                return new WP_Error('brewmyapp_no_email', __('No email address available.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }

            $sent = brewmyapp_send_otp_email($to, '123456');

            if (!$sent) {
                return new WP_Error('brewmyapp_mail_failed', __('Failed to send test email via BrewmyApp. Please check your store is connected to BrewmyApp.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 500));
            }

            return array(
                'success' => true,
                'email' => $to,
                /* translators: %s: email address the test was sent to */
                'message' => sprintf(__('Test email sent to %s via BrewmyApp (noreply@brewmyapp.io)', 'brewmyapp-mobile-app-builder-for-woocommerce'), $to),
            );
        },
    ));

    // ── WhatsApp OTP: Test (sends a test WhatsApp message to verify configuration works) ──
    register_rest_route('bma/v1', '/whatsapp/test', array(
        'methods' => 'POST',
        'permission_callback' => 'brewmyapp_permission_admin',
        'callback' => function (WP_REST_Request $req) {
            $phone = sanitize_text_field($req->get_param('phone'));
            if (empty($phone)) {
                return new WP_Error('brewmyapp_no_phone', __('Phone number is required.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }

            // Check WhatsApp is configured first
            $token = defined('BREWMYAPP_WA_TOKEN') ? BREWMYAPP_WA_TOKEN : get_option('brewmyapp_wa_token', '');
            $whatsapp_url = defined('BREWMYAPP_WA_SEND_URL') ? BREWMYAPP_WA_SEND_URL : get_option('brewmyapp_wa_send_url', '');
            if (empty($token) || empty($whatsapp_url)) {
                return new WP_Error('brewmyapp_whatsapp_not_configured', __('WhatsApp API not configured. Set WA_TOKEN and Send URL first.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }

            // Send a test OTP (code: 000000)
            $result = brewmyapp_send_whatsapp_otp($phone, '000000');

            if (is_wp_error($result)) {
                return new WP_Error('brewmyapp_whatsapp_test_failed', $result->get_error_message(), array(
                    'status' => 502,
                    'detail' => $result->get_error_data(),
                ));
            }

            return array(
                'success' => true,
                'phone' => $phone,
                /* translators: %s: phone number the test was sent to */
                'message' => sprintf(__('Test WhatsApp message sent to %s', 'brewmyapp-mobile-app-builder-for-woocommerce'), $phone),
            );
        },
    ));

    // ── Store Owner Info: Submit phone number to BrewmyApp backend ──
    register_rest_route('bma/v1', '/store-owner-info', array(
        'methods' => 'POST',
        'permission_callback' => 'brewmyapp_permission_admin',
        'callback' => function (WP_REST_Request $req) {
            $phone = sanitize_text_field($req->get_param('phone') ?? '');

            if (!$phone || strlen($phone) < 5) {
                return new WP_Error(
                    'brewmyapp_invalid_phone',
                    __('Please provide a valid phone number (at least 5 characters).', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                    array('status' => 400)
                );
            }

            // Get store and admin info
            $store_url = home_url();
            $admin_email = get_option('admin_email');
            $blog_name = get_option('blogname') ?: 'Store';

            // Send to BrewmyApp backend to store with the WordPress plugin store record
            // This will be matched by store_url and updated in the backend
            $backend_url = defined('BREWMYAPP_BACKEND_URL') ? BREWMYAPP_BACKEND_URL : 'https://api.brewmyapp.io';
            $endpoint = $backend_url . '/api/stores/wordpress/owner-info';

            $response = wp_remote_post(
                $endpoint,
                array(
                    'method' => 'POST',
                    'timeout' => 10,
                    'redirection' => 5,
                    'blocking' => true,
                    'sslverify' => true,
                    'headers' => array(
                        'Content-Type' => 'application/json',
                    ),
                    'body' => wp_json_encode(array(
                        'storeUrl' => $store_url,
                        'storePhone' => $phone,
                        'storeEmail' => $admin_email,
                        'storeName' => $blog_name,
                    )),
                )
            );

            if (is_wp_error($response)) {
                return new WP_Error(
                    'brewmyapp_backend_error',
                    sprintf(
                        /* translators: %s: error message */
                        __('Failed to submit phone to BrewmyApp: %s', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                        $response->get_error_message()
                    ),
                    array('status' => 500)
                );
            }

            $status_code = (int) wp_remote_retrieve_response_code($response);
            if ($status_code < 200 || $status_code >= 300) {
                $body = wp_remote_retrieve_body($response);
                return new WP_Error(
                    'brewmyapp_backend_error',
                    sprintf(
                        /* translators: %d: HTTP status code */
                        __('BrewmyApp backend returned status %d', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                        $status_code
                    ),
                    array('status' => 502)
                );
            }

            // Save phone locally in WordPress as well
            update_option('brewmyapp_store_owner_phone', $phone);

            return array(
                'success' => true,
                'message' => __('Phone number saved successfully.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                'phone' => $phone,
            );
        },
    ));

    // ── Payment: Generate order-pay URL ──────
    register_rest_route('bma/v1', '/payment/order-pay-url', array(
        'methods' => 'POST',
        'permission_callback' => 'brewmyapp_permission_woo_admin_or_basic',
        'callback' => function (WP_REST_Request $req) {
            $user_id = (int) $req->get_param('_brewmyapp_user_id');
            $order_id = (int) $req->get_param('order_id');
            $customer_id = (int) $req->get_param('customer_id');

            if (!$order_id) {
                return new WP_Error('brewmyapp_missing_order', __('order_id is required.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }
            if (!$customer_id) {
                return new WP_Error('brewmyapp_missing_customer', __('customer_id is required.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }

            brewmyapp_clean_post_cache($order_id);
            $order = wc_get_order($order_id);
            if (!$order) {
                return new WP_Error('brewmyapp_invalid_order', __('Order not found.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 404));
            }

            if (!$order->get_customer_id() || $order->get_customer_id() !== $customer_id) {
                return new WP_Error('brewmyapp_unauthorized', __('Order does not belong to this customer.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 403));
            }

            $status = $order->get_status();
            if (!in_array($status, array('pending', 'failed'), true)) {
                return new WP_Error(
                    'brewmyapp_invalid_status',
                    /* translators: %s: the order status */
                    sprintf(__('Order status "%s" cannot be paid.', 'brewmyapp-mobile-app-builder-for-woocommerce'), $status),
                    array('status' => 400)
                );
            }

            $token = brewmyapp_generate_payment_token($order_id, $customer_id, 3600);
            $pay_url = home_url('/bma-pay/?token=' . urlencode($token));

            return array(
                'success' => true,
                'payment_url' => $pay_url,
                'expires_in' => 3600,
                'order_id' => $order_id,
                'order_status' => $status,
                'order_total' => $order->get_total(),
                'currency' => $order->get_currency(),
            );
        },
    ));

    // ── Payment: Check order status ──────────
    register_rest_route('bma/v1', '/payment/order-status', array(
        'methods' => 'GET',
        'permission_callback' => 'brewmyapp_permission_woo_admin_or_basic',
        'callback' => function (WP_REST_Request $req) {
            $order_id = (int) $req->get_param('order_id');
            $customer_id = (int) $req->get_param('customer_id');

            if (!$order_id) {
                return new WP_Error('brewmyapp_missing_order', __('order_id is required.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }

            brewmyapp_clean_post_cache($order_id);
            $order = wc_get_order($order_id);
            if (!$order) {
                return new WP_Error('brewmyapp_invalid_order', __('Order not found.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 404));
            }

            if ($customer_id && $order->get_customer_id() && $order->get_customer_id() !== $customer_id) {
                return new WP_Error('brewmyapp_unauthorized', __('Order does not belong to this customer.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 403));
            }

            $status = $order->get_status();
            $is_paid = in_array($status, array('processing', 'completed', 'on-hold'), true);

            return array(
                'success' => true,
                'order_id' => $order_id,
                'status' => $status,
                'is_paid' => $is_paid,
                'total' => $order->get_total(),
                'currency' => $order->get_currency(),
                'payment_method' => $order->get_payment_method(),
                'payment_method_title' => $order->get_payment_method_title(),
                'date_paid' => $order->get_date_paid() ? $order->get_date_paid()->format('c') : null,
            );
        },
    ));

    // ══════════════════════════════════════════════
    // PUSH TOKEN ENDPOINTS
    // ══════════════════════════════════════════════

    // ── Register Push Token ──────────────────
    register_rest_route('bma/v1', '/push-token', array(
        'methods' => 'POST',
        'permission_callback' => 'brewmyapp_permission_jwt',
        'callback' => function (WP_REST_Request $req) {
            $user_id = (int) $req->get_param('_brewmyapp_user_id');
            $token = sanitize_text_field($req->get_param('token') ?? '');
            $platform = sanitize_text_field($req->get_param('platform') ?? '');
            $device_name = sanitize_text_field($req->get_param('device_name') ?? '');
            $customer_id = (int) ($req->get_param('customer_id') ?? 0);

            if (!$token) {
                return new WP_Error('brewmyapp_missing_token', __('Push token is required.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }

            if (!in_array($platform, array('ios', 'android'), true)) {
                $platform = 'unknown';
            }

            $token_prefix = substr($token, 0, 25);
            error_log("[BMA][PUSH][POST] user_id={$user_id} customer_id={$customer_id} platform={$platform} token_prefix={$token_prefix}");

            $tokens = get_user_meta($user_id, 'brewmyapp_expo_push_tokens', true);
            if (!is_array($tokens)) {
                $tokens = array();
            }

            $exists = false;
            foreach ($tokens as &$entry) {
                if ($entry['token'] === $token) {
                    $entry['platform'] = $platform;
                    $entry['device_name'] = $device_name;
                    $entry['updated_at'] = gmdate('c');
                    $exists = true;
                    break;
                }
            }
            unset($entry);

            if (!$exists) {
                $tokens[] = array(
                    'token' => $token,
                    'platform' => $platform,
                    'device_name' => $device_name,
                    'customer_id' => $customer_id,
                    'registered_at' => gmdate('c'),
                    'updated_at' => gmdate('c'),
                );
            }

            update_user_meta($user_id, 'brewmyapp_expo_push_tokens', $tokens);

            error_log("[BMA][PUSH][POST] user_id={$user_id} action=" . ($exists ? 'updated' : 'created') . " total_tokens=" . count($tokens));

            return array(
                'success' => true,
                'message' => $exists
                    ? __('Push token updated.', 'brewmyapp-mobile-app-builder-for-woocommerce')
                    : __('Push token registered.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                'total_tokens' => count($tokens),
            );
        },
    ));

    // ── Unregister Push Token ────────────────
    register_rest_route('bma/v1', '/push-token', array(
        'methods' => 'DELETE',
        'permission_callback' => 'brewmyapp_permission_jwt',
        'callback' => function (WP_REST_Request $req) {
            $user_id = (int) $req->get_param('_brewmyapp_user_id');
            $token = sanitize_text_field($req->get_param('token') ?? '');
            if (!$token) {
                return new WP_Error('brewmyapp_missing_token', __('Push token is required.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }

            $token_prefix = substr($token, 0, 25);
            error_log("[BMA][PUSH][DELETE] user_id={$user_id} token_prefix={$token_prefix}");

            $tokens = get_user_meta($user_id, 'brewmyapp_expo_push_tokens', true);
            if (!is_array($tokens)) {
                $tokens = array();
            }

            $original_count = count($tokens);
            $tokens = array_values(array_filter($tokens, function ($entry) use ($token) {
                return $entry['token'] !== $token;
            }));

            update_user_meta($user_id, 'brewmyapp_expo_push_tokens', $tokens);

            $removed = count($tokens) < $original_count;
            error_log("[BMA][PUSH][DELETE] user_id={$user_id} removed=" . ($removed ? 'yes' : 'no') . " remaining=" . count($tokens));

            return array(
                'success' => true,
                'message' => $removed
                    ? __('Push token removed.', 'brewmyapp-mobile-app-builder-for-woocommerce')
                    : __('Token not found.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                'total_tokens' => count($tokens),
            );
        },
    ));

    // ── Send Notification (Admin only via API) ──
    register_rest_route('bma/v1', '/push-notification/send', array(
        'methods' => 'POST',
        // admin_or_basic lets the BrewmyApp backend call this endpoint with
        // the merchant's WC consumer key/secret (no JWT available there).
        'permission_callback' => 'brewmyapp_permission_admin_or_basic',
        'callback' => function (WP_REST_Request $req) {
            $title = sanitize_text_field($req->get_param('title') ?? '');
            $body = sanitize_text_field($req->get_param('body') ?? '');
            $target = sanitize_text_field($req->get_param('target') ?? 'all');
            $target_ids = $req->get_param('target_ids') ?? array();
            $data = $req->get_param('data') ?? array();

            if (!$title || !$body) {
                return new WP_Error('brewmyapp_missing_fields', __('Title and body are required.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }

            $all_tokens = array();

            if ($target === 'specific' && !empty($target_ids)) {
                foreach ((array) $target_ids as $uid) {
                    $all_tokens = array_merge($all_tokens, brewmyapp_get_user_push_tokens((int) $uid));
                }
            } else {
                $users = get_users(array('meta_key' => 'brewmyapp_expo_push_tokens')); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
                foreach ($users as $user) {
                    $all_tokens = array_merge($all_tokens, brewmyapp_get_user_push_tokens($user->ID));
                }
            }

            if (empty($all_tokens)) {
                return array(
                    'success' => true,
                    'message' => __('No push tokens found for target audience.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                    'sent' => 0,
                    'failed' => 0,
                );
            }

            $result = brewmyapp_send_expo_push($all_tokens, $title, $body, $data);
            brewmyapp_log_push_notification($title, $body, $target, $result, 'api');

            return array(
                'success' => true,
                /* translators: %d: number of devices notified */
                'message' => sprintf(__('Notification sent to %d device(s).', 'brewmyapp-mobile-app-builder-for-woocommerce'), $result['sent']),
                'sent' => $result['sent'],
                'failed' => $result['failed'],
                'errors' => $result['errors'],
            );
        },
    ));

    // ── List Users with Push Tokens (for dashboard test tool) ──
    register_rest_route('bma/v1', '/push-tokens/users', array(
        'methods' => 'GET',
        'permission_callback' => 'brewmyapp_permission_woo_admin_or_basic',
        'callback' => function (WP_REST_Request $req) {
            $users = get_users(array('meta_key' => 'brewmyapp_expo_push_tokens')); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
            $result = array();

            foreach ($users as $user) {
                $tokens = get_user_meta($user->ID, 'brewmyapp_expo_push_tokens', true);
                if (!is_array($tokens) || empty($tokens)) {
                    continue;
                }

                $token_list = array_map(function ($entry) {
                    return array(
                        'token' => $entry['token'] ?? '',
                        'platform' => $entry['platform'] ?? 'unknown',
                        'device' => $entry['device_name'] ?? '',
                    );
                }, $tokens);

                $result[] = array(
                    'id' => $user->ID,
                    'display_name' => $user->display_name ? $user->display_name : $user->user_login,
                    'email' => $user->user_email,
                    'roles' => $user->roles,
                    'token_count' => count($tokens),
                    'tokens' => $token_list,
                );
            }

            return array('success' => true, 'users' => $result);
        },
    ));

    // ── Back-in-Stock: Subscribe ─────────────
    register_rest_route('bma/v1', '/back-in-stock', array(
        'methods' => 'POST',
        'permission_callback' => 'brewmyapp_permission_jwt',
        'callback' => function (WP_REST_Request $req) {
            $user_id = (int) $req->get_param('_brewmyapp_user_id');
            $product_id = (int) $req->get_param('product_id');

            if (!$product_id) {
                return new WP_Error('brewmyapp_missing_product', __('product_id is required.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }

            // Get existing subscriptions for this user
            $subs = get_user_meta($user_id, 'brewmyapp_back_in_stock_subs', true);
            if (!is_array($subs)) {
                $subs = array();
            }

            // Avoid duplicates
            if (!in_array($product_id, $subs, true)) {
                $subs[] = $product_id;
                update_user_meta($user_id, 'brewmyapp_back_in_stock_subs', $subs);
            }

            // (The out-of-stock flag is automatically set by the stock transition tracker hook)
    
            return array(
                'success' => true,
                'message' => __('You will be notified when this product is back in stock.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                'product_id' => $product_id,
            );
        },
    ));

    // ── Back-in-Stock: Subscriber list (read-only; BrewmyApp Notify Me page) ──
    //
    // Grouped by product, and deliberately reporting BOTH still-waiting and
    // already-notified customers: the notified group is the better marketing
    // audience, because their interest is proven and an alert alone does not
    // mean they bought.
    register_rest_route('bma/v1', '/back-in-stock/subscribers', array(
        'methods' => 'GET',
        'permission_callback' => 'brewmyapp_permission_admin_or_basic',
        'callback' => function (WP_REST_Request $req) {
            // A store with a very long tail would otherwise build an unbounded
            // response. Matches SUBSCRIBER_ROW_CAP on the BrewmyApp backend.
            $cap = 5000;
            $rows = 0;
            $truncated = false;
            $products = array();
            $customer_ids = array();

            foreach (brewmyapp_collect_back_in_stock_subs() as $user) {
                $entries = array();
                foreach ($user['waiting'] as $pid) {
                    $entries[] = array('pid' => (int) $pid, 'notified_at' => null);
                }
                foreach ($user['notified'] as $pid => $ts) {
                    $entries[] = array('pid' => (int) $pid, 'notified_at' => (int) $ts);
                }

                foreach ($entries as $entry) {
                    if ($rows >= $cap) {
                        $truncated = true;
                        break 2;
                    }
                    $rows++;
                    $pid = $entry['pid'];
                    if (!isset($products[$pid])) {
                        $products[$pid] = array(
                            'productId'   => (string) $pid,
                            'name'        => null,
                            'image'       => null,
                            'waiting'     => 0,
                            'notified'    => 0,
                            'subscribers' => array(),
                        );
                    }
                    if ($entry['notified_at']) {
                        $products[$pid]['notified']++;
                    } else {
                        $products[$pid]['waiting']++;
                    }
                    $products[$pid]['subscribers'][] = array(
                        'customerId'  => (string) $user['id'],
                        'name'        => $user['name'],
                        'email'       => $user['email'],
                        'subscribedAt' => null, // user meta carries no subscribe time
                        'notifiedAt'  => $entry['notified_at']
                            ? gmdate('c', $entry['notified_at'])
                            : null,
                    );
                    $customer_ids[$user['id']] = true;
                }
            }

            // Resolve names/images in one pass over the products we actually
            // have subscribers for — not a catalog query.
            foreach (array_keys($products) as $pid) {
                $product = wc_get_product($pid);
                if (!$product) {
                    continue;
                }
                $products[$pid]['name'] = $product->get_name();
                $image_id = $product->get_image_id();
                if ($image_id) {
                    $src = wp_get_attachment_image_url($image_id, 'woocommerce_thumbnail');
                    $products[$pid]['image'] = $src ? $src : null;
                }
            }

            // Most-wanted first — that ordering IS the insight the page exists for.
            $list = array_values($products);
            usort($list, function ($a, $b) {
                if ($a['waiting'] !== $b['waiting']) {
                    return $b['waiting'] - $a['waiting'];
                }
                if ($a['notified'] !== $b['notified']) {
                    return $b['notified'] - $a['notified'];
                }
                return strcasecmp((string) $a['name'], (string) $b['name']);
            });

            $total_waiting = 0;
            $total_notified = 0;
            foreach ($list as $entry_row) {
                $total_waiting += $entry_row['waiting'];
                $total_notified += $entry_row['notified'];
            }

            return array(
                'success'   => true,
                'products'  => $list,
                'totals'    => array(
                    'products'  => count($list),
                    'waiting'   => $total_waiting,
                    'notified'  => $total_notified,
                    'customers' => count($customer_ids),
                ),
                'truncated' => $truncated,
            );
        },
    ));

    // ── Push History (read-only; for dashboard history tab) ──
    register_rest_route('bma/v1', '/push-notification/history', array(
        'methods' => 'GET',
        'permission_callback' => 'brewmyapp_permission_admin_or_basic',
        'callback' => function (WP_REST_Request $req) {
            $logs = get_option('brewmyapp_push_notification_logs', array());
            if (!is_array($logs)) {
                $logs = array();
            }
            return array('success' => true, 'logs' => array_slice($logs, 0, 50));
        },
    ));

    // ── Push Stats (token counters + lifetime sent) ──
    register_rest_route('bma/v1', '/push-notification/stats', array(
        'methods' => 'GET',
        'permission_callback' => 'brewmyapp_permission_admin_or_basic',
        'callback' => function (WP_REST_Request $req) {
            $users = get_users(array('meta_key' => 'brewmyapp_expo_push_tokens', 'fields' => 'ID')); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
            $device_count = 0;
            foreach ($users as $uid) {
                $tokens = get_user_meta($uid, 'brewmyapp_expo_push_tokens', true);
                if (is_array($tokens)) {
                    $device_count += count($tokens);
                }
            }
            $logs = get_option('brewmyapp_push_notification_logs', array());
            $total_sent = 0;
            if (is_array($logs)) {
                foreach ($logs as $entry) {
                    $total_sent += (int) ($entry['sent'] ?? 0);
                }
            }
            return array(
                'success' => true,
                'unique_customers' => count($users),
                'device_count' => $device_count,
                'total_sent' => $total_sent,
            );
        },
    ));

    // ── Order Status Templates (GET / PUT) ──
    register_rest_route('bma/v1', '/push-notification/order-templates', array(
        'methods' => 'GET',
        'permission_callback' => 'brewmyapp_permission_admin_or_basic',
        'callback' => function (WP_REST_Request $req) {
            return array(
                'success' => true,
                'templates' => brewmyapp_get_order_notification_settings(),
                'app_url' => get_option('brewmyapp_app_url', ''),
            );
        },
    ));
    register_rest_route('bma/v1', '/push-notification/order-templates', array(
        'methods' => 'PUT',
        'permission_callback' => 'brewmyapp_permission_admin_or_basic',
        'callback' => function (WP_REST_Request $req) {
            $templates = $req->get_param('templates');
            if (!is_array($templates)) {
                return new WP_Error('brewmyapp_invalid', __('templates object required.', 'brewmyapp-mobile-app-builder-for-woocommerce'), array('status' => 400));
            }
            // Sanitize each status entry — only persist known fields.
            $clean = array();
            foreach ($templates as $status => $tpl) {
                $status_key = sanitize_key($status);
                $clean[$status_key] = array(
                    'enabled' => !empty($tpl['enabled']),
                    'title' => sanitize_text_field($tpl['title'] ?? ''),
                    'body' => sanitize_text_field($tpl['body'] ?? ''),
                );
            }
            update_option('brewmyapp_order_notification_settings', $clean);

            $app_url = $req->get_param('app_url');
            if ($app_url !== null) {
                update_option('brewmyapp_app_url', sanitize_text_field($app_url));
            }

            return array(
                'success' => true,
                'templates' => brewmyapp_get_order_notification_settings(),
                'app_url' => get_option('brewmyapp_app_url', ''),
            );
        },
    ));

    // ── Back-in-Stock Settings (GET / PUT) ──
    register_rest_route('bma/v1', '/push-notification/back-in-stock', array(
        'methods' => 'GET',
        'permission_callback' => 'brewmyapp_permission_admin_or_basic',
        'callback' => function (WP_REST_Request $req) {
            $settings = get_option('brewmyapp_back_in_stock_settings', array());
            if (!is_array($settings)) {
                $settings = array();
            }
            return array(
                'success' => true,
                'enabled' => isset($settings['enabled']) ? (bool) $settings['enabled'] : true,
                'title' => $settings['title'] ?? __('Back in stock!', 'brewmyapp-mobile-app-builder-for-woocommerce'),
                'body' => $settings['body'] ?? __('{product_name} is available again — grab it before it sells out.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            );
        },
    ));
    register_rest_route('bma/v1', '/push-notification/back-in-stock', array(
        'methods' => 'PUT',
        'permission_callback' => 'brewmyapp_permission_admin_or_basic',
        'callback' => function (WP_REST_Request $req) {
            $settings = array(
                'enabled' => !empty($req->get_param('enabled')),
                'title' => sanitize_text_field($req->get_param('title') ?? ''),
                'body' => sanitize_text_field($req->get_param('body') ?? ''),
            );
            update_option('brewmyapp_back_in_stock_settings', $settings);
            return array('success' => true) + $settings;
        },
    ));
});

// ══════════════════════════════════════════════
// PUSH NOTIFICATION LOGGING
// ══════════════════════════════════════════════

/**
 * Log a push notification to the options table for admin history.
 *
 * @param string $title  Notification title.
 * @param string $body   Notification body.
 * @param string $target Target audience.
 * @param array  $result Send result array.
 * @param string $source Source identifier (api, admin, woocommerce).
 */
function brewmyapp_log_push_notification($title, $body, $target, $result, $source = 'api')
{
    $logs = get_option('brewmyapp_push_notification_logs', array());
    if (!is_array($logs)) {
        $logs = array();
    }

    array_unshift($logs, array(
        'title' => $title,
        'body' => $body,
        'target' => $target,
        'sent' => $result['sent'] ?? 0,
        'failed' => $result['failed'] ?? 0,
        'errors' => $result['errors'] ?? array(),
        'source' => $source,
        'created_at' => current_time('mysql'),
    ));

    $logs = array_slice($logs, 0, 200);
    update_option('brewmyapp_push_notification_logs', $logs);
}

// ══════════════════════════════════════════════
// WOOCOMMERCE ORDER STATUS NOTIFICATIONS
// ══════════════════════════════════════════════

/**
 * Default notification templates per order status.
 *
 * @return array
 */
/**
 * Localized default order-status templates for THIS store's app build language,
 * fetched from the BrewmyApp backend (cached 6h) and keyed by WooCommerce order
 * status. Returns null when the backend is unreachable or returns nothing, so
 * callers fall back to the bundled English copy. Merchant overrides still win.
 */
function brewmyapp_get_localized_order_defaults()
{
    $resp = brewmyapp_plugin_api('/order-template-defaults', 6 * HOUR_IN_SECONDS);
    if (is_wp_error($resp) || empty($resp['defaults']) || !is_array($resp['defaults'])) {
        return null;
    }
    $out = array();
    foreach ($resp['defaults'] as $status => $tpl) {
        if (!is_array($tpl) || empty($tpl['title']) || empty($tpl['body'])) {
            continue;
        }
        $out[$status] = array(
            'enabled' => isset($tpl['enabled']) ? (bool) $tpl['enabled'] : true,
            'title' => (string) $tpl['title'],
            'body' => (string) $tpl['body'],
        );
    }
    return !empty($out) ? $out : null;
}

/**
 * Default order-status templates. Prefers the app-language localized copy from
 * the backend, layered over the bundled English defaults (so every WC status is
 * always covered even if the backend omits one).
 */
function brewmyapp_get_default_order_templates()
{
    $en = brewmyapp_get_default_order_templates_en();
    $localized = brewmyapp_get_localized_order_defaults();
    return is_array($localized) && !empty($localized)
        ? array_merge($en, $localized)
        : $en;
}

/** Bundled English fallback templates (translated via load_plugin_textdomain). */
function brewmyapp_get_default_order_templates_en()
{
    return array(
        'processing' => array(
            'enabled' => true,
            'title' => __('Order Confirmed! ✅', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'body' => __('Your order #{order_number} is confirmed and being processed.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        ),
        'completed' => array(
            'enabled' => true,
            'title' => __('Order Shipped! 🚚', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'body' => __('Great news! Your order #{order_number} is on its way.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        ),
        'shipped' => array(
            'enabled' => true,
            'title' => __('Order Shipped! 🚚', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'body' => __('Great news! Your order #{order_number} is on its way.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        ),
        'cancelled' => array(
            'enabled' => true,
            'title' => __('Order Cancelled', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'body' => __('Your order #{order_number} has been cancelled.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        ),
        'refunded' => array(
            'enabled' => true,
            'title' => __('Order Refunded', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'body' => __('Your order #{order_number} has been refunded.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        ),
        'on-hold' => array(
            'enabled' => true,
            'title' => __('Order On Hold ⏳', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'body' => __("Your order #{order_number} is on hold. We'll notify you once it's updated.", 'brewmyapp-mobile-app-builder-for-woocommerce'),
        ),
        'pending' => array(
            'enabled' => true,
            'title' => __('Order Pending Payment', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'body' => __('Your order #{order_number} is awaiting payment.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        ),
        'failed' => array(
            'enabled' => true,
            'title' => __('Order Failed', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'body' => __('Your order #{order_number} payment has failed. Please try again.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        ),
    );
}

/**
 * Return merged templates: saved settings over defaults.
 *
 * @return array
 */
function brewmyapp_get_order_notification_settings()
{
    $defaults = brewmyapp_get_default_order_templates();
    $saved = get_option('brewmyapp_order_notification_settings', array());
    if (!is_array($saved)) {
        $saved = array();
    }

    $merged = array();
    foreach ($defaults as $status => $tpl) {
        $merged[$status] = array(
            'enabled' => isset($saved[$status]['enabled']) ? (bool) $saved[$status]['enabled'] : $tpl['enabled'],
            'title' => !empty($saved[$status]['title']) ? $saved[$status]['title'] : $tpl['title'],
            'body' => !empty($saved[$status]['body']) ? $saved[$status]['body'] : $tpl['body'],
        );
    }
    return $merged;
}

add_action('woocommerce_order_status_changed', function ($order_id, $old_status, $new_status) {
    brewmyapp_clean_post_cache($order_id);
    $order = wc_get_order($order_id);
    if (!$order) {
        return;
    }

    $customer_id = $order->get_customer_id();
    if (!$customer_id) {
        return;
    }

    $settings = brewmyapp_get_order_notification_settings();

    if (!isset($settings[$new_status]) || !$settings[$new_status]['enabled']) {
        return;
    }

    $tokens = brewmyapp_get_user_push_tokens($customer_id);
    if (empty($tokens)) {
        return;
    }

    $order_number = $order->get_order_number();
    $tpl = $settings[$new_status];
    $title = str_replace('{order_number}', $order_number, $tpl['title']);
    $body = str_replace('{order_number}', $order_number, $tpl['body']);

    $app_url = trim(get_option('brewmyapp_app_url', 'brewmyapp'));
    if (!$app_url) {
        $app_url = 'brewmyapp';
    }

    $data = array(
        'type' => 'order_status',
        'orderId' => (string) $order_id,
        'status' => $new_status,
        'url' => $app_url . '://order/' . $order_id,
    );

    $result = brewmyapp_send_expo_push($tokens, $title, $body, $data);
    brewmyapp_log_push_notification($title, $body, "customer:{$customer_id}", $result, 'woocommerce');
}, 10, 3);

// ══════════════════════════════════════════════
// BACK-IN-STOCK NOTIFICATIONS
// ══════════════════════════════════════════════

/**
 * Track actual stock-status TRANSITIONS.
 *
 * `woocommerce_product_set_stock_status` fires every time WooCommerce
 * *sets* the status — even when it hasn't changed (e.g. a normal product
 * save while the item is already in-stock). Without transition tracking
 * we'd send spurious "back in stock" notifications on every product save.
 *
 * Approach:
 *  • When a product is set to 'outofstock' or 'onbackorder', we store a
 *    `_brewmyapp_was_outofstock` post-meta flag on the product.
 *  • The same flag is also set in the subscribe endpoint when a user taps
 *    "Notify Me" (the button only appears for out-of-stock products).
 *  • When the status is set to 'instock' AND the flag exists, we know this
 *    is a genuine restock → schedule the push notification and clear the flag.
 */
add_action('woocommerce_product_set_stock_status', function ($product_id, $stock_status, $product) {
    if ($stock_status === 'outofstock' || $stock_status === 'onbackorder') {
        // Mark that this product is/was out of stock
        update_post_meta($product_id, '_brewmyapp_was_outofstock', '1');
        return;
    }

    if ($stock_status !== 'instock') {
        return;
    }

    // Only proceed if the product was genuinely out of stock before
    $was_oos = get_post_meta($product_id, '_brewmyapp_was_outofstock', true);
    if ($was_oos !== '1') {
        return; // Product was already in-stock — skip
    }

    // Clear the flag so the next save won't re-trigger
    delete_post_meta($product_id, '_brewmyapp_was_outofstock');

    // Schedule the notification to fire 1 minute later so caches have time to clear
    if (!wp_next_scheduled('brewmyapp_send_back_in_stock_notification', array((int) $product_id))) {
        wp_schedule_single_event(time() + 60, 'brewmyapp_send_back_in_stock_notification', array((int) $product_id));
    }
}, 10, 3);

/**
 * Scheduled handler: send back-in-stock push notifications.
 * Runs ~1 minute after the stock status changed to 'instock'.
 */
add_action('brewmyapp_send_back_in_stock_notification', function ($product_id) {
    // Re-verify the product is still in stock (in case it was changed back)
    brewmyapp_clean_post_cache($product_id);
    $prod_obj = wc_get_product($product_id);
    if (!$prod_obj || $prod_obj->get_stock_status() !== 'instock') {
        return;
    }

    // Find all users who subscribed for this product
    $users = get_users(array(
        'meta_key' => 'brewmyapp_back_in_stock_subs', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
        'fields' => 'ID',
    ));

    if (empty($users)) {
        return;
    }

    $product_name = $prod_obj->get_name();

    $app_url = trim(get_option('brewmyapp_app_url', 'brewmyapp'));
    if (!$app_url) {
        $app_url = 'brewmyapp';
    }

    // Read merchant-configured back-in-stock toggle + template (set from the
    // dashboard via PUT /bma/v1/push-notification/back-in-stock). Fall back to
    // sensible defaults if nothing has been saved yet.
    $bis_settings = get_option('brewmyapp_back_in_stock_settings', array());
    $bis_enabled = isset($bis_settings['enabled']) ? (bool) $bis_settings['enabled'] : true;
    if (!$bis_enabled) {
        return;
    }
    $title_tpl = !empty($bis_settings['title'])
        ? $bis_settings['title']
        : __('Back in Stock! 🎉', 'brewmyapp-mobile-app-builder-for-woocommerce');
    $body_tpl = !empty($bis_settings['body'])
        ? $bis_settings['body']
        /* translators: %s: product name */
        : __('{product_name} is back in stock. Grab it before it sells out!', 'brewmyapp-mobile-app-builder-for-woocommerce');

    $total_sent = 0;
    $total_failed = 0;

    foreach ($users as $uid) {
        $subs = get_user_meta($uid, 'brewmyapp_back_in_stock_subs', true);
        if (!is_array($subs) || !in_array((int) $product_id, $subs, true)) {
            continue;
        }

        $tokens = brewmyapp_get_user_push_tokens($uid);
        if (empty($tokens)) {
            // Nobody reachable on this account. Close the subscription anyway
            // (as before), but record the interest — an unreachable customer is
            // still a customer who wanted this product.
            brewmyapp_close_back_in_stock_sub($uid, (int) $product_id, $subs);
            continue;
        }

        $title = str_replace('{product_name}', $product_name, $title_tpl);
        $body = str_replace('{product_name}', $product_name, $body_tpl);

        $data = array(
            'type' => 'back_in_stock',
            'productId' => (string) $product_id,
            'url' => $app_url . '://product/' . $product_id,
        );

        $result = brewmyapp_send_expo_push($tokens, $title, $body, $data);
        $total_sent += $result['sent'];
        $total_failed += $result['failed'];

        // One-time notification: move the product out of the waiting list and
        // into the notified ledger, so the BrewmyApp dashboard's Notify Me page
        // can still show who wanted it.
        brewmyapp_close_back_in_stock_sub($uid, (int) $product_id, $subs);
    }

    if ($total_sent > 0 || $total_failed > 0) {
        brewmyapp_log_push_notification(
            str_replace('{product_name}', $product_name, $title_tpl),
            str_replace('{product_name}', $product_name, $body_tpl),
            "product:{$product_id}",
            array('sent' => $total_sent, 'failed' => $total_failed, 'errors' => array()),
            'back_in_stock'
        );
    }
});

// ──────────────────────────────────────────────
// AUTO-LOGIN PAYMENT REDIRECT (/bma-pay/)
// ──────────────────────────────────────────────
add_action('template_redirect', function () {
    if (!isset($_SERVER['REQUEST_URI'])) {
        return;
    }
    $request_path = trim(wp_parse_url(esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])), PHP_URL_PATH), '/');
    if ($request_path !== 'bma-pay') {
        return;
    }

    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Token is validated by brewmyapp_verify_payment_token() below.
    $token = isset($_GET['token']) ? sanitize_text_field(wp_unslash($_GET['token'])) : '';
    if (!$token) {
        wp_die(
            esc_html__('Invalid payment link. Please try again from the app.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            esc_html__('Payment Error', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('response' => 400)
        );
    }

    $data = brewmyapp_verify_payment_token($token);
    if (!$data) {
        wp_die(
            esc_html__('This payment link has expired or is invalid. Please go back to the app and try again.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            esc_html__('Payment Link Expired', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('response' => 403)
        );
    }

    brewmyapp_clean_post_cache($data['order_id']);
    $order = wc_get_order($data['order_id']);
    if (!$order) {
        wp_die(
            esc_html__('Order not found.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            esc_html__('Payment Error', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            array('response' => 404)
        );
    }

    if ($data['customer_id'] && !is_user_logged_in()) {
        $user = get_user_by('ID', $data['customer_id']);
        if ($user) {
            wp_set_current_user($user->ID, $user->user_login); // phpcs:ignore Generic.PHP.ForbiddenFunctions.Discouraged
            // phpcs:ignore Generic.PHP.ForbiddenFunctions.Discouraged
            // nosemgrep: audit.php.wp.security.auth-bypass.wp-set-auth-cookie
            wp_set_auth_cookie($user->ID, false);
            do_action('wp_login', $user->user_login, $user);
        }
    }

    // Set cookie so checkout pages know this is an in-app session.
    // Also set it via PHP header for non-WebView clients.
    setcookie('brewmyapp_app_checkout', '1', array(
        'expires' => time() + 3600,
        'path' => '/',
        'secure' => is_ssl(),
        'httponly' => false,
        'samesite' => 'Lax',
    ));

    // WKWebView (iOS) may not persist cookies from 302 redirect responses,
    // so we output a small HTML page that sets the cookie via JavaScript
    // before navigating to the payment page. This ensures the cookie is
    // available for Store API AJAX calls made by WC Block Checkout JS.
    $redirect_url = esc_url_raw($order->get_checkout_payment_url());
    status_header(200);
    header('Content-Type: text/html; charset=utf-8');
    header('Cache-Control: no-store, no-cache, must-revalidate');
    // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- URL escaped above, HTML is static.
    echo '<!DOCTYPE html><html><head><meta charset="utf-8">';
    echo '<meta name="viewport" content="width=device-width,initial-scale=1">';
    printf('<%1$s>document.cookie="brewmyapp_app_checkout=1;path=/;max-age=3600%2$s;samesite=lax";window.location.replace(%3$s);</%1$s>', 'script', (is_ssl() ? ';secure' : ''), wp_json_encode($redirect_url));
    echo '</head><body></body></html>';
    exit;
});

// ──────────────────────────────────────────────
// REWRITE RULES FOR /bma-pay/
// ──────────────────────────────────────────────
add_action('init', function () {
    add_rewrite_rule('^bma-pay/?$', 'index.php?brewmyapp_pay=1', 'top');
});

add_filter('query_vars', function ($vars) {
    $vars[] = 'brewmyapp_pay';
    return $vars;
});

// ──────────────────────────────────────────────
// ──────────────────────────────────────────────
// PREVENT ORPHAN DRAFT ORDERS IN APP SESSIONS
// WooCommerce Block Checkout SSR calls the Store API checkout endpoint during
// page rendering, which triggers create_or_update_draft_order(). If the
// customer's WC session cart has items, this creates an unwanted checkout-draft.
// By clearing the cart before the page renders, the checkout endpoint finds an
// empty cart and skips draft creation entirely.
// The order-pay page displays items from the WC_Order object (not the cart),
// so clearing the cart has no impact on the payment form.
// ──────────────────────────────────────────────
add_action('template_redirect', function () {
    if (empty($_COOKIE['brewmyapp_app_checkout'])) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
        return;
    }
    if (!function_exists('is_checkout') || !is_checkout()) {
        return;
    }
    if (function_exists('WC') && WC()->cart) {
        WC()->cart->empty_cart();
    }
}, 5);

// MOBILE CHECKOUT BEAUTIFIER
// When the checkout / pay-for-order page is opened from the
// BrewmyApp mobile WebView, inject a clean, modern CSS skin
// that hides site chrome and styles the payment form.
// ──────────────────────────────────────────────
add_action('wp_enqueue_scripts', function () {
    if (empty($_COOKIE['brewmyapp_app_checkout'])) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
        return;
    }
    if (!function_exists('is_checkout') || !is_checkout()) {
        return;
    }

    wp_enqueue_style(
        'bma-mobile-checkout',
        plugins_url('assets/css/mobile-checkout.css', __FILE__),
        array(),
        BREWMYAPP_VERSION
    );

    wp_enqueue_script(
        'bma-mobile-checkout-js',
        plugins_url('assets/js/mobile-checkout.js', __FILE__),
        array(),
        BREWMYAPP_VERSION,
        true  // load in footer
    );
}, 999);

// Add the CSS class to <body> so our styles scope correctly
add_filter('body_class', function ($classes) {
    if (!empty($_COOKIE['brewmyapp_app_checkout']) && function_exists('is_checkout') && is_checkout()) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
        $classes[] = 'bma-mobile-checkout';
    }
    return $classes;
});

// ══════════════════════════════════════════════
// OTP LOGIN ON WOOCOMMERCE MY ACCOUNT PAGE
// ══════════════════════════════════════════════

/**
 * Get the OTP login mode. Returns 'disabled', 'both', or 'otp_only'.
 * Handles legacy 'yes'/'no' values from the old boolean setting.
 */
function brewmyapp_get_otp_login_mode()
{
    $mode = get_option('brewmyapp_otp_login_mode', 'disabled');
    // Migrate legacy values
    if ($mode === 'yes') {
        return 'both';
    }
    if ($mode === 'no') {
        return 'disabled';
    }
    return in_array($mode, array('disabled', 'both', 'otp_only'), true) ? $mode : 'disabled';
}

/**
 * In "otp_only" mode, hide the default WooCommerce password login form
 * and show OTP login form directly at the top.
 */
add_action('woocommerce_before_customer_login_form', function () {
    if (brewmyapp_get_otp_login_mode() !== 'otp_only') {
        return;
    }
    // Wrap the default login form in a hidden div so WC still renders it
    // (needed for registration form on the other column) but hide the login column.

    echo '<div id="bma-otp-only-login">';

    $has_whatsapp = (bool) (defined('BREWMYAPP_WA_TOKEN') ? BREWMYAPP_WA_TOKEN : get_option('brewmyapp_wa_token'));
    ?>
    <h2><?php esc_html_e('Login', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?></h2>
    <div id="bma-otp-login-standalone">
        <div id="bma-otp-standalone-step-method">
            <button type="button" class="button bma-otp-btn" data-method="email">
                <span class="dashicons dashicons-email-alt"></span>
                <?php esc_html_e('Login with Email OTP', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?>
            </button>
            <?php if ($has_whatsapp): ?>
                <button type="button" class="button bma-otp-btn bma-otp-btn-wa" data-method="whatsapp">
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"
                        style="vertical-align:middle;margin-right:4px;">
                        <path
                            d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z" />
                    </svg>
                    <?php esc_html_e('Login with WhatsApp OTP', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?>
                </button>
            <?php endif; ?>
        </div>

        <div id="bma-otp-standalone-step-send" style="display:none;">
            <p class="bma-otp-label" id="bma-otp-standalone-send-label"></p>
            <p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
                <label for="bma-otp-standalone-identifier"
                    id="bma-otp-standalone-identifier-label"><?php esc_html_e('Email address', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?></label>
                <input type="text" class="woocommerce-Input woocommerce-Input--text input-text"
                    id="bma-otp-standalone-identifier" autocomplete="email" />
            </p>
            <button type="button" class="button bma-otp-btn bma-otp-btn-primary" id="bma-otp-standalone-send-btn">
                <?php esc_html_e('Send OTP', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?>
            </button>
            <p class="bma-otp-back"><a href="#" id="bma-otp-standalone-back-method">&larr;
                    <?php esc_html_e('Back', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?></a></p>
            <p class="bma-otp-message" id="bma-otp-standalone-send-msg" style="display:none;"></p>
        </div>

        <div id="bma-otp-standalone-step-verify" style="display:none;">
            <p class="bma-otp-label" id="bma-otp-standalone-verify-label"></p>
            <p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
                <label
                    for="bma-otp-standalone-code"><?php esc_html_e('Enter OTP', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?></label>
                <input type="text" class="woocommerce-Input woocommerce-Input--text input-text" id="bma-otp-standalone-code"
                    maxlength="6" inputmode="numeric" pattern="[0-9]*" autocomplete="one-time-code" placeholder="123456" />
            </p>
            <button type="button" class="button bma-otp-btn bma-otp-btn-primary" id="bma-otp-standalone-verify-btn">
                <?php esc_html_e('Verify & Login', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?>
            </button>
            <p class="bma-otp-resend">
                <span id="bma-otp-standalone-timer"></span>
                <a href="#" id="bma-otp-standalone-resend-link"
                    style="display:none;"><?php esc_html_e('Resend OTP', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?></a>
            </p>
            <p class="bma-otp-back"><a href="#" id="bma-otp-standalone-back-send">&larr;
                    <?php esc_html_e('Change', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?></a></p>
            <p class="bma-otp-message" id="bma-otp-standalone-verify-msg" style="display:none;"></p>
        </div>
    </div>
    </div>
    <?php
});

/**
 * Inject OTP login form into WooCommerce My Account login page.
 * Uses the `woocommerce_login_form_end` hook to add an "Or login with OTP"
 * section below the standard password login form.
 */
add_action('woocommerce_login_form_end', function () {
    $mode = brewmyapp_get_otp_login_mode();
    if ($mode !== 'both') {
        return;
    }

    $has_whatsapp = (bool) (defined('BREWMYAPP_WA_TOKEN') ? BREWMYAPP_WA_TOKEN : get_option('brewmyapp_wa_token'));
    ?>
    <div class="bma-otp-divider">
        <span><?php esc_html_e('or', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?></span>
    </div>
    <div id="bma-otp-login">
        <div id="bma-otp-step-method">
            <button type="button" class="button bma-otp-btn" data-method="email">
                <span class="dashicons dashicons-email-alt"></span>
                <?php esc_html_e('Login with Email OTP', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?>
            </button>
            <?php if ($has_whatsapp): ?>
                <button type="button" class="button bma-otp-btn bma-otp-btn-wa" data-method="whatsapp">
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"
                        style="vertical-align:middle;margin-right:4px;">
                        <path
                            d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z" />
                    </svg>
                    <?php esc_html_e('Login with WhatsApp OTP', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?>
                </button>
            <?php endif; ?>
        </div>

        <div id="bma-otp-step-send" style="display:none;">
            <p class="bma-otp-label" id="bma-otp-send-label"></p>
            <p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
                <label for="bma-otp-identifier"
                    id="bma-otp-identifier-label"><?php esc_html_e('Email address', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?></label>
                <input type="text" class="woocommerce-Input woocommerce-Input--text input-text" id="bma-otp-identifier"
                    autocomplete="email" />
            </p>
            <button type="button" class="button bma-otp-btn bma-otp-btn-primary" id="bma-otp-send-btn">
                <?php esc_html_e('Send OTP', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?>
            </button>
            <p class="bma-otp-back"><a href="#" id="bma-otp-back-method">&larr;
                    <?php esc_html_e('Back', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?></a></p>
            <p class="bma-otp-message" id="bma-otp-send-msg" style="display:none;"></p>
        </div>

        <div id="bma-otp-step-verify" style="display:none;">
            <p class="bma-otp-label" id="bma-otp-verify-label"></p>
            <p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
                <label
                    for="bma-otp-code"><?php esc_html_e('Enter OTP', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?></label>
                <input type="text" class="woocommerce-Input woocommerce-Input--text input-text" id="bma-otp-code"
                    maxlength="6" inputmode="numeric" pattern="[0-9]*" autocomplete="one-time-code" placeholder="123456" />
            </p>
            <button type="button" class="button bma-otp-btn bma-otp-btn-primary" id="bma-otp-verify-btn">
                <?php esc_html_e('Verify & Login', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?>
            </button>
            <p class="bma-otp-resend">
                <span id="bma-otp-timer"></span>
                <a href="#" id="bma-otp-resend-link"
                    style="display:none;"><?php esc_html_e('Resend OTP', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?></a>
            </p>
            <p class="bma-otp-back"><a href="#" id="bma-otp-back-send">&larr;
                    <?php esc_html_e('Change', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?></a></p>
            <p class="bma-otp-message" id="bma-otp-verify-msg" style="display:none;"></p>
        </div>
    </div>
    <?php
});

/**
 * Enqueue frontend OTP login assets on My Account page.
 */
add_action('wp_enqueue_scripts', function () {
    $mode = brewmyapp_get_otp_login_mode();
    if ($mode === 'disabled') {
        return;
    }
    if (!function_exists('is_account_page') || !is_account_page()) {
        return;
    }
    // Only on the login form (not when user is already logged in)
    if (is_user_logged_in()) {
        return;
    }

    wp_enqueue_style(
        'brewmyapp-otp-login',
        plugins_url('assets/css/otp-login.css', __FILE__),
        array(),
        BREWMYAPP_VERSION
    );

    if ($mode === 'otp_only') {
        wp_add_inline_style('brewmyapp-otp-login', '.woocommerce-form-login-toggle,.woocommerce form.woocommerce-form-login{display:none!important;}');
    }

    wp_enqueue_script(
        'brewmyapp-otp-login',
        plugins_url('assets/js/otp-login.js', __FILE__),
        array(),
        BREWMYAPP_VERSION,
        true
    );

    wp_localize_script('brewmyapp-otp-login', 'brewmyappOtp', array(
        'ajaxUrl' => admin_url('admin-ajax.php'),
        'nonce' => wp_create_nonce('brewmyapp_otp_login_nonce'),
        'mode' => $mode,
        'i18n' => array(
            'emailLabel' => __('Email address', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'phoneLabel' => __('Phone number (with country code)', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'sendingOtp' => __('Sending…', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'sendOtp' => __('Send OTP', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'verifying' => __('Verifying…', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'verifyLogin' => __('Verify & Login', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'otpSent' => __('OTP sent! Check your inbox.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'otpSentWa' => __('OTP sent via WhatsApp!', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'resendIn' => __('Resend in %ds', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'emailSendDesc' => __('We\'ll send a 6-digit code to your email.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'whatsappSendDesc' => __('We\'ll send a 6-digit code to your WhatsApp.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'emailVerifyDesc' => __('Enter the 6-digit code sent to your email.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            'whatsappVerifyDesc' => __('Enter the 6-digit code sent to your WhatsApp.', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        ),
    ));
});

/**
 * AJAX: Send OTP for website login (email or WhatsApp).
 * Reuses existing bma_* OTP helper functions.
 */
add_action('wp_ajax_nopriv_brewmyapp_web_otp_request', 'brewmyapp_web_otp_request_handler');
add_action('wp_ajax_brewmyapp_web_otp_request', 'brewmyapp_web_otp_request_handler');
function brewmyapp_web_otp_request_handler()
{
    check_ajax_referer('brewmyapp_otp_login_nonce', 'nonce');

    if (brewmyapp_get_otp_login_mode() === 'disabled') {
        wp_send_json_error(array('message' => __('OTP login is not enabled.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 403);
    }

    $method = sanitize_text_field(wp_unslash($_POST['method'] ?? ''));
    $identifier = sanitize_text_field(wp_unslash($_POST['identifier'] ?? ''));

    if ($method === 'email') {
        $email = sanitize_email($identifier);
        if (!$email) {
            wp_send_json_error(array('message' => __('Please enter a valid email address.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 400);
        }

        $user = get_user_by('email', $email);
        if (!$user) {
            wp_send_json_error(array('message' => __('No account found with this email. Please register first.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 404);
        }

        $rate_key = brewmyapp_otp_rate_key($email);
        if (get_transient($rate_key)) {
            wp_send_json_error(array('message' => __('Please wait before requesting another OTP.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 429);
        }

        $otp = brewmyapp_generate_numeric_otp(6);
        $otp_key = brewmyapp_otp_email_key($email);
        set_transient($otp_key, array(
            'hash' => brewmyapp_hash_otp($email, $otp),
            'created' => time(),
            'tries' => 0,
        ), 10 * MINUTE_IN_SECONDS);

        $sent = brewmyapp_send_otp_email($email, $otp);
        if (!$sent) {
            delete_transient($otp_key);
            wp_send_json_error(array('message' => __('Unable to send OTP email. Please try again or contact support.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 500);
        }

        set_transient($rate_key, 1, 60);
        wp_send_json_success(array('message' => __('OTP sent to your email.', 'brewmyapp-mobile-app-builder-for-woocommerce')));

    } elseif ($method === 'whatsapp') {
        $phone = brewmyapp_normalize_phone($identifier);
        if (!$phone) {
            wp_send_json_error(array('message' => __('Please enter a valid phone number with country code.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 400);
        }

        $user = brewmyapp_find_user_by_phone($phone);
        if (!$user) {
            wp_send_json_error(array('message' => __('No account found with this phone number.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 404);
        }

        $rate_key = brewmyapp_otp_phone_rate_key($phone);
        if (get_transient($rate_key)) {
            wp_send_json_error(array('message' => __('Please wait before requesting another OTP.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 429);
        }

        $otp = brewmyapp_generate_numeric_otp(6);
        $otp_key = brewmyapp_otp_phone_key($phone);
        set_transient($otp_key, array(
            'hash' => brewmyapp_hash_otp_phone($phone, $otp),
            'created' => time(),
            'tries' => 0,
        ), 10 * MINUTE_IN_SECONDS);

        $result = brewmyapp_send_whatsapp_otp($phone, $otp);
        if (is_wp_error($result)) {
            delete_transient($otp_key);
            wp_send_json_error(array('message' => __('Failed to send WhatsApp OTP. Please try again.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 502);
        }

        set_transient($rate_key, 1, 60);
        wp_send_json_success(array('message' => __('OTP sent to your WhatsApp.', 'brewmyapp-mobile-app-builder-for-woocommerce')));

    } else {
        wp_send_json_error(array('message' => __('Invalid OTP method.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 400);
    }
}

/**
 * AJAX: Verify OTP and log the user in (sets WP auth cookie).
 */
add_action('wp_ajax_nopriv_brewmyapp_web_otp_verify', 'brewmyapp_web_otp_verify_handler');
add_action('wp_ajax_brewmyapp_web_otp_verify', 'brewmyapp_web_otp_verify_handler');
function brewmyapp_web_otp_verify_handler()
{
    check_ajax_referer('brewmyapp_otp_login_nonce', 'nonce');

    if (brewmyapp_get_otp_login_mode() === 'disabled') {
        wp_send_json_error(array('message' => __('OTP login is not enabled.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 403);
    }

    $method = sanitize_text_field(wp_unslash($_POST['method'] ?? ''));
    $identifier = sanitize_text_field(wp_unslash($_POST['identifier'] ?? ''));
    $otp = sanitize_text_field(wp_unslash($_POST['otp'] ?? ''));

    if (!$identifier || !$otp || !$method) {
        wp_send_json_error(array('message' => __('All fields are required.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 400);
    }

    if ($method === 'email') {
        $email = sanitize_email($identifier);
        $otp_key = brewmyapp_otp_email_key($email);
        $record = get_transient($otp_key);

        if (!$record || empty($record['hash'])) {
            wp_send_json_error(array('message' => __('OTP expired. Please request a new one.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 401);
        }

        if (((int) ($record['tries'] ?? 0)) >= 5) {
            delete_transient($otp_key);
            wp_send_json_error(array('message' => __('Too many attempts. Please request a new OTP.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 429);
        }

        if (!hash_equals((string) $record['hash'], brewmyapp_hash_otp($email, $otp))) {
            $record['tries'] = ((int) ($record['tries'] ?? 0)) + 1;
            set_transient($otp_key, $record, 10 * MINUTE_IN_SECONDS);
            wp_send_json_error(array('message' => __('Invalid OTP. Please try again.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 401);
        }

        delete_transient($otp_key);
        $user = get_user_by('email', $email);

    } elseif ($method === 'whatsapp') {
        $phone = brewmyapp_normalize_phone($identifier);
        $otp_key = brewmyapp_otp_phone_key($phone);
        $record = get_transient($otp_key);

        if (!$record || empty($record['hash'])) {
            wp_send_json_error(array('message' => __('OTP expired. Please request a new one.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 401);
        }

        if (((int) ($record['tries'] ?? 0)) >= 5) {
            delete_transient($otp_key);
            wp_send_json_error(array('message' => __('Too many attempts. Please request a new OTP.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 429);
        }

        if (!hash_equals((string) $record['hash'], brewmyapp_hash_otp_phone($phone, $otp))) {
            $record['tries'] = ((int) ($record['tries'] ?? 0)) + 1;
            set_transient($otp_key, $record, 10 * MINUTE_IN_SECONDS);
            wp_send_json_error(array('message' => __('Invalid OTP. Please try again.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 401);
        }

        delete_transient($otp_key);
        $user = brewmyapp_find_user_by_phone($phone);

    } else {
        wp_send_json_error(array('message' => __('Invalid method.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 400);
        return; // Never reached, but makes static analysis happy.
    }

    if (!$user) {
        wp_send_json_error(array('message' => __('User not found.', 'brewmyapp-mobile-app-builder-for-woocommerce')), 404);
    }

    // Log the user in via WP auth cookie (same as WC login form).
    wp_set_current_user($user->ID, $user->user_login); // phpcs:ignore Generic.PHP.ForbiddenFunctions.Discouraged
    // phpcs:ignore Generic.PHP.ForbiddenFunctions.Discouraged
    // nosemgrep: audit.php.wp.security.auth-bypass.wp-set-auth-cookie
    wp_set_auth_cookie($user->ID, true);
    do_action('wp_login', $user->user_login, $user);

    wp_send_json_success(array(
        'message' => __('Login successful! Redirecting…', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        'redirect' => wc_get_page_permalink('myaccount'),
    ));
}

// ══════════════════════════════════════════════
// ADMIN PAGES — v2.0 Modern UI
// ══════════════════════════════════════════════

add_action('admin_menu', function () {
    // Top-level "BrewmyApp" renders the Connect panel — the merchant either
    // sees the "open BrewmyApp in a new tab to attach this store" button
    // (fresh install) or the "Connected — open dashboard" view once they've
    // attached. We deliberately don't embed brewmyapp.io in an iframe here
    // anymore: third-party cookie blocking in Safari/Chrome made sign-in
    // unreliable. The new-tab handoff is the only path that works everywhere.
    add_menu_page(
        'BrewmyApp',
        'BrewmyApp',
        'manage_options',
        'brewmyapp',
        'brewmyapp_render_welcome_page',
        'data:image/svg+xml;base64,' . base64_encode('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><text x="50%" y="55%" dominant-baseline="central" text-anchor="middle" fill="black" font-size="15" font-weight="bold" font-family="Arial, sans-serif">B</text></svg>'),
        30
    );

    // Register brewmyapp-welcome as a submenu so legacy links
    // (page=brewmyapp-welcome) keep resolving. We hide the submenu items
    // via CSS below — destroying $submenu['brewmyapp'] outright would break
    // WordPress's capability lookup and throw "not allowed" on the page.
    add_submenu_page('brewmyapp', 'BrewmyApp', 'BrewmyApp', 'manage_options', 'brewmyapp-welcome', 'brewmyapp_render_welcome_page');
});

// Hide the BrewmyApp submenu items globally so the sidebar shows a single
// top-level "BrewmyApp" link. We keep the submenu pages registered so
// ?page=brewmyapp-welcome (and any other slugs we add later) resolve
// correctly without triggering "not allowed".
add_action('admin_head', function () {
    echo '<style>#adminmenu li#toplevel_page_brewmyapp .wp-submenu{display:none !important;}#adminmenu li#toplevel_page_brewmyapp.wp-has-submenu>a.menu-top:after{display:none !important;}</style>';
});

/**
 * ──────────────────────────────────────────────
 * CLEAN UI — HIDE THIRD-PARTY NOTICES
 * ──────────────────────────────────────────────
 * Hides annoying theme/plugin banners and notices on BrewmyApp pages
 * to provide a clean, focused app-builder experience.
 */
add_action('admin_head', function () {
    global $plugin_page;
    if (!isset($plugin_page) || stripos((string) $plugin_page, 'brewmyapp') === false) {
        return;
    }

    // 1) Remove standard WordPress notice actions
    remove_all_actions('admin_notices');
    remove_all_actions('all_admin_notices');
    remove_all_actions('network_admin_notices');
    remove_all_actions('user_admin_notices');

    // 2) Inject CSS to hide notices that might be added via unconventional hooks or JS
    echo '<style>
        /* Hide all standard WordPress notices and nags */
        .notice, .updated, .error, .update-nag, .is-dismissible, .wp-core-ui .notice { 
            display: none !important; 
        }
        /* Specifically target some common theme/plugin banner containers */
        #wpbody-content > .notice, 
        #wpbody-content > .updated, 
        #wpbody-content > .error,
        .popularis-notice, 
        .aioseo-notice { 
            display: none !important; 
        }
        /* Ensure our own help banner remains visible if it uses a custom class */
        .bma-help-banner { 
            display: flex !important; 
        }
    </style>';
}, 1);

add_action('admin_enqueue_scripts', function ($hook) {
    if (stripos((string) $hook, 'brewmyapp') === false) {
        return;
    }
    wp_enqueue_style('brewmyapp-admin-css', plugins_url('assets/css/admin.css', __FILE__), array(), BREWMYAPP_VERSION);
    wp_enqueue_script('brewmyapp-admin-js', plugins_url('assets/js/admin.js', __FILE__), array(), BREWMYAPP_VERSION, true);
    wp_localize_script('brewmyapp-admin-js', 'brewmyappAdmin', array(
        'restUrl' => esc_url_raw(rest_url()),
        'nonce' => wp_create_nonce('wp_rest'),
    ));
});

// ── Frontend: Cart-sync script ───────────────
// Refreshes WooCommerce mini-cart fragments when user returns to
// the browser tab, picking up changes made from the mobile app.
// Only loaded for logged-in users (guests don't have cross-device carts).
add_action('wp_enqueue_scripts', function () {
    if (!class_exists('WooCommerce') || !is_user_logged_in()) {
        return;
    }
    wp_enqueue_script(
        'bma-cart-sync',
        plugins_url('assets/js/cart-sync.js', __FILE__),
        array('jquery'),
        BREWMYAPP_VERSION,
        true
    );
});

// ── Helper: Page Header ──────────────────────
function brewmyapp_page_header($title, $subtitle = '')
{
    echo '<div class="bma-wrap">';

    // Top help banner — shown on every plugin page
    echo '<div class="bma-help-banner" style="display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;background:linear-gradient(90deg,#eff6ff,#f5f3ff);border:1px solid #dbeafe;border-radius:10px;padding:10px 14px;margin:0 0 16px;">';
    echo '<div style="display:flex;align-items:center;gap:10px;font-size:13px;color:#1e3a8a;">';
    echo '<span class="dashicons dashicons-sos" style="font-size:18px;width:18px;height:18px;color:#3b82f6;"></span>';
    echo '<span>' . wp_kses_post(__('<strong>Need a hand?</strong> Our team can help you set up BrewmyApp and launch your mobile app.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</span>';
    echo '</div>';
    echo '<a href="' . esc_url('https://www.brewmyapp.io/contact') . '" target="_blank" rel="noopener" style="display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:600;color:#fff;background:#18181b;text-decoration:none;padding:8px 14px;border-radius:8px;">' . esc_html__('Contact Us', 'brewmyapp-mobile-app-builder-for-woocommerce') . ' &rarr;</a>';
    echo '</div>';

    echo '<div class="bma-header">';
    echo '<div class="bma-logo"><img src="' . esc_url(plugins_url('assets/images/brewmyapp-logo.png', __FILE__)) . '" alt="BrewmyApp" /></div>';
    echo '<div class="bma-header-text">';
    echo '<h1>' . esc_html($title) . '</h1>';
    if ($subtitle) {
        echo '<p>' . esc_html($subtitle) . '</p>';
    }
    echo '</div>';
    echo '<div class="bma-header-actions" style="margin-left:auto;display:flex;align-items:center;gap:8px;flex-wrap:wrap;">';
    brewmyapp_render_language_switcher();
    echo '<a href="' . esc_url('https://www.brewmyapp.io/contact') . '" target="_blank" rel="noopener" class="bma-header-link" style="display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:600;color:#3f3f46;text-decoration:none;padding:8px 12px;border:1px solid #e4e4e7;border-radius:8px;background:#fff;"><span class="dashicons dashicons-sos" style="font-size:16px;width:16px;height:16px;"></span>' . esc_html__('Support', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '</div>';
    echo '</div>';
}

function brewmyapp_page_footer()
{
    echo '<div class="bma-footer">';
    echo esc_html('BrewmyApp v' . BREWMYAPP_VERSION) . ' &middot; <a href="' . esc_url('https://www.brewmyapp.io') . '" target="_blank" rel="noopener">brewmyapp.io</a> &middot; <a href="' . esc_url('https://www.brewmyapp.io/contact') . '" target="_blank" rel="noopener">' . esc_html__('Support', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '</div>';
    echo '</div>'; // close .bma-wrap
}

// ══════════════════════════════════════════════
// EMBEDDED APP PAGE — iframe into brewmyapp.io
// ══════════════════════════════════════════════

/**
 * Frontend URL used for embedding. Can be overridden by defining
 * BREWMYAPP_FRONTEND_URL in wp-config.php (useful for local dev).
 */
function brewmyapp_get_frontend_url()
{
    if (defined('BREWMYAPP_FRONTEND_URL') && BREWMYAPP_FRONTEND_URL) {
        return rtrim((string) BREWMYAPP_FRONTEND_URL, '/');
    }
    $opt = get_option('brewmyapp_frontend_url');
    if (is_string($opt) && $opt !== '') {
        return rtrim($opt, '/');
    }
    return 'https://www.brewmyapp.io';
}

/**
 * Mint (or reuse) WooCommerce REST API keys scoped to the BrewmyApp app.
 * Returns a plaintext { consumer_key, consumer_secret } pair the first
 * time it runs; on subsequent calls returns null because the secret can't
 * be retrieved from WC's DB (only its hash is stored). The caller is
 * responsible for shipping the plaintext to the BrewmyApp backend the
 * first time, since this is the only chance.
 *
 * Stores only the WC key_id locally — never the plaintext secret — and
 * uses it to detect "already provisioned" without persisting credentials
 * in WP options where another plugin could read them.
 *
 * Requires WooCommerce to be active (the plugin already declares this
 * as a hard dependency in the header).
 */
function brewmyapp_provision_wc_keys()
{
    global $wpdb;

    if (!class_exists('WooCommerce') || !function_exists('wc_rand_hash')) {
        return null;
    }

    $existing_key_id = (int) get_option('brewmyapp_wc_key_id', 0);
    if ($existing_key_id > 0) {
        // Already provisioned — confirm the row is still there. If WC admin
        // deleted it manually, clear our marker + cached creds so the next
        // request mints a fresh pair.
        $still_there = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT key_id FROM {$wpdb->prefix}woocommerce_api_keys WHERE key_id = %d",
            $existing_key_id
        ));
        if ($still_there) {
            // Return cached plaintext creds so we can re-deliver them on
            // every iframe render. WC stores consumer_key HMAC'd and the
            // plaintext is unrecoverable after first insert, so if we
            // don't cache it ourselves the backend never learns the keys
            // unless they happened to verify the very first nonce.
            $cached_key = get_option('brewmyapp_wc_consumer_key', '');
            $cached_secret = get_option('brewmyapp_wc_consumer_secret', '');
            if ($cached_key && $cached_secret) {
                return array(
                    'consumer_key' => (string) $cached_key,
                    'consumer_secret' => (string) $cached_secret,
                );
            }
            // Stuck state: a key was provisioned by an earlier plugin
            // version that didn't cache plaintext, so we have no way to
            // re-deliver it. Drop the WC row and fall through to fresh
            // provision below. The backend overwrites consumerKey /
            // consumerSecret on every connect, so legitimate installs
            // simply pick up the new pair on the next iframe render.
            $wpdb->delete(
                $wpdb->prefix . 'woocommerce_api_keys',
                array('key_id' => $existing_key_id),
                array('%d')
            );
        }
        delete_option('brewmyapp_wc_key_id');
        delete_option('brewmyapp_wc_consumer_key');
        delete_option('brewmyapp_wc_consumer_secret');
    }

    $user_id = get_current_user_id();
    if (!$user_id || !user_can($user_id, 'manage_woocommerce')) {
        return null;
    }

    $consumer_key = 'ck_' . wc_rand_hash();
    $consumer_secret = 'cs_' . wc_rand_hash();

    $inserted = $wpdb->insert(
        $wpdb->prefix . 'woocommerce_api_keys',
        array(
            'user_id' => $user_id,
            'description' => 'BrewmyApp Mobile App',
            'permissions' => 'read_write',
            // WC stores the consumer_key as an HMAC for lookup; the
            // consumer_secret is stored as plaintext for HTTP Basic auth.
            'consumer_key' => hash_hmac('sha256', $consumer_key, 'wc-api'),
            'consumer_secret' => $consumer_secret,
            'truncated_key' => substr($consumer_key, -7),
        ),
        array('%d', '%s', '%s', '%s', '%s', '%s')
    );

    if (!$inserted) {
        return null;
    }

    update_option('brewmyapp_wc_key_id', (int) $wpdb->insert_id, false);
    // Cache plaintext so we can re-deliver these creds on every iframe
    // render — WC only keeps the consumer_key HMAC'd, and the BrewmyApp
    // backend needs to see plaintext to store its own encrypted copy.
    // Without this cache, only the very first nonce-verify call carries
    // the keys; if that one races with sign-up / expires, the store is
    // permanently created without API access.
    update_option('brewmyapp_wc_consumer_key', $consumer_key, false);
    update_option('brewmyapp_wc_consumer_secret', $consumer_secret, false);

    return array(
        'consumer_key' => $consumer_key,
        'consumer_secret' => $consumer_secret,
    );
}

/**
 * Render the "Connect to BrewmyApp" panel (top-level menu).
 *
 * Two states:
 *   • Not connected: mint a fresh nonce + WC keys, render a big "Connect"
 *     button that opens brewmyapp.io/connect/wordpress in a new tab. On
 *     first load the JS attempts to auto-open the tab (may be blocked by
 *     a popup blocker — the button is the always-visible fallback). After
 *     the merchant signs in there, the BrewmyApp backend callback-verifies
 *     the nonce against /wp-json/bma/v1/verify-iframe-nonce, which sets
 *     `brewmyapp_connected_at` so the next visit shows the connected view.
 *   • Connected: skip the nonce; show "Connected — open BrewmyApp" linking
 *     to /dashboard/stores in a new tab, plus a "Reconnect" link in case
 *     the merchant needs to attach the store to a different account.
 *
 * We deliberately do NOT iframe brewmyapp.io anymore — the third-party
 * cookie problem (MissingCSRF in Safari/Chrome strict mode) made every
 * embedded sign-in path unreliable. The new-tab handoff is first-party
 * for the merchant's browser, so cookies/CSRF behave normally.
 */
function brewmyapp_render_embedded_app_page()
{
    if (!current_user_can('manage_options')) {
        wp_die(esc_html__('You do not have permission to access this page.', 'brewmyapp-mobile-app-builder-for-woocommerce'));
    }

    $frontend = brewmyapp_get_frontend_url();

    // Refresh button / "I've connected" → force a backend re-check (auto-detect)
    // and drop the cached dashboard data so subscription/preview re-fetch.
    $force_check = !empty($_GET['bma_refresh']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    if ($force_check) {
        delete_transient('bma_plugin_api_' . md5('/subscription'));
        delete_transient('bma_plugin_api_' . md5('/preview'));
        delete_transient('bma_plugin_api_' . md5('/status'));
    }

    // ── Already connected view ────────────────────────────────────
    // Auto-detect via the backend so an already-attached store is bypassed
    // straight to the dashboard, even without the local flag.
    if (brewmyapp_is_connected($force_check) && empty($_GET['reconnect'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        // Handle the OTP-bypass form (posts back to this page) before rendering.
        brewmyapp_handle_otp_bypass_save();
        brewmyapp_render_connected_dashboard();
        return;
    }

    // ── Not connected — mint a connect URL ────────────────────────
    $user = wp_get_current_user();
    if (!$user || !$user->exists() || !$user->user_email) {
        echo '<div class="notice notice-error"><p>' . esc_html__('Could not determine the current WordPress user.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p></div>';
        return;
    }

    $nonce = bin2hex(random_bytes(32));
    $payload = array(
        'email' => strtolower(trim($user->user_email)),
        'first_name' => (string) $user->first_name,
        'last_name' => (string) $user->last_name,
        'username' => (string) $user->user_login,
        'display_name' => (string) $user->display_name,
        'site_name' => (string) get_bloginfo('name'),
        'phone' => (string) get_option('brewmyapp_store_owner_phone', ''),
        'issued_at' => time(),
    );

    // First-connect-only: ship WC REST API keys in the nonce payload so the
    // backend can persist them and start syncing immediately. Subsequent
    // connects skip this — keys are only deliverable once.
    $wc_keys = brewmyapp_provision_wc_keys();
    if (is_array($wc_keys) && !empty($wc_keys['consumer_key'])) {
        $payload['consumer_key'] = $wc_keys['consumer_key'];
        $payload['consumer_secret'] = $wc_keys['consumer_secret'];
        $payload['key_permissions'] = 'read_write';
    }

    // Per-store shared secret: minted once, re-sent on every connect. The
    // backend persists it so this plugin can later read its own subscription /
    // preview data from /api/plugin/wp/* by sending it as X-BMA-Store-Secret.
    $payload['plugin_secret'] = brewmyapp_get_plugin_secret();

    // 30 min TTL — headroom to complete email/Google sign-up (incl. the OAuth
    // round-trip and profile completion) before the nonce expires.
    set_transient('bma_iframe_nonce_' . $nonce, $payload, 1800);

    $site_url = home_url('/');
    $return_url = admin_url('admin.php?page=brewmyapp');

    // email + siteName are display-only hints for the confirmation page (so it
    // can greyout the email and decide "create" vs "sign in" without touching
    // the single-use nonce). The authoritative identity still comes from the
    // backend's nonce verification — these are never trusted for auth.
    $connect_url = $frontend
        . '/connect/wordpress'
        . '?siteUrl=' . rawurlencode(rtrim($site_url, '/'))
        . '&nonce=' . rawurlencode($nonce)
        . '&email=' . rawurlencode(strtolower(trim($user->user_email)))
        . '&siteName=' . rawurlencode((string) get_bloginfo('name'))
        . '&returnUrl=' . rawurlencode($return_url);

    // Auto-open the new tab on first activation. The transient is set by
    // register_activation_hook and consumed here once so we don't keep
    // popping a new tab on every page revisit.
    $auto_open = (bool) get_transient('brewmyapp_auto_open_connect');
    if ($auto_open) {
        delete_transient('brewmyapp_auto_open_connect');
    }
    ?>
    <div class="bma-connect-wrap">
        <div class="bma-connect-card">
            <div class="bma-connect-badge">
                <?php echo esc_html__('One-step setup', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?>
            </div>
            <h1><?php echo esc_html__('Connect this store to BrewmyApp', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?>
            </h1>
            <p><?php echo esc_html__("We'll open BrewmyApp in a new tab so you can sign in (or create your account). Once you're signed in, this store is added to your account automatically.", 'brewmyapp-mobile-app-builder-for-woocommerce'); ?>
            </p>
            <a id="bma-connect-btn" class="bma-connect-btn" href="<?php echo esc_url($connect_url); ?>" target="_blank"
                rel="noopener">
                <?php echo esc_html__('Connect to BrewmyApp', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?> &nearr;
            </a>
            <p class="bma-connect-foot" id="bma-connect-popup-note" style="display:none;">
                <?php echo esc_html__('Your browser blocked the popup. Click the button above to open BrewmyApp in a new tab.', 'brewmyapp-mobile-app-builder-for-woocommerce'); ?>
            </p>
            <p class="bma-connect-foot">
                <a href="<?php echo esc_url(add_query_arg('bma_refresh', '1')); ?>"
                    style="display:inline-block;background:#fff;border:2px solid #18181b;color:#18181b;padding:12px 24px;border-radius:8px;font-size:15px;font-weight:600;cursor:pointer;margin-top:8px;text-decoration:none;">&#x21bb;
                    <?php echo esc_html__("I've connected — Refresh status", 'brewmyapp-mobile-app-builder-for-woocommerce'); ?></a>
            </p>
        </div>
    </div>
    <script>
        (function () {
                var url = <?php echo wp_json_encode($connect_url); ?>;
                var autoOpen = <?php echo $auto_open ? 'true' : 'false'; ?>;
                if (!autoOpen) return;
                // Popup-blockers usually permit window.open during a load handler
                // only when triggered by a user gesture. Most browsers still block
                // a programmatic open from initial page load — if so, the manual
                // button stays visible and we surface a hint.
                var w = window.open(url, '_blank');
                if (!w || w.closed || typeof w.closed === 'undefined') {
                    var note = document.getElementById('bma-connect-popup-note');
                    if (note) note.style.display = 'block';
                }
            })();
        </script>
        <?php
        brewmyapp_render_connect_panel_styles();
}

function brewmyapp_render_connect_panel_styles()
{
    ?>
        <style>
            #wpcontent {
                padding-left: 20px;
            }

            .bma-connect-wrap {
                display: flex;
                justify-content: center;
                padding: 48px 20px;
            }

            .bma-connect-card {
                max-width: 560px;
                width: 100%;
                background: #fff;
                border: 1px solid #e5e7eb;
                border-radius: 16px;
                padding: 36px 32px;
                box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
                text-align: center;
            }

            .bma-connect-badge {
                display: inline-block;
                font-size: 11px;
                font-weight: 600;
                letter-spacing: 0.08em;
                text-transform: uppercase;
                color: #6366f1;
                background: #eef2ff;
                padding: 4px 10px;
                border-radius: 999px;
                margin-bottom: 16px;
            }

            .bma-connect-badge-ok {
                color: #047857;
                background: #d1fae5;
            }

            .bma-connect-card h1 {
                font-size: 22px;
                font-weight: 600;
                color: #18181b;
                margin: 0 0 12px;
                line-height: 1.3;
            }

            .bma-connect-card p {
                color: #525252;
                line-height: 1.6;
                margin: 0 0 20px;
                font-size: 14px;
            }

            .bma-connect-btn {
                display: inline-flex;
                align-items: center;
                gap: 6px;
                padding: 12px 22px;
                background: #18181b;
                color: #fff !important;
                text-decoration: none;
                border-radius: 10px;
                font-weight: 600;
                font-size: 14px;
                transition: background 0.15s ease;
            }

            .bma-connect-btn:hover {
                background: #27272a;
            }

            .bma-connect-foot {
                font-size: 12px;
                color: #737373;
                margin-top: 18px;
                margin-bottom: 0;
            }

            .bma-connect-foot a {
                color: #6366f1;
                text-decoration: none;
            }

            .bma-connect-foot a:hover {
                text-decoration: underline;
            }
        </style>
        <?php
}


// ══════════════════════════════════════════════
// CONNECTED DASHBOARD — post-connection cards
// Shown on the main "brewmyapp" page once the store is linked. Mirrors the
// Shopify embedded app: live-preview QR, current subscription, pricing, OTP
// bypass, quick actions, cart-sync guide, plus the existing stats/status cards.
// ══════════════════════════════════════════════

/**
 * Shared store metrics used by the stats grid + integration status card.
 */
function brewmyapp_get_dashboard_stats()
{
    $users_with_tokens = get_users(array('meta_key' => 'brewmyapp_expo_push_tokens')); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
    $total_devices = 0;
    foreach ($users_with_tokens as $user) {
        $total_devices += count(brewmyapp_get_user_push_tokens($user->ID));
    }
    $push_logs = get_option('brewmyapp_push_notification_logs', array());
    $api_logs = brewmyapp_get_api_logs();

    return array(
        'total_devices' => $total_devices,
        'users_with_app' => count($users_with_tokens),
        'total_notifications' => is_array($push_logs) ? count($push_logs) : 0,
        'total_log_entries' => count($api_logs),
    );
}

/**
 * Stats grid (4 metric cards). Shared by the dashboard page + connected view.
 */
function brewmyapp_render_stats_grid()
{
    $s = brewmyapp_get_dashboard_stats();

    echo '<div class="bma-stats-grid">';

    echo '<div class="bma-stat-card">';
    echo '<div class="bma-stat-icon emerald"><span class="dashicons dashicons-smartphone"></span></div>';
    echo '<div class="bma-stat-value">' . esc_html($s['total_devices']) . '</div>';
    echo '<div class="bma-stat-label">' . esc_html__('Registered Devices', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</div>';
    echo '</div>';

    echo '<div class="bma-stat-card">';
    echo '<div class="bma-stat-icon blue"><span class="dashicons dashicons-groups"></span></div>';
    echo '<div class="bma-stat-value">' . esc_html($s['users_with_app']) . '</div>';
    echo '<div class="bma-stat-label">' . esc_html__('Users with App', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</div>';
    echo '</div>';

    echo '<div class="bma-stat-card">';
    echo '<div class="bma-stat-icon purple"><span class="dashicons dashicons-megaphone"></span></div>';
    echo '<div class="bma-stat-value">' . esc_html($s['total_notifications']) . '</div>';
    echo '<div class="bma-stat-label">' . esc_html__('Notifications Sent', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</div>';
    echo '</div>';

    echo '<div class="bma-stat-card">';
    echo '<div class="bma-stat-icon amber"><span class="dashicons dashicons-media-text"></span></div>';
    echo '<div class="bma-stat-value">' . esc_html($s['total_log_entries']) . '</div>';
    echo '<div class="bma-stat-label">' . esc_html__('API Log Entries', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</div>';
    echo '</div>';

    echo '</div>'; // stats grid
}

/**
 * Integration status card. Shared by the dashboard page + connected view.
 */
function brewmyapp_render_integration_status_card()
{
    $s = brewmyapp_get_dashboard_stats();
    $total_devices = $s['total_devices'];
    $wa_configured = !empty(get_option('brewmyapp_wa_token', ''));
    $woo_active = class_exists('WooCommerce');

    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-admin-plugins"></span> ' . esc_html__('Integration Status', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';

    echo '<div class="bma-info-row">';
    echo '<span class="bma-info-label">' . esc_html__('WooCommerce', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo $woo_active
        ? '<span class="bma-badge bma-badge-success"><span class="bma-badge-dot"></span> ' . esc_html__('Connected', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>'
        : '<span class="bma-badge bma-badge-error"><span class="bma-badge-dot"></span> ' . esc_html__('Not Found', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '<div class="bma-info-row">';
    echo '<span class="bma-info-label">' . esc_html__('WhatsApp OTP', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo $wa_configured
        ? '<span class="bma-badge bma-badge-success"><span class="bma-badge-dot"></span> ' . esc_html__('Configured', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>'
        : '<span class="bma-badge bma-badge-warning"><span class="bma-badge-dot"></span> ' . esc_html__('Not Configured', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '<div class="bma-info-row">';
    echo '<span class="bma-info-label">' . esc_html__('Email OTP', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '<span class="bma-badge bma-badge-success"><span class="bma-badge-dot"></span> ' . esc_html__('Active', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '<div class="bma-info-row">';
    echo '<span class="bma-info-label">' . esc_html__('Push Notifications', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo $total_devices > 0
        /* translators: %d: number of registered devices */
        ? '<span class="bma-badge bma-badge-success"><span class="bma-badge-dot"></span> ' . esc_html(sprintf(_n('%d device', '%d device(s)', $total_devices, 'brewmyapp-mobile-app-builder-for-woocommerce'), $total_devices)) . '</span>'
        : '<span class="bma-badge bma-badge-neutral"><span class="bma-badge-dot"></span> ' . esc_html__('No devices', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '<div class="bma-info-row">';
    echo '<span class="bma-info-label">' . esc_html__('JWT Auth', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    $jwt_secret = get_option('brewmyapp_jwt_secret', '');
    echo $jwt_secret
        ? '<span class="bma-badge bma-badge-success"><span class="bma-badge-dot"></span> ' . esc_html__('Custom Secret', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>'
        : '<span class="bma-badge bma-badge-info"><span class="bma-badge-dot"></span> ' . esc_html__('Using WP Salt', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '</div>';
}

/**
 * Fetch active subscription plans from the BrewmyApp backend (public endpoint).
 * Returns an array of plans (possibly empty) — cached for 1h.
 */
function brewmyapp_fetch_plans()
{
    $cache_key = 'bma_subscription_plans';
    $cached = get_transient($cache_key);
    if (is_array($cached)) {
        return $cached;
    }

    $response = wp_remote_get(
        brewmyapp_get_backend_url() . '/api/subscriptions/plans',
        array('timeout' => 12, 'sslverify' => true)
    );
    if (is_wp_error($response)) {
        return array();
    }
    $data = json_decode(wp_remote_retrieve_body($response), true);
    $plans = (is_array($data) && !empty($data['plans']) && is_array($data['plans'])) ? $data['plans'] : array();

    set_transient($cache_key, $plans, HOUR_IN_SECONDS);
    return $plans;
}

/**
 * Live-preview QR card. Mirrors the Shopify launcher: a QR encoding the
 * BrewmyApp web preview URL for this store, which opens the live app preview.
 */
function brewmyapp_render_preview_qr_card($frontend)
{
    $preview = brewmyapp_plugin_api('/preview');
    $store_id = (!is_wp_error($preview) && !empty($preview['storeId'])) ? (string) $preview['storeId'] : '';

    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-smartphone"></span> ' . esc_html__('Live App Preview', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<p class="bma-card-desc">' . esc_html__("This is a live preview — not your published app. Your products are already loaded, so you can explore and design in real time. Building your own branded app comes later.", 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';

    if ($store_id) {
        $preview_url = $frontend . '/preview?store_id=' . rawurlencode($store_id);
        $qr_src = 'https://api.qrserver.com/v1/create-qr-code/?size=220x220&margin=10&data=' . rawurlencode($preview_url);
        echo '<div style="display:flex;gap:18px;align-items:center;flex-wrap:wrap;">';
        echo '<img src="' . esc_url($qr_src) . '" width="180" height="180" alt="' . esc_attr__('Live preview QR code', 'brewmyapp-mobile-app-builder-for-woocommerce') . '" style="border:1px solid #e5e7eb;border-radius:10px;background:#fff;" />';
        echo '<div>';
        echo '<p style="margin:0 0 10px;font-size:13px;color:#52525b;">' . esc_html__('Scan with the free BrewmyApp Preview app, or open the preview link directly.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
        echo '<a href="' . esc_url($preview_url) . '" target="_blank" rel="noopener" class="bma-quick-link"><span class="dashicons dashicons-external"></span> ' . esc_html__('Open live preview', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
        echo '</div>';
        echo '</div>';
    } else {
        echo '<p style="font-size:13px;color:#71717a;">' . esc_html__('Preview will be available shortly after your store finishes syncing. Open the BrewmyApp dashboard to view it.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
        echo '<a href="' . esc_url($frontend . '/dashboard/preview') . '" target="_blank" rel="noopener" class="bma-quick-link"><span class="dashicons dashicons-external"></span> ' . esc_html__('Open preview on BrewmyApp', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    }

    echo '</div>';
}

/**
 * Current subscription card (read-only). Management stays on brewmyapp.io.
 */
function brewmyapp_render_subscription_card($frontend)
{
    $result = brewmyapp_plugin_api('/subscription');
    $sub = (!is_wp_error($result) && !empty($result['subscription'])) ? $result['subscription'] : null;

    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-awards"></span> ' . esc_html__('Current Subscription', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';

    if ($sub) {
        $status = isset($sub['status']) ? strtoupper((string) $sub['status']) : '';
        $plan_name = !empty($sub['plan']['name']) ? (string) $sub['plan']['name'] : __('Plan', 'brewmyapp-mobile-app-builder-for-woocommerce');
        $price = isset($sub['plan']['price']) ? $sub['plan']['price'] : null;
        $cycle = !empty($sub['plan']['billingCycle']) ? strtolower((string) $sub['plan']['billingCycle']) : '';

        $badge_class = 'bma-badge-info';
        if ($status === 'ACTIVE' || $status === 'TRIAL') {
            $badge_class = 'bma-badge-success';
        } elseif ($status === 'PAST_DUE' || $status === 'PAUSED') {
            $badge_class = 'bma-badge-warning';
        } elseif ($status === 'CANCELLED' || $status === 'EXPIRED') {
            $badge_class = 'bma-badge-error';
        }

        echo '<div class="bma-info-row">';
        echo '<span class="bma-info-label">' . esc_html__('Status', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
        echo '<span class="bma-badge ' . esc_attr($badge_class) . '"><span class="bma-badge-dot"></span> ' . esc_html(ucfirst(strtolower($status))) . '</span>';
        echo '</div>';

        echo '<div class="bma-info-row">';
        echo '<span class="bma-info-label">' . esc_html__('Plan', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
        $plan_text = $plan_name;
        if ($price !== null && $price !== '') {
            $plan_text .= ' — $' . number_format((float) $price, 2);
            if ($cycle) {
                $plan_text .= '/' . ($cycle === 'yearly' ? __('yr', 'brewmyapp-mobile-app-builder-for-woocommerce') : ($cycle === 'monthly' ? __('mo', 'brewmyapp-mobile-app-builder-for-woocommerce') : $cycle));
            }
        }
        echo '<strong style="font-size:13px;color:#18181b;">' . esc_html($plan_text) . '</strong>';
        echo '</div>';

        if (!empty($sub['endDate'])) {
            $renews = !empty($sub['cancelAtPeriodEnd']) ? __('Ends', 'brewmyapp-mobile-app-builder-for-woocommerce') : __('Renews', 'brewmyapp-mobile-app-builder-for-woocommerce');
            $end_ts = strtotime((string) $sub['endDate']);
            if ($end_ts) {
                echo '<div class="bma-info-row">';
                echo '<span class="bma-info-label">' . esc_html($renews) . '</span>';
                echo '<span style="font-size:13px;color:#52525b;">' . esc_html(date_i18n(get_option('date_format'), $end_ts)) . '</span>';
                echo '</div>';
            }
        }

        if (!empty($sub['pendingPlan']['name'])) {
            echo '<div class="bma-info-row">';
            echo '<span class="bma-info-label">' . esc_html__('Scheduled change', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
            echo '<span style="font-size:13px;color:#52525b;">' . esc_html((string) $sub['pendingPlan']['name']) . '</span>';
            echo '</div>';
        }
    } else {
        // Free tier — show a Free badge and exactly what's included for free.
        echo '<div class="bma-info-row">';
        echo '<span class="bma-info-label">' . esc_html__('Status', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
        echo '<span class="bma-badge bma-badge-success"><span class="bma-badge-dot"></span> ' . esc_html__('Free', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
        echo '</div>';
        echo '<div class="bma-info-row">';
        echo '<span class="bma-info-label">' . esc_html__('Plan', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
        echo '<strong style="font-size:13px;color:#18181b;">' . esc_html__('Free — design & preview', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</strong>';
        echo '</div>';

        // Free App Builder — price, tagline & full free feature list.
        echo '<div style="display:flex;align-items:baseline;gap:6px;margin:14px 0 4px;">';
        echo '<span style="font-size:22px;font-weight:800;color:#047857;">$0</span>';
        echo '<span style="font-size:12px;color:#52525b;">/ ' . esc_html__('forever', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
        echo '</div>';
        echo '<p style="font-size:12px;color:#52525b;margin:0 0 12px;">' . esc_html__('Design & preview your app for free — pay only when you build & publish.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
        echo '<p style="font-size:12px;color:#3f3f46;margin:0 0 8px;font-weight:600;">' . esc_html__("What you get on Free:", 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
        $free_features = array(
            __('Connect unlimited WooCommerce & Shopify stores', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            __('Full store data — products, categories, customers, orders & reports', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            __('Drag-and-drop block app designer', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            __('Custom branding, theming & colors', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            __('Live app preview on your own device', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            __('Real-time, two-way store sync', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            __('WhatsApp & email OTP login — built in', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            __('Push notification configuration', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            __('Full multi-language support', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            __('Popup marketing banner', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            __('App-purchase discount', 'brewmyapp-mobile-app-builder-for-woocommerce'),
            __('Shared access — multiple team members per store', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        );
        echo '<ul style="list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px;">';
        foreach ($free_features as $f) {
            echo '<li style="display:flex;align-items:flex-start;gap:8px;font-size:12px;color:#3f3f46;"><span class="dashicons dashicons-yes" style="color:#10b981;font-size:18px;width:18px;height:18px;flex-shrink:0;"></span><span>' . esc_html($f) . '</span></li>';
        }
        echo '</ul>';
        echo '<p style="font-size:12px;color:#71717a;margin:10px 0 0;">' . esc_html__('Upgrade to Pro to build & publish to the App Store & Play Store, send push notifications, and ship OTA updates.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    }

    // Upgrade / manage CTA.
    $upgrade_label = $sub ? __('Manage / upgrade plan', 'brewmyapp-mobile-app-builder-for-woocommerce') : __('Upgrade to Pro', 'brewmyapp-mobile-app-builder-for-woocommerce');
    echo '<div style="margin-top:14px;display:flex;gap:10px;flex-wrap:wrap;align-items:center;">';
    echo '<a href="' . esc_url($frontend . '/dashboard/subscriptions') . '" target="_blank" rel="noopener" class="bma-cta-btn">' . esc_html($upgrade_label) . ' &nearr;</a>';
    echo '<a href="' . esc_url($frontend . '/dashboard/stores') . '" target="_blank" rel="noopener" class="bma-quick-link"><span class="dashicons dashicons-admin-settings"></span> ' . esc_html__('Manage on BrewmyApp', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '</div>';

    echo '</div>';
}

/**
 * Pricing card — the Pro upgrade, mirroring brewmyapp.io/pricing.
 *
 * The Free plan (design, preview & sync, no payment) now lives in the
 * subscription card; this card focuses on Pro, which adds builds, publishing,
 * OTA and the advanced push / marketing / analytics suite. Pro has a
 * monthly/yearly toggle; regular prices come from the
 * backend plans (fallback to the known $99 / $999).
 */
function brewmyapp_render_pricing_card($frontend)
{
    $plans = brewmyapp_fetch_plans();

    $monthly = null;
    $yearly = null;
    foreach ($plans as $p) {
        $price = isset($p['price']) ? (float) $p['price'] : 0;
        $cycle = !empty($p['billingCycle']) ? strtolower((string) $p['billingCycle']) : '';
        if ($price <= 0) {
            continue;
        }
        if ($cycle === 'monthly' && $monthly === null) {
            $monthly = $price;
        }
        if ($cycle === 'yearly' && $yearly === null) {
            $yearly = $price;
        }
    }
    if ($monthly === null) {
        $monthly = 99.0;
    }
    if ($yearly === null) {
        $yearly = 999.0;
    }
    $save_pct = ($monthly > 0) ? (int) round((1 - ($yearly / ($monthly * 12))) * 100) : 0;

    $pro_features = array(
        __('iOS & Android production builds', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('Over-the-air (OTA) updates', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('App Store & Play Store publishing guidance', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('Send unlimited push notifications to your customers', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('Scheduled & rich-media (image/GIF) push', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('Abandoned cart recovery — automatic reminders', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('Automated journeys — welcome, browse & win-back', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('Behavioral customer segmentation & groups', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('App & shopper analytics — funnel, top products, LTV', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('Live shopping / shoppable video', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('Klaviyo integration', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('Product & category deep linking', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('Standard email support', 'brewmyapp-mobile-app-builder-for-woocommerce'),
    );

    $reassure = array(
        __('No revenue share or per-order fees', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('No upfront payment required', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('Preview your app before building', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('Cancel anytime', 'brewmyapp-mobile-app-builder-for-woocommerce'),
    );

    $li = function ($text, $color) {
        echo '<li style="display:flex;align-items:flex-start;gap:8px;font-size:12px;color:#3f3f46;"><span class="dashicons dashicons-yes" style="color:' . esc_attr($color) . ';font-size:18px;width:18px;height:18px;flex-shrink:0;"></span><span>' . esc_html($text) . '</span></li>';
    };

    $uid = 'bma-pricing';

    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-cart"></span> ' . esc_html__('Pricing', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';

    // ── Pro plan ──
    echo '<div style="border:1px solid #e4e4e7;border-radius:12px;padding:16px;">';

    // Monthly / Yearly toggle
    echo '<div style="display:flex;align-items:center;gap:0;background:#f4f4f5;border-radius:10px;padding:4px;margin-bottom:14px;">';
    echo '<button type="button" id="' . esc_attr($uid) . '-mo" style="flex:1;border:0;background:#fff;color:#18181b;font-weight:600;font-size:13px;padding:8px 10px;border-radius:8px;cursor:pointer;box-shadow:0 1px 2px rgba(0,0,0,.06);">' . esc_html__('Monthly', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</button>';
    $yearly_btn = esc_html__('Yearly', 'brewmyapp-mobile-app-builder-for-woocommerce');
    if ($save_pct > 0) {
        $yearly_btn .= ' <span style="color:#059669;">(' . esc_html(sprintf(__('Save %d%%', 'brewmyapp-mobile-app-builder-for-woocommerce'), $save_pct)) . ')</span>';
    }
    echo '<button type="button" id="' . esc_attr($uid) . '-yr" style="flex:1;border:0;background:transparent;color:#52525b;font-weight:600;font-size:13px;padding:8px 10px;border-radius:8px;cursor:pointer;">' . $yearly_btn . '</button>';
    echo '</div>';

    // Header + price (switchable)
    echo '<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:8px;flex-wrap:wrap;">';
    echo '<div><strong style="font-size:14px;color:#18181b;">' . esc_html__('BrewmyApp Pro', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</strong><p style="font-size:11px;color:#71717a;margin:2px 0 0;">' . esc_html__('per store', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p></div>';
    echo '<div style="text-align:right;">';
    // Monthly price
    echo '<div id="' . esc_attr($uid) . '-price-mo">';
    echo '<span style="font-size:24px;font-weight:800;color:#18181b;">$' . esc_html(number_format($monthly, 0)) . '</span><span style="font-size:12px;color:#71717a;">/' . esc_html__('mo', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';
    // Yearly price
    echo '<div id="' . esc_attr($uid) . '-price-yr" style="display:none;">';
    echo '<span style="font-size:24px;font-weight:800;color:#18181b;">$' . esc_html(number_format($yearly, 0)) . '</span><span style="font-size:12px;color:#71717a;">/' . esc_html__('yr', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';
    echo '</div>';
    echo '</div>';

    echo '<p style="font-size:12px;font-weight:600;color:#18181b;margin:12px 0 8px;">' . esc_html__('Everything in Free, plus', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '<ul style="list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px;">';
    foreach ($pro_features as $f) {
        $li($f, '#18181b');
    }
    echo '</ul>';
    echo '</div>';

    // ── Reassurance strip ──
    echo '<div style="margin-top:14px;border-top:1px solid #f1f5f9;padding-top:12px;display:flex;flex-direction:column;gap:6px;">';
    foreach ($reassure as $r) {
        echo '<div style="display:flex;align-items:center;gap:8px;font-size:12px;color:#52525b;"><span class="dashicons dashicons-shield-alt" style="color:#059669;font-size:16px;width:16px;height:16px;flex-shrink:0;"></span><span>' . esc_html($r) . '</span></div>';
    }
    echo '</div>';

    // ── CTA ──
    echo '<div style="margin-top:14px;display:flex;gap:10px;flex-wrap:wrap;align-items:center;">';
    echo '<a href="' . esc_url($frontend . '/dashboard/subscriptions') . '" target="_blank" rel="noopener" class="bma-cta-btn">' . esc_html__('Upgrade to Pro', 'brewmyapp-mobile-app-builder-for-woocommerce') . ' &nearr;</a>';
    echo '<a href="' . esc_url($frontend . '/pricing') . '" target="_blank" rel="noopener" class="bma-quick-link">' . esc_html__('See full plan details', 'brewmyapp-mobile-app-builder-for-woocommerce') . ' &rarr;</a>';
    echo '</div>';
    ?>
    <script>
        (function () {
            var mo = document.getElementById('<?php echo esc_js($uid); ?>-mo');
            var yr = document.getElementById('<?php echo esc_js($uid); ?>-yr');
            var pm = document.getElementById('<?php echo esc_js($uid); ?>-price-mo');
            var py = document.getElementById('<?php echo esc_js($uid); ?>-price-yr');
            if (!mo || !yr || !pm || !py) return;
            function set(monthly) {
                pm.style.display = monthly ? 'block' : 'none';
                py.style.display = monthly ? 'none' : 'block';
                mo.style.background = monthly ? '#fff' : 'transparent';
                mo.style.color = monthly ? '#18181b' : '#52525b';
                mo.style.boxShadow = monthly ? '0 1px 2px rgba(0,0,0,.06)' : 'none';
                yr.style.background = monthly ? 'transparent' : '#fff';
                yr.style.color = monthly ? '#52525b' : '#18181b';
                yr.style.boxShadow = monthly ? 'none' : '0 1px 2px rgba(0,0,0,.06)';
            }
            mo.addEventListener('click', function () { set(true); });
            yr.addEventListener('click', function () { set(false); });
        })();
    </script>
    <?php
    echo '</div>';
}

/**
 * Quick actions card — deep links into the BrewmyApp dashboard.
 */
function brewmyapp_render_quick_actions_card($frontend)
{
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-admin-links"></span> ' . esc_html__('Quick Actions', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<div class="bma-quick-links" style="flex-direction:column;">';

    echo '<a href="' . esc_url($frontend . '/dashboard/designer') . '" target="_blank" rel="noopener" class="bma-quick-link"><span class="dashicons dashicons-art"></span> ' . esc_html__('Design your app', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '<a href="' . esc_url($frontend . '/dashboard/builds') . '" target="_blank" rel="noopener" class="bma-quick-link"><span class="dashicons dashicons-smartphone"></span> ' . esc_html__('Build & publish', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '<a href="' . esc_url($frontend . '/dashboard/preview') . '" target="_blank" rel="noopener" class="bma-quick-link"><span class="dashicons dashicons-visibility"></span> ' . esc_html__('Live preview', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '<a href="' . esc_url($frontend . '/dashboard/stores') . '" target="_blank" rel="noopener" class="bma-quick-link"><span class="dashicons dashicons-store"></span> ' . esc_html__('Manage stores', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '<a href="' . esc_url('https://www.brewmyapp.io/contact') . '" target="_blank" rel="noopener" class="bma-quick-link"><span class="dashicons dashicons-sos"></span> ' . esc_html__('Contact support', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';

    echo '</div>';
    echo '</div>';
}

/**
 * Cart sync / mobile checkout setup guide card.
 */
function brewmyapp_render_cart_sync_card()
{
    $woo_active = class_exists('WooCommerce');

    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-cart"></span> ' . esc_html__('Cart Sync & Mobile Checkout', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<p class="bma-card-desc">' . esc_html__('Keep the shopping cart in sync between your website and the mobile app, and let customers complete checkout seamlessly.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';

    if ($woo_active) {
        echo '<div class="bma-info-row">';
        echo '<span class="bma-info-label">' . esc_html__('Cart sync', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
        echo '<span class="bma-badge bma-badge-success"><span class="bma-badge-dot"></span> ' . esc_html__('Active for logged-in users', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
        echo '</div>';
    }

    echo '<ol style="margin:8px 0 0;padding-left:18px;font-size:13px;color:#52525b;line-height:1.7;">';
    echo '<li>' . esc_html__('Make sure WooCommerce is active and your products are published.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</li>';
    echo '<li>' . esc_html__('Customers sign in on the app with OTP — their cart carries over to your store automatically.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</li>';
    echo '<li>' . esc_html__('Checkout opens your WooCommerce checkout with the customer already logged in.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</li>';
    echo '</ol>';

    echo '</div>';
}

/**
 * Read the local OTP-bypass config (app-review test login).
 * Returns array{ enabled: bool, email: string, otp: string }.
 */
function brewmyapp_get_otp_bypass()
{
    $config = get_option('brewmyapp_otp_bypass', array());
    if (!is_array($config)) {
        $config = array();
    }
    return array(
        'enabled' => !empty($config['enabled']),
        'email' => isset($config['email']) ? (string) $config['email'] : '',
        'otp' => isset($config['otp']) ? (string) $config['otp'] : '',
    );
}

/**
 * True when OTP bypass is enabled and the given email + OTP match the config.
 * Constant-time comparison on the OTP.
 */
function brewmyapp_otp_bypass_allows($email, $otp)
{
    $config = brewmyapp_get_otp_bypass();
    if (empty($config['enabled']) || empty($config['email']) || empty($config['otp'])) {
        return false;
    }
    if (strtolower(trim((string) $email)) !== strtolower(trim($config['email']))) {
        return false;
    }
    return hash_equals($config['otp'], (string) $otp);
}

/**
 * Handle the OTP-bypass form (posts back to the connected page).
 */
function brewmyapp_handle_otp_bypass_save()
{
    if (!isset($_POST['brewmyapp_otp_bypass_save']) || !current_user_can('manage_options')) {
        return;
    }
    if (!check_admin_referer('brewmyapp_otp_bypass_action', 'brewmyapp_otp_bypass_nonce')) {
        return;
    }

    $action = sanitize_text_field(wp_unslash($_POST['brewmyapp_otp_bypass_save']));
    $config = brewmyapp_get_otp_bypass();

    if ($action === 'disable') {
        $config['enabled'] = false;
        update_option('brewmyapp_otp_bypass', $config);
        $GLOBALS['bma_otp_bypass_notice'] = array('type' => 'success', 'msg' => __('OTP bypass turned off.', 'brewmyapp-mobile-app-builder-for-woocommerce'));
        return;
    }

    $email = sanitize_email(wp_unslash($_POST['brewmyapp_otp_bypass_email'] ?? ''));
    if (!$email || !is_email($email)) {
        $GLOBALS['bma_otp_bypass_notice'] = array('type' => 'error', 'msg' => __('Enter a valid customer email to enable OTP bypass.', 'brewmyapp-mobile-app-builder-for-woocommerce'));
        return;
    }

    // Reuse the existing OTP if the email is unchanged; otherwise mint a new one.
    $otp = (!empty($config['otp']) && strtolower($config['email']) === strtolower($email))
        ? $config['otp']
        : str_pad((string) wp_rand(0, 999999), 6, '0', STR_PAD_LEFT);

    update_option('brewmyapp_otp_bypass', array(
        'enabled' => true,
        'email' => $email,
        'otp' => $otp,
    ));
    $GLOBALS['bma_otp_bypass_notice'] = array('type' => 'success', 'msg' => __('OTP bypass enabled.', 'brewmyapp-mobile-app-builder-for-woocommerce'));
}

/**
 * OTP bypass card — lets the admin set a fixed test OTP for one customer email
 * so App Store / Play Store reviewers can sign in without receiving a real OTP.
 */
function brewmyapp_render_otp_bypass_card()
{
    $config = brewmyapp_get_otp_bypass();
    $notice = isset($GLOBALS['bma_otp_bypass_notice']) ? $GLOBALS['bma_otp_bypass_notice'] : null;

    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-unlock"></span> ' . esc_html__('OTP Testing (App Review Login)', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<p class="bma-card-desc">' . esc_html__('Set a fixed OTP for one existing customer so app reviewers can log in without receiving a real code. Works for both Email and WhatsApp OTP login.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';

    if ($notice) {
        $cls = $notice['type'] === 'error' ? 'bma-badge-error' : 'bma-badge-success';
        echo '<p><span class="bma-badge ' . esc_attr($cls) . '"><span class="bma-badge-dot"></span> ' . esc_html($notice['msg']) . '</span></p>';
    }

    if (!empty($config['enabled']) && !empty($config['email'])) {
        echo '<div style="border:1px solid #d1fae5;background:#ecfdf5;border-radius:10px;padding:12px 14px;margin-bottom:12px;">';
        echo '<div class="bma-info-row" style="border:0;padding:0 0 6px;">';
        echo '<span class="bma-info-label">' . esc_html__('Login email', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
        echo '<code style="font-size:13px;">' . esc_html($config['email']) . '</code>';
        echo '</div>';
        echo '<div class="bma-info-row" style="border:0;padding:0;">';
        echo '<span class="bma-info-label">' . esc_html__('Fixed OTP', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
        echo '<code style="font-size:15px;letter-spacing:2px;font-weight:700;">' . esc_html($config['otp']) . '</code>';
        echo '</div>';
        echo '</div>';

        echo '<form method="post" style="display:inline;">';
        wp_nonce_field('brewmyapp_otp_bypass_action', 'brewmyapp_otp_bypass_nonce');
        echo '<button type="submit" name="brewmyapp_otp_bypass_save" value="disable" class="bma-cta-btn bma-cta-btn-secondary">' . esc_html__('Turn off bypass', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</button>';
        echo '</form>';
    } else {
        echo '<form method="post">';
        wp_nonce_field('brewmyapp_otp_bypass_action', 'brewmyapp_otp_bypass_nonce');
        echo '<table class="bma-form-table"><tr>';
        echo '<th><label for="brewmyapp_otp_bypass_email">' . esc_html__('Customer email', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</label></th>';
        echo '<td><input type="email" id="brewmyapp_otp_bypass_email" name="brewmyapp_otp_bypass_email" value="' . esc_attr($config['email']) . '" class="regular-text" placeholder="reviewer@example.com" required /></td>';
        echo '</tr></table>';
        echo '<p style="font-size:12px;color:#71717a;margin:4px 0 12px;">' . esc_html__('Must be an existing customer account on this store. A 6-digit OTP is generated on save.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
        echo '<button type="submit" name="brewmyapp_otp_bypass_save" value="enable" class="bma-cta-btn">' . esc_html__('Enable OTP bypass', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</button>';
        echo '</form>';
    }

    echo '</div>';
}

/**
 * Orchestrates the full connected dashboard view.
 */
function brewmyapp_render_connected_dashboard()
{
    $frontend = brewmyapp_get_frontend_url();

    brewmyapp_page_header(
        __('BrewmyApp', 'brewmyapp-mobile-app-builder-for-woocommerce'),
        __('Your store is connected. Preview your app, manage your plan, and test login below.', 'brewmyapp-mobile-app-builder-for-woocommerce')
    );

    // Connected banner
    echo '<div class="bma-card" style="display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;">';
    echo '<div style="display:flex;align-items:center;gap:12px;">';
    echo '<span class="bma-badge bma-badge-success"><span class="bma-badge-dot"></span> ' . esc_html__('Connected', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '<span style="font-size:14px;color:#18181b;font-weight:600;">' . esc_html__('Your store is connected to BrewmyApp', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';
    echo '<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">';
    echo '<a href="' . esc_url(add_query_arg('bma_refresh', '1')) . '" class="bma-quick-link" style="white-space:nowrap;"><span class="dashicons dashicons-update"></span> ' . esc_html__('Refresh', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '<a href="' . esc_url($frontend . '/dashboard/stores') . '" target="_blank" rel="noopener" class="bma-cta-btn">' . esc_html__('Open BrewmyApp dashboard', 'brewmyapp-mobile-app-builder-for-woocommerce') . ' &nearr;</a>';
    echo '</div>';
    echo '</div>';

    // Live Preview QR + Subscription
    echo '<div class="bma-two-col">';
    brewmyapp_render_preview_qr_card($frontend);
    brewmyapp_render_subscription_card($frontend);
    echo '</div>';

    // OTP testing + pricing
    echo '<div class="bma-two-col">';
    brewmyapp_render_otp_bypass_card();
    brewmyapp_render_pricing_card($frontend);
    echo '</div>';

    // Quick actions + cart sync
    echo '<div class="bma-two-col">';
    brewmyapp_render_quick_actions_card($frontend);
    brewmyapp_render_cart_sync_card();
    echo '</div>';

    brewmyapp_page_footer();
}

// ══════════════════════════════════════════════
// DASHBOARD PAGE
// ══════════════════════════════════════════════
function brewmyapp_render_dashboard_page()
{
    if (!current_user_can('manage_options')) {
        return;
    }

    brewmyapp_page_header(__('Dashboard', 'brewmyapp-mobile-app-builder-for-woocommerce'), __('Your WooCommerce mobile app - overview, status, and quick actions', 'brewmyapp-mobile-app-builder-for-woocommerce'));

    // Health Check
    echo '<div class="bma-card" style="display:flex;align-items:center;gap:16px;padding:18px 24px;">';
    echo '<span class="bma-health-dot inactive" id="bma-health-dot"></span>';
    echo '<div>';
    echo '<strong style="font-size:14px;color:#18181b;">' . esc_html__('REST API Status', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</strong>';
    echo '<span id="bma-health-text" style="font-size:13px;color:#71717a;margin-left:12px;">' . esc_html__('Checking…', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';
    echo '</div>';

    // Stats Grid
    brewmyapp_render_stats_grid();

    // Quick Status Cards
    echo '<div class="bma-two-col">';

    // Integration Status
    brewmyapp_render_integration_status_card();

    // Quick Links
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-admin-links"></span> ' . esc_html__('Quick Actions', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<div class="bma-quick-links" style="flex-direction:column;">';

    echo '<a href="' . esc_url(admin_url('admin.php?page=brewmyapp-settings')) . '" class="bma-quick-link"><span class="dashicons dashicons-admin-generic"></span> ' . esc_html__('JWT & Authentication Settings', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '<a href="' . esc_url(admin_url('admin.php?page=brewmyapp-whatsapp')) . '" class="bma-quick-link"><span class="dashicons dashicons-whatsapp"></span> ' . esc_html__('Configure WhatsApp OTP', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '<a href="https://www.brewmyapp.io/dashboard/push-notifications" target="_blank" rel="noopener" class="bma-quick-link"><span class="dashicons dashicons-megaphone"></span> ' . esc_html__('Send Push Notification', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '<a href="' . esc_url(admin_url('admin.php?page=brewmyapp-log')) . '" class="bma-quick-link"><span class="dashicons dashicons-media-text"></span> ' . esc_html__('View API Logs', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';

    echo '</div>';
    echo '</div>';

    echo '</div>'; // two-col

    // Features Grid
    echo '<div class="bma-features-grid">';

    echo '<div class="bma-feature-card">';
    echo '<div class="bma-feature-icon" style="background:#dbeafe;color:#3b82f6;">&#128274;</div>';
    echo '<h3>' . esc_html__('OTP Authentication', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h3>';
    echo '<p>' . esc_html__('Passwordless login via Email or WhatsApp OTP. Secure, rate-limited, with JWT token response for your mobile app.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '<a href="' . esc_url(admin_url('admin.php?page=brewmyapp-settings')) . '" class="bma-feature-link">' . esc_html__('Configure settings', 'brewmyapp-mobile-app-builder-for-woocommerce') . ' &rarr;</a>';
    echo '</div>';

    echo '<div class="bma-feature-card">';
    echo '<div class="bma-feature-icon" style="background:#d1fae5;color:#10b981;">&#128176;</div>';
    echo '<h3>' . esc_html__('In-App Payments', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h3>';
    echo '<p>' . esc_html__('Generate secure payment URLs from your app. Auto-login customers, redirect to checkout, and track payment status.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '<a href="' . esc_url(admin_url('admin.php?page=brewmyapp-settings')) . '" class="bma-feature-link">' . esc_html__('Configure settings', 'brewmyapp-mobile-app-builder-for-woocommerce') . ' &rarr;</a>';
    echo '</div>';

    echo '<div class="bma-feature-card">';
    echo '<div class="bma-feature-icon" style="background:#ede9fe;color:#8b5cf6;">&#128276;</div>';
    echo '<h3>' . esc_html__('Push Notifications', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h3>';
    echo '<p>' . esc_html__('Send promotional or order-status notifications to your app users via Expo push. Fully configurable from the dashboard.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '<a href="https://www.brewmyapp.io/dashboard/push-notifications" target="_blank" rel="noopener" class="bma-feature-link">' . esc_html__('Manage notifications', 'brewmyapp-mobile-app-builder-for-woocommerce') . ' &rarr;</a>';
    echo '</div>';

    echo '<div class="bma-feature-card">';
    echo '<div class="bma-feature-icon" style="background:#fef3c7;color:#f59e0b;">&#128202;</div>';
    echo '<h3>' . esc_html__('API Logging', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h3>';
    echo '<p>' . esc_html__('Track every API call - OTP requests, verifications, WhatsApp sends, and more. Filter by channel and export as JSON.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '<a href="' . esc_url(admin_url('admin.php?page=brewmyapp-log')) . '" class="bma-feature-link">' . esc_html__('View logs', 'brewmyapp-mobile-app-builder-for-woocommerce') . ' &rarr;</a>';
    echo '</div>';

    echo '</div>';

    brewmyapp_page_footer();
}

// ══════════════════════════════════════════════
// SETTINGS PAGE
// ══════════════════════════════════════════════
function brewmyapp_render_settings_page()
{
    if (!current_user_can('manage_options')) {
        return;
    }

    $notice = '';
    if (isset($_POST['brewmyapp_settings_save']) && check_admin_referer('brewmyapp_settings_save_action', 'brewmyapp_settings_save_nonce')) {
        if (isset($_POST['brewmyapp_jwt_secret'])) {
            update_option('brewmyapp_jwt_secret', sanitize_text_field(wp_unslash($_POST['brewmyapp_jwt_secret'])));
        }
        if (isset($_POST['brewmyapp_jwt_expiry'])) {
            $expiry = (int) sanitize_text_field(wp_unslash($_POST['brewmyapp_jwt_expiry']));
            update_option('brewmyapp_jwt_expiry', max(60, $expiry));
        }
        if (isset($_POST['brewmyapp_otp_login_mode'])) {
            $otp_login_mode = sanitize_text_field(wp_unslash($_POST['brewmyapp_otp_login_mode']));
            if (in_array($otp_login_mode, array('disabled', 'both', 'otp_only'), true)) {
                update_option('brewmyapp_otp_login_mode', $otp_login_mode);
            }
        }
        if (isset($_POST['brewmyapp_show_credit_submitted'])) {
            $show_credit = isset($_POST['brewmyapp_show_credit']) && $_POST['brewmyapp_show_credit'] === 'yes' ? 'yes' : 'no';
            update_option('brewmyapp_show_credit', $show_credit);
        }
        $notice = 'success';
    }

    $saved_secret = get_option('brewmyapp_jwt_secret', '');
    $saved_expiry = (int) get_option('brewmyapp_jwt_expiry', 0);
    $effective_secret = brewmyapp_get_jwt_secret();
    $effective_expiry = brewmyapp_get_jwt_expiry();

    brewmyapp_page_header(__('Settings', 'brewmyapp-mobile-app-builder-for-woocommerce'), __('Configure JWT authentication, OTP login, and plugin behavior', 'brewmyapp-mobile-app-builder-for-woocommerce'));

    if ($notice === 'success') {
        echo '<div class="bma-notice bma-notice-success"><span class="dashicons dashicons-yes-alt"></span> ' . esc_html__('Settings saved successfully.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</div>';
    }

    // JWT Settings Form
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-shield"></span> ' . esc_html__('JWT Authentication', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<p class="bma-card-desc">' . esc_html__('Configure the JSON Web Token settings used for mobile app authentication. Leave the secret blank to use WordPress salt keys.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';

    echo '<form method="post">';
    wp_nonce_field('brewmyapp_settings_save_action', 'brewmyapp_settings_save_nonce');

    echo '<table class="bma-form-table">';

    echo '<tr>';
    echo '<th><label for="brewmyapp_jwt_secret">' . esc_html__('JWT Secret', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</label></th>';
    echo '<td>';
    echo '<input name="brewmyapp_jwt_secret" id="brewmyapp_jwt_secret" type="text" class="bma-input" value="' . esc_attr($saved_secret) . '" placeholder="' . esc_attr__('Leave blank to use WordPress salt', 'brewmyapp-mobile-app-builder-for-woocommerce') . '" />';
    echo '<span class="bma-field-hint">' . wp_kses_post(__('A custom secret key for signing JWT tokens. If empty, the plugin uses <code>AUTH_KEY</code> or <code>wp_salt()</code>.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</span>';
    echo '</td>';
    echo '</tr>';

    echo '<tr>';
    echo '<th><label for="brewmyapp_jwt_expiry">' . esc_html__('Token Expiry', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</label></th>';
    echo '<td>';
    echo '<input name="brewmyapp_jwt_expiry" id="brewmyapp_jwt_expiry" type="number" min="60" class="bma-input" style="max-width:200px" value="' . esc_attr($saved_expiry ? $saved_expiry : $effective_expiry) . '" />';
    echo '<span class="bma-field-hint">' . wp_kses_post(__('Token lifetime in seconds. Default: <code>604800</code> (7 days). Minimum: <code>60</code>.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</span>';
    echo '</td>';
    echo '</tr>';

    echo '</table>';
    echo '<button type="submit" name="brewmyapp_settings_save" class="bma-btn bma-btn-primary"><span class="dashicons dashicons-saved"></span> ' . esc_html__('Save Settings', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</button>';
    echo '</form>';
    echo '</div>';

    // OTP Login on Website
    $otp_login_mode = get_option('brewmyapp_otp_login_mode', 'disabled');
    // Migrate legacy boolean value
    if ($otp_login_mode === 'yes') {
        $otp_login_mode = 'both';
    }
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-smartphone"></span> ' . esc_html__('OTP Login on Website', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<p class="bma-card-desc">' . esc_html__('Allow customers to log in on your WooCommerce My Account page using a one-time password (Email or WhatsApp) instead of a password.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '<form method="post">';
    wp_nonce_field('brewmyapp_settings_save_action', 'brewmyapp_settings_save_nonce');
    // Carry existing values so they don't get overwritten
    echo '<input type="hidden" name="brewmyapp_jwt_secret" value="' . esc_attr($saved_secret) . '" />';
    echo '<input type="hidden" name="brewmyapp_jwt_expiry" value="' . esc_attr($saved_expiry ? $saved_expiry : $effective_expiry) . '" />';
    echo '<table class="bma-form-table">';
    echo '<tr>';
    echo '<th><label for="brewmyapp_otp_login_mode">' . esc_html__('Login Mode', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</label></th>';
    echo '<td>';
    echo '<select name="brewmyapp_otp_login_mode" id="brewmyapp_otp_login_mode" class="bma-input" style="max-width:320px">';
    echo '<option value="disabled"' . selected($otp_login_mode, 'disabled', false) . '>' . esc_html__('Disabled — Password login only (default)', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</option>';
    echo '<option value="both"' . selected($otp_login_mode, 'both', false) . '>' . esc_html__('Both — Password + OTP login', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</option>';
    echo '<option value="otp_only"' . selected($otp_login_mode, 'otp_only', false) . '>' . esc_html__('OTP Only — Hide password form, show only OTP', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</option>';
    echo '</select>';
    echo '<span class="bma-field-hint">' . esc_html__('Choose how customers log in on the WooCommerce My Account page. "Both" shows the standard password form with an OTP option below. "OTP Only" replaces the password form entirely. Supports Email OTP and WhatsApp OTP (if configured).', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</td>';
    echo '</tr>';
    echo '</table>';
    echo '<button type="submit" name="brewmyapp_settings_save" class="bma-btn bma-btn-primary"><span class="dashicons dashicons-saved"></span> ' . esc_html__('Save Settings', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</button>';
    echo '</form>';
    echo '</div>';

    // General Settings (Credit Link, etc.)
    $show_credit = get_option('brewmyapp_show_credit', 'no') === 'yes';
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-admin-links"></span> ' . esc_html__('Developer Credit', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<p class="bma-card-desc">' . esc_html__('Choose whether to display a backlink to BrewmyApp in your website footer.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '<form method="post">';
    wp_nonce_field('brewmyapp_settings_save_action', 'brewmyapp_settings_save_nonce');
    echo '<input type="hidden" name="brewmyapp_show_credit_submitted" value="1" />';
    echo '<table class="bma-form-table">';
    echo '<tr>';
    echo '<th><label for="brewmyapp_show_credit">' . esc_html__('Show Backlink', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</label></th>';
    echo '<td>';
    echo '<label style="display:inline-flex;align-items:center;cursor:pointer;">';
    echo '<input name="brewmyapp_show_credit" id="brewmyapp_show_credit" type="checkbox" value="yes"' . checked($show_credit, true, false) . ' /> ';
    echo '<span style="margin-left:8px;">' . esc_html__('Display "Mobile app powered by BrewmyApp" link in footer', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</label>';
    echo '</td>';
    echo '</tr>';
    echo '</table>';
    echo '<button type="submit" name="brewmyapp_settings_save" class="bma-btn bma-btn-primary"><span class="dashicons dashicons-saved"></span> ' . esc_html__('Save Settings', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</button>';
    echo '</form>';
    echo '</div>';

    // Effective Values
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-info-outline"></span> ' . esc_html__('Active Configuration', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<p class="bma-card-desc">' . esc_html__('These are the values currently being used by the plugin for JWT operations.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';

    echo '<div class="bma-info-row">';
    echo '<span class="bma-info-label">' . esc_html__('JWT Secret (in use)', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '<span class="bma-info-value">' . esc_html(substr($effective_secret, 0, 16) . '…') . '</span>';
    echo '</div>';

    echo '<div class="bma-info-row">';
    echo '<span class="bma-info-label">' . esc_html__('Token Expiry (in use)', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '<span class="bma-info-value">' . esc_html(number_format($effective_expiry)) . ' ' . esc_html__('seconds', 'brewmyapp-mobile-app-builder-for-woocommerce') . ' (' . esc_html(round($effective_expiry / DAY_IN_SECONDS, 1)) . ' ' . esc_html__('days', 'brewmyapp-mobile-app-builder-for-woocommerce') . ')</span>';
    echo '</div>';

    echo '<div class="bma-info-row">';
    echo '<span class="bma-info-label">' . esc_html__('Secret Source', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    if ($saved_secret) {
        $source = __('Custom (plugin settings)', 'brewmyapp-mobile-app-builder-for-woocommerce');
    } elseif (defined('AUTH_KEY')) {
        $source = __('AUTH_KEY constant', 'brewmyapp-mobile-app-builder-for-woocommerce');
    } else {
        $source = __('wp_salt() function', 'brewmyapp-mobile-app-builder-for-woocommerce');
    }
    echo '<span class="bma-info-value" style="font-family:inherit;">' . esc_html($source) . '</span>';
    echo '</div>';

    echo '<div class="bma-info-row">';
    echo '<span class="bma-info-label">' . esc_html__('Algorithm', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '<span class="bma-info-value">HS256 (HMAC-SHA256)</span>';
    echo '</div>';

    echo '</div>';

    brewmyapp_page_footer();
}

// ══════════════════════════════════════════════
// WHATSAPP SETUP PAGE
// ══════════════════════════════════════════════
function brewmyapp_render_whatsapp_page()
{
    if (!current_user_can('manage_options')) {
        return;
    }

    $notice = '';
    if (isset($_POST['brewmyapp_whatsapp_save']) && check_admin_referer('brewmyapp_whatsapp_save_action', 'brewmyapp_whatsapp_save_nonce')) {
        update_option('brewmyapp_wa_token', sanitize_text_field(wp_unslash($_POST['brewmyapp_wa_token'] ?? '')));
        update_option('brewmyapp_wa_otp_template', sanitize_text_field(wp_unslash($_POST['brewmyapp_wa_otp_template'] ?? '')));
        update_option('brewmyapp_wa_template_lang', sanitize_text_field(wp_unslash($_POST['brewmyapp_wa_template_lang'] ?? '')));
        update_option('brewmyapp_wa_send_url', esc_url_raw(wp_unslash($_POST['brewmyapp_wa_send_url'] ?? '')));
        $notice = 'success';
    }

    $token = get_option('brewmyapp_wa_token', '');
    $template = get_option('brewmyapp_wa_otp_template', '');
    $lang = get_option('brewmyapp_wa_template_lang', 'en_US');
    $url = get_option('brewmyapp_wa_send_url', '');

    $is_configured = !empty($token) && !empty($url);

    brewmyapp_page_header(__('WhatsApp OTP', 'brewmyapp-mobile-app-builder-for-woocommerce'), __('Connect your Meta WhatsApp Business API to send OTP codes via WhatsApp', 'brewmyapp-mobile-app-builder-for-woocommerce'));

    if ($notice === 'success') {
        echo '<div class="bma-notice bma-notice-success"><span class="dashicons dashicons-yes-alt"></span> ' . esc_html__('WhatsApp settings saved successfully.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</div>';
    }

    // Status
    echo '<div class="bma-card" style="display:flex;align-items:center;gap:16px;padding:18px 24px;">';
    echo $is_configured
        ? '<span class="bma-badge bma-badge-success" style="font-size:13px;padding:6px 14px;"><span class="bma-badge-dot"></span> ' . esc_html__('WhatsApp OTP is configured and ready', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>'
        : '<span class="bma-badge bma-badge-warning" style="font-size:13px;padding:6px 14px;"><span class="bma-badge-dot"></span> ' . esc_html__('WhatsApp OTP is not configured — fill in the fields below', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    // Setup Form
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-whatsapp"></span> ' . esc_html__('WhatsApp Business API', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<p class="bma-card-desc">' . esc_html__('Connect your Meta WhatsApp Business API to send OTP codes via WhatsApp. You need a WhatsApp Business account and an approved message template.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';

    echo '<form method="post">';
    wp_nonce_field('brewmyapp_whatsapp_save_action', 'brewmyapp_whatsapp_save_nonce');

    echo '<table class="bma-form-table">';

    echo '<tr>';
    echo '<th><label for="brewmyapp_wa_token">' . esc_html__('API Access Token', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</label></th>';
    echo '<td>';
    echo '<input name="brewmyapp_wa_token" id="brewmyapp_wa_token" type="password" class="bma-input" value="' . esc_attr($token) . '" placeholder="EAAxxxxxxxx…" />';
    echo '<span class="bma-field-hint">' . esc_html__('Your permanent or temporary access token from the Meta Developer Console.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</td>';
    echo '</tr>';

    echo '<tr>';
    echo '<th><label for="brewmyapp_wa_send_url">' . esc_html__('Message Send URL', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</label></th>';
    echo '<td>';
    echo '<input name="brewmyapp_wa_send_url" id="brewmyapp_wa_send_url" type="url" class="bma-input bma-input-wide" value="' . esc_attr($url) . '" placeholder="https://graph.facebook.com/v18.0/PHONE_NUMBER_ID/messages" />';
    echo '<span class="bma-field-hint">' . wp_kses_post(__('The WhatsApp Cloud API endpoint. Format: <code>https://graph.facebook.com/v18.0/{PHONE_NUMBER_ID}/messages</code>', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</span>';
    echo '</td>';
    echo '</tr>';

    echo '<tr>';
    echo '<th><label for="brewmyapp_wa_otp_template">' . esc_html__('Template Name', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</label></th>';
    echo '<td>';
    echo '<input name="brewmyapp_wa_otp_template" id="brewmyapp_wa_otp_template" type="text" class="bma-input" value="' . esc_attr($template) . '" placeholder="e.g. otp_verification" />';
    echo '<span class="bma-field-hint">' . esc_html__('The approved WhatsApp message template name. Leave blank to send a plain text OTP message.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</td>';
    echo '</tr>';

    echo '<tr>';
    echo '<th><label for="brewmyapp_wa_template_lang">' . esc_html__('Template Language', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</label></th>';
    echo '<td>';
    echo '<input name="brewmyapp_wa_template_lang" id="brewmyapp_wa_template_lang" type="text" class="bma-input" style="max-width:200px" value="' . esc_attr($lang) . '" placeholder="en_US" />';
    echo '<span class="bma-field-hint">' . wp_kses_post(__('Language code of your template (e.g. <code>en_US</code>, <code>hi</code>).', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</span>';
    echo '</td>';
    echo '</tr>';

    echo '</table>';
    echo '<button type="submit" name="brewmyapp_whatsapp_save" class="bma-btn bma-btn-primary"><span class="dashicons dashicons-saved"></span> ' . esc_html__('Save WhatsApp Settings', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</button>';
    echo '</form>';
    echo '</div>';

    // How it works
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-editor-help"></span> ' . esc_html__('How it works', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(220px, 1fr));gap:20px;margin-top:8px;">';

    echo '<div style="text-align:center;padding:16px 12px;">';
    echo '<div style="font-size:32px;margin-bottom:8px;">1&#xFE0F;&#x20E3;</div>';
    echo '<strong style="font-size:13px;color:#18181b;">' . esc_html__('User enters phone', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</strong>';
    echo '<p style="font-size:12px;color:#71717a;margin:4px 0 0;">' . esc_html__('Your app sends the phone number to the API', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '</div>';

    echo '<div style="text-align:center;padding:16px 12px;">';
    echo '<div style="font-size:32px;margin-bottom:8px;">2&#xFE0F;&#x20E3;</div>';
    echo '<strong style="font-size:13px;color:#18181b;">' . esc_html__('OTP sent via WhatsApp', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</strong>';
    echo '<p style="font-size:12px;color:#71717a;margin:4px 0 0;">' . esc_html__('A 6-digit code is delivered instantly', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '</div>';

    echo '<div style="text-align:center;padding:16px 12px;">';
    echo '<div style="font-size:32px;margin-bottom:8px;">3&#xFE0F;&#x20E3;</div>';
    echo '<strong style="font-size:13px;color:#18181b;">' . esc_html__('User verifies OTP', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</strong>';
    echo '<p style="font-size:12px;color:#71717a;margin:4px 0 0;">' . esc_html__('App verifies the code and receives a JWT token', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '</div>';

    echo '<div style="text-align:center;padding:16px 12px;">';
    echo '<div style="font-size:32px;margin-bottom:8px;">4&#xFE0F;&#x20E3;</div>';
    echo '<strong style="font-size:13px;color:#18181b;">' . esc_html__('User is logged in', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</strong>';
    echo '<p style="font-size:12px;color:#71717a;margin:4px 0 0;">' . esc_html__('JWT authenticates all subsequent app requests', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '</div>';

    echo '</div>';
    echo '</div>';

    // Step-by-step Setup Guide
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-welcome-learn-more"></span> ' . esc_html__('Setup Guide — Where to Get Your Credentials', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<p class="bma-card-desc">' . esc_html__('Follow these steps to set up WhatsApp OTP. You need a Meta Business account and a WhatsApp Business API app.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';

    // Step 1
    echo '<div style="border:1px solid var(--bma-gray-200);border-radius:8px;padding:20px;margin-bottom:16px;">';
    echo '<h3 style="font-size:14px;font-weight:700;color:#18181b;margin:0 0 8px;">&#9312; ' . esc_html__('Create a Meta Developer App', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h3>';
    echo '<ol style="margin:0;padding-left:20px;font-size:13px;color:#52525b;line-height:1.8;">';
    echo '<li>' . wp_kses_post(__('Go to <a href="https://developers.facebook.com/apps/" target="_blank" rel="noopener noreferrer">Meta for Developers</a> and click <strong>Create App</strong>.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    echo '<li>' . wp_kses_post(__('Select <strong>Business</strong> as the app type, then choose your Business portfolio.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    echo '<li>' . wp_kses_post(__('Under the app dashboard, click <strong>Add Product</strong> and choose <strong>WhatsApp</strong>.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    echo '</ol>';
    echo '</div>';

    // Step 2
    echo '<div style="border:1px solid var(--bma-gray-200);border-radius:8px;padding:20px;margin-bottom:16px;">';
    echo '<h3 style="font-size:14px;font-weight:700;color:#18181b;margin:0 0 8px;">&#9313; ' . esc_html__('Get Your Access Token', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h3>';
    echo '<ol style="margin:0;padding-left:20px;font-size:13px;color:#52525b;line-height:1.8;">';
    echo '<li>' . wp_kses_post(__('In your app dashboard, go to <strong>WhatsApp &rarr; API Setup</strong>.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    echo '<li>' . wp_kses_post(__('You will see a <strong>Temporary access token</strong> (valid for 24 hours). Copy it for testing.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    echo '<li>' . wp_kses_post(__('For production, create a <strong>System User</strong> in <a href="https://business.facebook.com/settings/system-users" target="_blank" rel="noopener noreferrer">Business Settings &rarr; System Users</a>, generate a permanent token with <code>whatsapp_business_messaging</code> permission, and paste it in the <strong>API Access Token</strong> field above.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    echo '</ol>';
    echo '</div>';

    // Step 3
    echo '<div style="border:1px solid var(--bma-gray-200);border-radius:8px;padding:20px;margin-bottom:16px;">';
    echo '<h3 style="font-size:14px;font-weight:700;color:#18181b;margin:0 0 8px;">&#9314; ' . esc_html__('Find Your Phone Number ID & Message Send URL', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h3>';
    echo '<ol style="margin:0;padding-left:20px;font-size:13px;color:#52525b;line-height:1.8;">';
    echo '<li>' . wp_kses_post(__('In the <strong>WhatsApp &rarr; API Setup</strong> section, find the <strong>Phone Number ID</strong> listed under your business phone number.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    echo '<li>' . wp_kses_post(__('Your Message Send URL is: <code>https://graph.facebook.com/v18.0/{PHONE_NUMBER_ID}/messages</code>', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    echo '<li>' . wp_kses_post(__('Replace <code>{PHONE_NUMBER_ID}</code> with your actual Phone Number ID and paste the full URL in the <strong>Message Send URL</strong> field above.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    echo '</ol>';
    echo '</div>';

    // Step 4
    echo '<div style="border:1px solid var(--bma-gray-200);border-radius:8px;padding:20px;margin-bottom:16px;">';
    echo '<h3 style="font-size:14px;font-weight:700;color:#18181b;margin:0 0 8px;">&#9315; ' . esc_html__('Create a Message Template (Optional)', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h3>';
    echo '<ol style="margin:0;padding-left:20px;font-size:13px;color:#52525b;line-height:1.8;">';
    echo '<li>' . wp_kses_post(__('Go to <a href="https://business.facebook.com/wa/manage/message-templates/" target="_blank" rel="noopener noreferrer">WhatsApp Manager &rarr; Message Templates</a>.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    echo '<li>' . wp_kses_post(__('Click <strong>Create Template</strong>, select category <strong>Authentication</strong>, and name it (e.g. <code>otp_verification</code>).', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    echo '<li>' . wp_kses_post(__('Add a body parameter <code>{{1}}</code> for the OTP code. Submit for approval (usually approved within minutes).', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    echo '<li>' . wp_kses_post(__('Once approved, enter the template name in the <strong>Template Name</strong> field above. If left blank, a plain text OTP message is sent instead.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    echo '</ol>';
    echo '</div>';

    // Step 5
    echo '<div style="border:1px solid var(--bma-gray-200);border-radius:8px;padding:20px;">';
    echo '<h3 style="font-size:14px;font-weight:700;color:#18181b;margin:0 0 8px;">&#9316; ' . esc_html__('Test Your Setup', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h3>';
    echo '<ol style="margin:0;padding-left:20px;font-size:13px;color:#52525b;line-height:1.8;">';
    echo '<li>' . esc_html__('Save the settings above after filling in all fields.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</li>';
    echo '<li>' . wp_kses_post(__('Open your mobile app and trigger a WhatsApp OTP login to verify everything works.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</li>';
    /* translators: %s: URL to the API Logs admin page */
    echo '<li>' . wp_kses_post(sprintf(__('Check the <a href="%s">API Logs</a> page for any errors if OTP delivery fails.', 'brewmyapp-mobile-app-builder-for-woocommerce'), esc_url(admin_url('admin.php?page=brewmyapp-log')))) . '</li>';
    echo '</ol>';
    echo '</div>';

    echo '</div>';

    brewmyapp_page_footer();
}


// ══════════════════════════════════════════════
// LOGS PAGE
// ══════════════════════════════════════════════
function brewmyapp_render_log_page()
{
    if (!current_user_can('manage_options')) {
        return;
    }

    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Pagination parameter, no state change.
    $page = isset($_GET['paged']) ? max(1, (int) $_GET['paged']) : 1;
    $per_page = 50;
    $entries = brewmyapp_get_api_logs();
    $total = count($entries);
    $total_pages = $per_page > 0 ? (int) ceil($total / $per_page) : 1;

    $offset = ($page - 1) * $per_page;
    $paged_entries = array_slice($entries, $offset, $per_page);

    // Filter logs
    $email_entries = brewmyapp_filter_email_logs($entries);
    $whatsapp_entries = brewmyapp_filter_whatsapp_logs($entries);

    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Tab filter parameter, no state change.
    $tab = isset($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : 'all';

    brewmyapp_page_header(__('API Logs', 'brewmyapp-mobile-app-builder-for-woocommerce'), __('Full audit trail of every OTP request, verification, and API call', 'brewmyapp-mobile-app-builder-for-woocommerce'));

    // Stats Row
    echo '<div class="bma-stats-grid" style="grid-template-columns: repeat(3, 1fr);">';

    echo '<div class="bma-stat-card">';
    echo '<div class="bma-stat-icon amber"><span class="dashicons dashicons-media-text"></span></div>';
    echo '<div class="bma-stat-value">' . esc_html($total) . '</div>';
    echo '<div class="bma-stat-label">' . esc_html__('Total Log Entries', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</div>';
    echo '</div>';

    echo '<div class="bma-stat-card">';
    echo '<div class="bma-stat-icon blue"><span class="dashicons dashicons-email"></span></div>';
    echo '<div class="bma-stat-value">' . esc_html(count($email_entries)) . '</div>';
    echo '<div class="bma-stat-label">' . esc_html__('Email OTP Entries', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</div>';
    echo '</div>';

    echo '<div class="bma-stat-card">';
    echo '<div class="bma-stat-icon emerald"><span class="dashicons dashicons-whatsapp"></span></div>';
    echo '<div class="bma-stat-value">' . esc_html(count($whatsapp_entries)) . '</div>';
    echo '<div class="bma-stat-label">' . esc_html__('WhatsApp OTP Sends', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</div>';
    echo '</div>';

    echo '</div>';

    // Tabs
    $base_url = admin_url('admin.php?page=brewmyapp-log');
    echo '<div class="bma-tabs">';
    echo '<a href="' . esc_url(add_query_arg('tab', 'all', $base_url)) . '" class="bma-tab ' . ($tab === 'all' ? 'active' : '') . '">' . esc_html__('All Logs', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '<a href="' . esc_url(add_query_arg('tab', 'email', $base_url)) . '" class="bma-tab ' . ($tab === 'email' ? 'active' : '') . '">' . esc_html__('Email OTP', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '<a href="' . esc_url(add_query_arg('tab', 'whatsapp', $base_url)) . '" class="bma-tab ' . ($tab === 'whatsapp' ? 'active' : '') . '">' . esc_html__('WhatsApp OTP', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    echo '</div>';

    // Select entries for current tab
    if ($tab === 'email') {
        $display_entries = array_slice($email_entries, -100);
        $display_title = __('Email OTP Log', 'brewmyapp-mobile-app-builder-for-woocommerce');
    } elseif ($tab === 'whatsapp') {
        $display_entries = array_slice($whatsapp_entries, -100);
        $display_title = __('WhatsApp OTP Log', 'brewmyapp-mobile-app-builder-for-woocommerce');
    } else {
        $display_entries = $paged_entries;
        $display_title = __('All Log Entries', 'brewmyapp-mobile-app-builder-for-woocommerce');
    }

    $display_items = array_map(function ($entry, $index) use ($offset, $tab) {
        $base = $tab === 'all' ? ($offset + $index + 1) : ($index + 1);
        return array('id' => $base, 'data' => $entry);
    }, $display_entries, array_keys($display_entries));

    // Export JSON
    $export_data = wp_json_encode(array('total' => count($display_entries), 'items' => $display_items), JSON_PRETTY_PRINT);

    echo '<div class="bma-card">';
    echo '<div class="bma-card-header">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-media-text"></span> ' . esc_html($display_title) . '</h2>';
    echo '<button class="bma-copy-btn" data-copy-target="bma-export-json"><span class="dashicons dashicons-clipboard"></span> ' . esc_html__('Export JSON', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</button>';
    echo '</div>';
    echo '<textarea id="bma-export-json" class="bma-sr-only">' . esc_textarea($export_data) . '</textarea>';

    if (empty($display_items)) {
        echo '<div class="bma-empty-state">';
        echo '<span class="dashicons dashicons-media-text"></span>';
        echo '<p>' . esc_html__('No log entries found for this filter.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
        echo '</div>';
    } else {
        echo '<div class="bma-table-wrap">';
        echo '<table class="bma-table">';
        echo '<thead><tr>';
        echo '<th style="width:40px">#</th>';
        echo '<th style="width:180px">' . esc_html__('Timestamp', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</th>';
        echo '<th>' . esc_html__('Route', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</th>';
        echo '<th style="width:70px">' . esc_html__('Status', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</th>';
        echo '<th style="width:70px">' . esc_html__('User', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</th>';
        echo '<th>' . esc_html__('Details', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</th>';
        echo '</tr></thead>';
        echo '<tbody>';

        foreach ($display_items as $item) {
            $data = is_array($item['data'] ?? null) ? $item['data'] : array();
            $timestamp = esc_html($data['timestamp'] ?? '—');
            $route = esc_html($data['route'] ?? '—');
            $status = (int) ($data['status'] ?? 0);
            $user_id_display = esc_html($data['user_id'] ?? '—');
            $extra = $data['extra'] ?? null;
            if (is_array($extra)) {
                $extra = wp_json_encode($extra, JSON_UNESCAPED_SLASHES);
            }
            $extra = esc_html(trim((string) $extra));
            if ($extra === '') {
                $extra = '—';
            }

            // Status badge color
            $status_class = 'bma-badge-neutral';
            if ($status >= 200 && $status < 300) {
                $status_class = 'bma-badge-success';
            } elseif ($status >= 400 && $status < 500) {
                $status_class = 'bma-badge-warning';
            } elseif ($status >= 500) {
                $status_class = 'bma-badge-error';
            }

            echo '<tr>';
            echo '<td class="bma-mono">' . esc_html($item['id']) . '</td>';
            echo '<td class="bma-mono" style="font-size:11px;">' . esc_html($timestamp) . '</td>';
            echo '<td class="bma-mono" style="font-size:12px;">' . esc_html($route) . '</td>';
            echo '<td><span class="bma-badge ' . esc_attr($status_class) . '">' . esc_html($status) . '</span></td>';
            echo '<td>' . esc_html($user_id_display) . '</td>';
            echo '<td style="font-size:12px;color:#71717a;">' . esc_html($extra) . '</td>';
            echo '</tr>';
        }

        echo '</tbody></table>';
        echo '</div>';
    }

    // Pagination (only for "all" tab)
    if ($tab === 'all' && $total_pages > 1) {
        echo '<div class="bma-pagination">';
        if ($page > 1) {
            echo '<a href="' . esc_url(add_query_arg(array('paged' => $page - 1, 'tab' => 'all'), $base_url)) . '">&larr; ' . esc_html__('Previous', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
        }
        /* translators: 1: current page, 2: total pages */
        echo '<span class="current">' . esc_html(sprintf(__('Page %1$d of %2$d', 'brewmyapp-mobile-app-builder-for-woocommerce'), $page, $total_pages)) . '</span>';
        if ($page < $total_pages) {
            echo '<a href="' . esc_url(add_query_arg(array('paged' => $page + 1, 'tab' => 'all'), $base_url)) . '">' . esc_html__('Next', 'brewmyapp-mobile-app-builder-for-woocommerce') . ' &rarr;</a>';
        }
        echo '</div>';
    }

    echo '</div>';

    brewmyapp_page_footer();
}

// ══════════════════════════════════════════════
// WELCOME / GET STARTED PAGE
// ══════════════════════════════════════════════
function brewmyapp_render_welcome_page()
{
    if (!current_user_can('manage_options')) {
        return;
    }
    // One unified experience: a clean Connect panel when not connected, and the
    // full connected dashboard (Live Preview QR, OTP testing, subscription,
    // pricing + upgrade) once connected — identical to the main BrewmyApp page,
    // mirroring the Shopify embedded app.
    brewmyapp_render_embedded_app_page();
}

// ══════════════════════════════════════════════
// API REFERENCE PAGE
// ══════════════════════════════════════════════
function brewmyapp_render_api_page()
{
    if (!current_user_can('manage_options')) {
        return;
    }

    $base = rest_url('bma/v1/');

    brewmyapp_page_header(__('API Reference', 'brewmyapp-mobile-app-builder-for-woocommerce'), __('All REST API endpoints, authentication details, and request examples', 'brewmyapp-mobile-app-builder-for-woocommerce'));

    // Base URL
    echo '<div class="bma-card">';
    echo '<div class="bma-card-header">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-admin-site-alt3"></span> ' . esc_html__('Base URL', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<button class="bma-copy-btn" data-copy-text="' . esc_attr($base) . '"><span class="dashicons dashicons-clipboard"></span> ' . esc_html__('Copy', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</button>';
    echo '</div>';
    echo '<div class="bma-code-block"><code>' . esc_html($base) . '</code></div>';
    echo '</div>';

    // Authentication Endpoints
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-lock"></span> ' . esc_html__('Authentication', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<p class="bma-card-desc">' . esc_html__('OTP-based passwordless authentication. Returns a JWT token on successful verification.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';

    echo '<div class="bma-endpoint-group">';
    echo '<h3>' . esc_html__('Email OTP', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h3>';

    echo '<div class="bma-endpoint">';
    echo '<span class="bma-method bma-method-post">POST</span>';
    echo '<span class="bma-endpoint-path">/otp/request</span>';
    echo '<span class="bma-endpoint-desc">' . esc_html__('Send OTP to email', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '<div class="bma-endpoint">';
    echo '<span class="bma-method bma-method-post">POST</span>';
    echo '<span class="bma-endpoint-path">/otp/verify</span>';
    echo '<span class="bma-endpoint-desc">' . esc_html__('Verify OTP → get JWT', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '</div>';

    echo '<div class="bma-endpoint-group">';
    echo '<h3>' . esc_html__('WhatsApp OTP', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h3>';

    echo '<div class="bma-endpoint">';
    echo '<span class="bma-method bma-method-post">POST</span>';
    echo '<span class="bma-endpoint-path">/whatsapp/otp/request</span>';
    echo '<span class="bma-endpoint-desc">' . esc_html__('Send OTP via WhatsApp', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '<div class="bma-endpoint">';
    echo '<span class="bma-method bma-method-post">POST</span>';
    echo '<span class="bma-endpoint-path">/whatsapp/otp/verify</span>';
    echo '<span class="bma-endpoint-desc">' . esc_html__('Verify OTP → get JWT', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '</div>';
    echo '</div>';

    // Payment Endpoints
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-cart"></span> ' . esc_html__('Payments', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<p class="bma-card-desc">' . esc_html__('Generate secure payment URLs for in-app checkout. Customers are auto-logged in via token.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';

    echo '<div class="bma-endpoint">';
    echo '<span class="bma-method bma-method-post">POST</span>';
    echo '<span class="bma-endpoint-path">/payment/order-pay-url</span>';
    echo '<span class="bma-endpoint-desc">' . esc_html__('Generate payment link', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '<div class="bma-endpoint">';
    echo '<span class="bma-method bma-method-get">GET</span>';
    echo '<span class="bma-endpoint-path">/payment/order-status</span>';
    echo '<span class="bma-endpoint-desc">' . esc_html__('Check order payment status', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '</div>';

    // Push Notification Endpoints
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-megaphone"></span> ' . esc_html__('Push Notifications', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<p class="bma-card-desc">' . esc_html__('Register device tokens and send push notifications via Expo. Requires JWT authentication.', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';

    echo '<div class="bma-endpoint">';
    echo '<span class="bma-method bma-method-post">POST</span>';
    echo '<span class="bma-endpoint-path">/push-token</span>';
    echo '<span class="bma-endpoint-desc">' . esc_html__('Register push token (JWT required)', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '<div class="bma-endpoint">';
    echo '<span class="bma-method bma-method-delete">DEL</span>';
    echo '<span class="bma-endpoint-path">/push-token</span>';
    echo '<span class="bma-endpoint-desc">' . esc_html__('Unregister push token (JWT required)', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '<div class="bma-endpoint">';
    echo '<span class="bma-method bma-method-post">POST</span>';
    echo '<span class="bma-endpoint-path">/push-notification/send</span>';
    echo '<span class="bma-endpoint-desc">' . esc_html__('Send notification (Admin JWT required)', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '<div class="bma-endpoint">';
    echo '<span class="bma-method bma-method-get">GET</span>';
    echo '<span class="bma-endpoint-path">/push-tokens/users</span>';
    echo '<span class="bma-endpoint-desc">' . esc_html__('List users with push tokens', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '</div>';

    // Utility Endpoints
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-admin-tools"></span> ' . esc_html__('Utility', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';

    echo '<div class="bma-endpoint">';
    echo '<span class="bma-method bma-method-get">GET</span>';
    echo '<span class="bma-endpoint-path">/ping</span>';
    echo '<span class="bma-endpoint-desc">' . esc_html__('Health check — returns plugin status', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '<div class="bma-endpoint">';
    echo '<span class="bma-method bma-method-get">GET</span>';
    echo '<span class="bma-endpoint-path">/logs/store-login</span>';
    echo '<span class="bma-endpoint-desc">' . esc_html__('Retrieve API logs (paginated)', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</span>';
    echo '</div>';

    echo '</div>';

    // Auth Guide
    echo '<div class="bma-card">';
    echo '<h2 class="bma-card-title"><span class="dashicons dashicons-editor-help"></span> ' . esc_html__('Authentication Guide', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h2>';
    echo '<p class="bma-card-desc">' . esc_html__('Include the JWT token in the Authorization header for protected endpoints:', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '<div class="bma-code-block"><code>Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...</code></div>';

    echo '<h3 style="margin-top:20px;font-size:14px;font-weight:600;">' . esc_html__('OTP Flow Example', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</h3>';
    echo '<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(300px, 1fr));gap:16px;margin-top:12px;">';

    echo '<div>';
    echo '<p style="font-size:12px;font-weight:600;color:#52525b;margin-bottom:6px;">' . esc_html__('1. Request OTP', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '<div class="bma-code-block" style="font-size:12px;"><code>POST /bma/v1/otp/request' . "\n" . 'Content-Type: application/json' . "\n\n" . '{"email": "user@example.com"}</code></div>';
    echo '</div>';

    echo '<div>';
    echo '<p style="font-size:12px;font-weight:600;color:#52525b;margin-bottom:6px;">' . esc_html__('2. Verify OTP', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</p>';
    echo '<div class="bma-code-block" style="font-size:12px;"><code>POST /bma/v1/otp/verify' . "\n" . 'Content-Type: application/json' . "\n\n" . '{"email": "user@example.com", "otp": "123456"}</code></div>';
    echo '</div>';

    echo '</div>';

    echo '<p style="margin-top:12px;font-size:13px;color:#52525b;">' . wp_kses_post(__('Response includes a <code>token</code> field with the JWT bearer token.', 'brewmyapp-mobile-app-builder-for-woocommerce')) . '</p>';

    echo '</div>';

    brewmyapp_page_footer();
}

// ══════════════════════════════════════════════
// ACTIVATION & OPTION MIGRATION
// ══════════════════════════════════════════════

register_activation_hook(__FILE__, function () {
    // Every activation: clear stale connection state so the Get Started
    // page accurately reflects the real status, and redirect there.
    delete_option('brewmyapp_connected_at');
    set_transient('brewmyapp_activation_redirect', true, 30);

    // Migrate old option names to new ones (v1.x → v2.0).
    $migrations = array(
        'app_jwt_secret' => 'brewmyapp_jwt_secret',
        'app_jwt_expiry' => 'brewmyapp_jwt_expiry',
        'WA_TOKEN' => 'brewmyapp_wa_token',
        'app_whatsapp_otp_template' => 'brewmyapp_wa_otp_template',
        'app_whatsapp_template_lang' => 'brewmyapp_wa_template_lang',
        'wa_message_send_url' => 'brewmyapp_wa_send_url',
    );

    foreach ($migrations as $old_key => $new_key) {
        $old_value = get_option($old_key);
        if ($old_value !== false && get_option($new_key) === false) {
            update_option($new_key, $old_value);
            delete_option($old_key);
        }
    }



    // Flush rewrite rules for /bma-pay/ endpoint.
    flush_rewrite_rules();
});

register_deactivation_hook(__FILE__, function () {
    flush_rewrite_rules();
});

// ── Redirect to Get Started page on activation ──
add_action('admin_init', function () {
    if (get_transient('brewmyapp_activation_redirect')) {
        delete_transient('brewmyapp_activation_redirect');
        if (!isset($_GET['activate-multi'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            wp_safe_redirect(admin_url('admin.php?page=brewmyapp-welcome'));
            exit;
        }
    }
});

// Add "Connect" and "Settings" links on the plugins page.
add_filter('plugin_action_links_' . plugin_basename(__FILE__), function ($links) {
    $app = '<a href="' . esc_url(admin_url('admin.php?page=brewmyapp-welcome')) . '"><strong>' . esc_html__('Get Started', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</strong></a>';
    $settings = '<a href="' . esc_url(admin_url('admin.php?page=brewmyapp-settings')) . '">' . esc_html__('Settings', 'brewmyapp-mobile-app-builder-for-woocommerce') . '</a>';
    array_unshift($links, $app, $settings);
    return $links;
});

// Add "Get the App" row meta link.
add_filter('plugin_row_meta', function ($links, $file) {
    if ($file === plugin_basename(__FILE__)) {
        $links[] = '<a href="' . esc_url('https://www.brewmyapp.io') . '" target="_blank" style="font-weight:600;">' . esc_html__('Build Your Mobile App', 'brewmyapp-mobile-app-builder-for-woocommerce') . ' &rarr;</a>';
    }
    return $links;
}, 10, 2);

// ── Frontend Backlink: "Powered by BrewmyApp" ──
// Prints a small credit link in the public-facing footer linking to brewmyapp.io.
// Site owners can disable via: add_filter('brewmyapp_show_credit', '__return_false');
add_action('wp_footer', function () {
    if (is_admin()) {
        return;
    }
    $show_credit = get_option('brewmyapp_show_credit', 'no') === 'yes';
    if (!apply_filters('brewmyapp_show_credit', $show_credit)) {
        return;
    }
    echo '<div class="brewmyapp-credit" style="text-align:center;font-size:12px;color:#71717a;padding:16px 12px;line-height:1.5;">';
    echo esc_html__('Mobile app powered by', 'brewmyapp-mobile-app-builder-for-woocommerce');
    echo ' <a href="' . esc_url('https://www.brewmyapp.io/') . '" target="_blank" rel="noopener" style="color:#3b82f6;text-decoration:none;font-weight:600;">BrewmyApp</a>';
    echo '</div>';
}, 99);
