<?php

namespace Twnt4All\Backend;

use Twnt4All\Exception\OpenSslNotLoadedException;
use Twnt4All\Exception\PropertiesNotSetException;
use Twnt4All\Support\Paths;
use Twnt4All\Support\Cipher;
use Twnt4All\Engine\Support\EngineHelper;
use Twnt4All\Engine\Support\MarkupBuilder;
use Twnt4All\Support\WpBridge;

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

/**
 * Renders the TWINT configuration page under the WooCommerce admin menu.
 */
class ConfigPage
{
    private static $instance;

    protected $id;
    protected $label;

    /**
     * Hook into WC functions.
     */
    public function __construct()
    {
        $this->id    = 'twint';
        // $this->label is intentionally left unset: assigning __() here would
        // call _load_textdomain_just_in_time() during plugins_loaded, which is
        // too early for translation loading since WordPress 6.7. The wizard
        // renders its own headings via render_admin_page(), so no other code
        // reads this property.

    }

    public static function get_instance()
    {
        if ( empty( static::$instance ) ) {
            static::$instance = new static;
        }

        return static::$instance;
    }

    /**
     * Whether the Pay4All Shop (pay4all-form) plugin is loaded.
     *
     * Gates the « Pay4All Shop » wizard tab — those status settings only
     * affect Pay4All Shop orders (stored in `t4a_twint_p4form_status_*` and
     * read by FormDomainEvents::mark_payment_paid). Independent from the
     * WooCommerce status keys (`t4a_twint_settings_order_status_*`) which
     * are read by pay4all-pro's WooBridge.
     */
    public static function has_pay4all_form() : bool
    {
        return defined( 'P4ALL_FORM_VERSION' ) || class_exists( '\\Pay4All\\Form\\Plugin' );
    }

    /**
     * Whether WooCommerce is loaded — gates the « WooCommerce » wizard tab.
     * Those settings write to `t4a_twint_settings_order_status_*` which
     * pay4all-pro's WooBridge reads at payment completion.
     */
    public static function has_woocommerce() : bool
    {
        return class_exists( 'WooCommerce' );
    }

    /**
     * Localised URL of the Pay4All tutorial page for the intro step's
     * « Besoin d'aide » call-to-action. French is served on the base URL
     * (no query arg — the site's primary language); every other supported
     * locale gets a `?lang=<code>` hint that the Pay4All landing page
     * resolves to the right translation before the #tutorials fragment.
     */
    public static function tutorial_url() : string
    {
        $lang = self::current_supported_lang();
        $base = 'https://pay4all.ch/p/twint-woocommerce';
        $qs   = ( $lang === 'fr' ) ? '' : '?lang=' . $lang;
        return $base . $qs . '#tutorials';
    }

    /**
     * Runs the wizard POST handler EARLY on admin_init so wp_safe_redirect()
     * can fire before WordPress emits the admin chrome (otherwise headers are
     * already sent and saving any step silently fails with a "Cannot modify
     * header information" warning).
     *
     * Hooked from Bootstrap::on_plugins_loaded(). Safe to call on every admin
     * request — the handler short-circuits unless a t4a_action POST is present.
     */
    public static function maybe_handle_wizard_post()
    {
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- dispatch-only guard ; each t4a_action branch inside handle_wizard_post() verifies its own nonce before running.
        if ( empty( $_POST[ 't4a_action' ] ) ) { return; }
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only routing check (identifies the wizard page) ; state changes below are gated by capability + per-action nonce.
        $page = isset( $_GET[ 'page' ] ) ? sanitize_key( wp_unslash( $_GET[ 'page' ] ) ) : '';
        if ( $page !== 'pay4all-settings' ) { return; }
        if ( ! current_user_can( 'manage_options' ) ) { return; }

        $instance = static::get_instance();
        $flash    = $instance->handle_wizard_post();
        if ( is_array( $flash ) ) {
            set_transient( 't4a_wizard_flash', $flash, 30 );
        }
    }

    /**
     * Render the standalone TWINT configuration page registered as a WooCommerce submenu.
     */
    public static function render_admin_page()
    {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'You do not have permission to access this page.', 'pay4all-for-twint' ) );
        }

        $instance = static::get_instance();

        // POST actions are processed earlier on admin_init (see
        // maybe_handle_wizard_post) so wp_safe_redirect() works. The flash
        // notice — for both successes and validation errors — is read back
        // from the transient set by the early handler.
        $flash = $instance->consume_flash();

        // 4-step wizard: 1=Intro, 2=Credentials, 3=Order statuses (combined
        // Pay4All Shop + WooCommerce sections, only shown when at least one
        // host plugin is present), 4=Status snapshot.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only step navigation, no state mutation.
        $step = isset( $_GET[ 'step' ] ) ? max( 1, min( 4, (int) $_GET[ 'step' ] ) ) : $instance->compute_initial_step();

        $instance->enqueue_wizard_assets();
        ?>
        <div class="wrap t4a-wizard">
            <h1 class="t4a-wizard-title"><?php echo esc_html__( 'TWINT', 'pay4all-for-twint' ); ?></h1>

            <?php if ( !empty( $flash ) ) : ?>
                <div class="notice notice-<?php echo esc_attr( $flash[ 'type' ] ); ?> is-dismissible">
                    <p><?php echo wp_kses_post( $flash[ 'message' ] ); ?></p>
                </div>
            <?php endif; ?>

            <ol class="t4a-wizard-steps">
                <?php
                $labels = [
                    1 => __( 'Setup', 'pay4all-for-twint' ),
                    2 => __( 'Credentials', 'pay4all-for-twint' ),
                ];
                if ( self::has_pay4all_form() || self::has_woocommerce() ) {
                    $labels[3] = __( 'Order', 'pay4all-for-twint' );
                }
                $labels[4] = __( 'Status', 'pay4all-for-twint' );

                // Sequential visible index so hiding step 3 doesn't leave a
                // gap in the numbered badges ("1, 2, 4" would look like a bug).
                $visible_index = 0;
                foreach ( $labels as $num => $label ) :
                    $visible_index++;
                    $state    = ( $num === $step ) ? 'is-active' : ( ( $num < $step ) ? 'is-done' : 'is-todo' );
                    $url      = add_query_arg( [ 'page' => 'pay4all-settings', 'step' => $num ], admin_url( 'admin.php' ) );
                    $is_final = ( $num === 4 );
                ?>
                <li class="<?php echo esc_attr( $state . ( $is_final ? ' is-final' : '' ) ); ?>">
                    <a href="<?php echo esc_url( $url ); ?>">
                        <span class="t4a-step-num">
                            <?php if ( $is_final ) : ?>
                                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>
                            <?php else : ?>
                                <?php echo (int) $visible_index; ?>
                            <?php endif; ?>
                        </span>
                        <span class="t4a-step-label"><?php echo esc_html( $label ); ?></span>
                    </a>
                </li>
                <?php endforeach; ?>
            </ol>

            <?php
            switch ( $step ) {
                case 2:
                    $instance->render_step_credentials();
                    break;
                case 3:
                    // Combined Order tab — renders the Pay4All Shop section
                    // if that host plugin is present AND the WooCommerce
                    // section if WC is present. Falls through to the final
                    // step when neither is installed.
                    if ( self::has_pay4all_form() || self::has_woocommerce() ) {
                        $instance->render_step_order();
                    } else {
                        $instance->render_step_status();
                    }
                    break;
                case 4:
                    $instance->render_step_status();
                    break;
                default:
                    $instance->render_step_intro();
            }
            ?>
        </div>
        <?php
    }

    /**
     * Build a sensible default register identifier from the WordPress site title:
     * stripped of accents, special characters and whitespace, capped at 50 chars.
     */
    protected static function default_register_id()
    {
        $title = get_bloginfo( 'name' );
        if ( function_exists( 'remove_accents' ) ) {
            $title = remove_accents( $title );
        }
        $clean = preg_replace( '/[^A-Za-z0-9]/', '', (string) $title );
        $clean = substr( $clean, 0, 50 );
        return $clean !== '' ? $clean : 'Register1';
    }

    /**
     * Step 1: orientation page explaining the prerequisites on the TWINT portal.
     */
    protected function render_step_intro()
    {
        $next_url     = add_query_arg( [ 'page' => 'pay4all-settings', 'step' => 2 ], admin_url( 'admin.php' ) );
        $tutorial_url = self::tutorial_url();
        ?>
        <section class="t4a-wizard-card">
            <h2><?php esc_html_e( 'Before you begin', 'pay4all-for-twint' ); ?></h2>
            <p>
                <?php esc_html_e( 'This wizard walks you through activating TWINT on your shop step by step. To complete the setup, you first need a TWINT merchant account with a shop created in it.', 'pay4all-for-twint' ); ?>
            </p>
            <a href="<?php echo esc_url( $tutorial_url ); ?>" target="_blank" rel="noopener noreferrer" class="t4a-tutorial-link">
                <span class="t4a-tutorial-icon" aria-hidden="true">
                    <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2.5" y="5" width="19" height="14" rx="2.5"/><polygon points="10.5 9 15.5 12 10.5 15" fill="currentColor" stroke="none"/></svg>
                </span>
                <span class="t4a-tutorial-text">
                    <strong><?php esc_html_e( 'Need help setting up your TWINT account and shop?', 'pay4all-for-twint' ); ?></strong>
                    <span><?php esc_html_e( 'Check out the Pay4All tutorial.', 'pay4all-for-twint' ); ?></span>
                </span>
                <span class="t4a-tutorial-arrow" aria-hidden="true">→</span>
            </a>
        </section>

        <section class="t4a-wizard-card">
            <h2><?php esc_html_e( 'Create your TWINT account and shop', 'pay4all-for-twint' ); ?></h2>
            <ol class="t4a-checklist">
                <li>
                    <strong><?php esc_html_e( 'Create a TWINT account', 'pay4all-for-twint' ); ?></strong>
                    <p class="description"><?php
                        printf(
                            /* translators: %s dynamic value(s). */
                            wp_kses( __( 'Go to %s and click "Sign up" to open a TWINT merchant account. Provide your company details until the account is validated.', 'pay4all-for-twint' ), [ 'a' => [ 'href' => [], 'target' => [], 'rel' => [] ] ] ),
                            '<a href="https://portal.twint.ch" target="_blank" rel="noopener">portal.twint.ch</a>'
                        );
                    ?></p>
                    <p class="t4a-callout"><span class="t4a-callout-label"><?php esc_html_e( 'To prepare', 'pay4all-for-twint' ); ?></span> <?php esc_html_e( 'A recent bank statement of your business account in PDF format - required during signup to verify the IBAN TWINT will use for payouts.', 'pay4all-for-twint' ); ?></p>
                </li>
                <li>
                    <strong><?php esc_html_e( 'Add a shop ("Store")', 'pay4all-for-twint' ); ?></strong>
                    <p class="description"><?php esc_html_e( 'Once signed in, open the STORES section of the portal and create a shop that matches your website. On validation, TWINT assigns a unique UID.', 'pay4all-for-twint' ); ?></p>
                    <p class="t4a-callout t4a-callout-optional"><span class="t4a-callout-label"><?php esc_html_e( 'Optional', 'pay4all-for-twint' ); ?></span> <?php esc_html_e( 'Your shop logo as an 800 x 400 px PNG. It is shown to customers during TWINT payment, but you can add it later from the portal.', 'pay4all-for-twint' ); ?></p>
                </li>
                <li>
                    <strong><?php esc_html_e( 'Generate the shop certificate', 'pay4all-for-twint' ); ?></strong>
                    <p class="description"><?php esc_html_e( 'Still on the portal, a certificate will be generated for this shop. Set (and write down!) the password that protects it. Then download the generated .p12 file and keep it - you will need it in the next step.', 'pay4all-for-twint' ); ?></p>
                </li>
            </ol>
        </section>

        <section class="t4a-wizard-card">
            <h2><?php esc_html_e( 'What you will need to provide', 'pay4all-for-twint' ); ?></h2>
            <ul class="t4a-needs">
                <li>
                    <span class="t4a-need-icon" aria-hidden="true">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="5" width="20" height="14" rx="2"/><circle cx="9" cy="12" r="2.2"/><line x1="14" y1="10" x2="19" y2="10"/><line x1="14" y1="14" x2="17.5" y2="14"/></svg>
                    </span>
                    <div>
                        <strong><?php esc_html_e( 'The shop UID', 'pay4all-for-twint' ); ?></strong>
                        <p class="description"><?php esc_html_e( 'Unique identifier tied to your TWINT shop.', 'pay4all-for-twint' ); ?></p>
                    </div>
                </li>
                <li>
                    <span class="t4a-need-icon" aria-hidden="true">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><circle cx="12" cy="14" r="2.2"/><path d="M10.5 16.4l-1.1 3 2.6-1 2.6 1-1.1-3"/></svg>
                    </span>
                    <div>
                        <strong><?php esc_html_e( 'The certificate file (.p12)', 'pay4all-for-twint' ); ?></strong>
                        <p class="description"><?php esc_html_e( 'Downloaded from the TWINT portal, to be uploaded in the next step.', 'pay4all-for-twint' ); ?></p>
                    </div>
                </li>
                <li>
                    <span class="t4a-need-icon" aria-hidden="true">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="15" r="4"/><path d="M10.85 12.15 19 4"/><path d="M18 5l3 3"/><path d="M15 8l3 3"/></svg>
                    </span>
                    <div>
                        <strong><?php esc_html_e( 'The certificate password', 'pay4all-for-twint' ); ?></strong>
                        <p class="description"><?php esc_html_e( 'The one you set when generating the certificate on the TWINT portal.', 'pay4all-for-twint' ); ?></p>
                    </div>
                </li>
            </ul>
        </section>

        <div class="t4a-actions">
            <span></span>
            <a href="<?php echo esc_url( $next_url ); ?>" class="button button-secondary">
                <?php esc_html_e( 'I have everything, continue →', 'pay4all-for-twint' ); ?>
            </a>
        </div>
        <?php
    }

    /**
     * Determine the initial wizard step when no ?step= is provided.
     * Defaults to the first incomplete step.
     */
    protected function compute_initial_step()
    {
        $uuid     = get_option( 't4a_twint_settings_uuid' );
        $has_cert = file_exists( T4A_CERTIFICATE_PATH );
        $test_ok  = get_option( 't4a_twint_last_test_result' ) === 'success';

        // First-time visitor lands on the intro page.
        if ( ! $uuid && ! $has_cert ) {
            return 1;
        }
        // Credentials missing or the last connection test failed: stay on step 2.
        if ( ! $uuid || ! $has_cert || ! $test_ok ) {
            return 2;
        }
        // Once configured, jump straight to the status snapshot (step 4).
        // The optional Order tab has sensible defaults and only needs to be
        // visited once — the credentials-success redirect (step2_test intent
        // 'continue') lands the merchant on it exactly one time.
        return 4;
    }

    /**
     * Two-letter language code recognised by the plugin's per-locale storage.
     * Anything else falls back to French ('fr').
     */
    protected static function supported_lang_codes()
    {
        return [ 'fr', 'de', 'it', 'en' ];
    }

    /**
     * Resolve the current site/user locale to one of the four supported codes.
     */
    public static function current_supported_lang()
    {
        $locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale();
        $code   = strtolower( substr( (string) $locale, 0, 2 ) );
        return in_array( $code, self::supported_lang_codes(), true ) ? $code : 'fr';
    }

    /**
     * Step 1: collect credentials (Store UUID, certificate password, register ID, cert file).
     */
    protected function render_step_credentials()
    {
        $uuid       = get_option( 't4a_twint_settings_uuid', '' );
        // We deliberately never echo the certificate password back into
        // the form (the audit flagged this). `$has_cert_pw` toggles the
        // « Un mot de passe est déjà enregistré — laissez le champ vide
        // pour le conserver » hint under the field.
        $has_cert_pw = \Twnt4All\Engine\EnvironmentConfig::get_certificate_password() !== '';
        $register   = get_option( 't4a_twint_settings_registerid', '' );
        if ( $register === '' ) {
            $register = self::default_register_id();
        }
        $notify_email = get_option( 't4a_twint_settings_email_on_enroll_failure', '' );
        if ( $notify_email === '' ) {
            $notify_email = get_option( 'admin_email' );
        }
        $cert_ready = file_exists( T4A_CERTIFICATE_PATH );
        $test_result  = get_option( 't4a_twint_last_test_result' );
        $test_message = get_option( 't4a_twint_last_test_message' );
        // The wizard writes the two well-known default messages verbatim in
        // English (see step2_test action at the bottom of this class) so the
        // display layer can translate them into the admin's current locale
        // - otherwise a FR/DE/IT merchant would see the EN original on the
        // "Connected to TWINT" card on this tab. The legacy FR literals are
        // still matched so upgraded installs with a persisted FR value keep
        // rendering translated text.
        if ( $test_message === 'TWINT connection is up and running.'
            || $test_message === 'La connexion à TWINT est opérationnelle.' ) {
            $test_message = __( 'TWINT connection is up and running.', 'pay4all-for-twint' );
        } elseif ( $test_message === 'Connection test failed.'
            || $test_message === 'Le test de connexion a échoué.' ) {
            $test_message = __( 'Connection test failed.', 'pay4all-for-twint' );
        }
        $test_time    = (int) get_option( 't4a_twint_last_test_time', 0 );
        $can_continue = $test_result === 'success';
        ?>
        <form id="t4a-step2-form" method="post" action="" enctype="multipart/form-data" class="t4a-wizard-form">
            <?php wp_nonce_field( 't4a_wizard_step2', 't4a_nonce' ); ?>
            <input type="hidden" name="t4a_action" value="step2_test" />

            <section class="t4a-wizard-card">
                <h2><?php esc_html_e( 'TWINT credentials', 'pay4all-for-twint' ); ?></h2>
                <p class="t4a-card-intro"><?php esc_html_e( 'Enter the information from your TWINT account (merchant portal).', 'pay4all-for-twint' ); ?></p>

                <div class="t4a-field-row">
                    <label for="t4a_twint_settings_email_on_enroll_failure"><?php esc_html_e( 'Email for system notifications', 'pay4all-for-twint' ); ?></label>
                    <input type="email" id="t4a_twint_settings_email_on_enroll_failure" name="t4a_twint_settings_email_on_enroll_failure" value="<?php echo esc_attr( $notify_email ); ?>" />
                    <p class="description"><?php esc_html_e( 'Where to send notifications when TWINT fails. Defaults to the site administrator\'s email.', 'pay4all-for-twint' ); ?></p>
                </div>

                <div class="t4a-field-row">
                    <label for="t4a_twint_settings_uuid"><?php esc_html_e( 'Shop UID', 'pay4all-for-twint' ); ?></label>
                    <input type="text" id="t4a_twint_settings_uuid" name="t4a_twint_settings_uuid" value="<?php echo esc_attr( $uuid ); ?>" required />
                    <p class="description"><?php esc_html_e( 'Available in the TWINT portal, on the "STORES" page via the edit button.', 'pay4all-for-twint' ); ?></p>
                </div>

                <div class="t4a-field-row">
                    <label for="t4a_twint_settings_registerid"><?php esc_html_e( 'Register ID', 'pay4all-for-twint' ); ?></label>
                    <input type="text" id="t4a_twint_settings_registerid" name="t4a_twint_settings_registerid" value="<?php echo esc_attr( $register ); ?>" maxlength="50" required />
                    <p class="description"><?php esc_html_e( 'Free-form identifier TWINT uses to distinguish this point of sale.', 'pay4all-for-twint' ); ?></p>
                </div>
            </section>

            <section class="t4a-wizard-card">
                <h2><?php esc_html_e( 'Certificate', 'pay4all-for-twint' ); ?></h2>
                <?php if ( $cert_ready ) : ?>
                    <p class="t4a-status t4a-status-ok"><?php esc_html_e( 'A valid certificate is already installed. You can replace it below if needed.', 'pay4all-for-twint' ); ?></p>
                <?php else : ?>
                    <p class="t4a-status t4a-status-warn"><?php esc_html_e( 'No certificate installed yet.', 'pay4all-for-twint' ); ?></p>
                <?php endif; ?>

                <div class="t4a-field-row">
                    <label class="t4a-field-title"><?php esc_html_e( 'Certificate file', 'pay4all-for-twint' ); ?></label>
                    <div class="t4a-upload-zone">
                        <input type="file" id="t4a_twint_cert_file" name="t4a_twint_cert_file" class="t4a-upload-input" accept=".p12,.pfx,.pem,.txt" <?php echo ! $cert_ready ? 'required' : ''; ?> />
                        <label for="t4a_twint_cert_file" class="t4a-upload-button">
                            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
                            <span><?php esc_html_e( 'Choose a file', 'pay4all-for-twint' ); ?></span>
                        </label>
                        <span class="t4a-upload-filename" data-empty="<?php echo esc_attr__( 'No file selected', 'pay4all-for-twint' ); ?>"><?php esc_html_e( 'No file selected', 'pay4all-for-twint' ); ?></span>
                    </div>
                    <p class="description"><?php esc_html_e( 'Use the certificate you downloaded from your TWINT account. The password below will be used to unlock it.', 'pay4all-for-twint' ); ?></p>
                </div>

                <div class="t4a-field-row">
                    <label for="t4a_twint_settings_certpw"><?php esc_html_e( 'Certificate password', 'pay4all-for-twint' ); ?></label>
                    <div style="position:relative;display:flex;align-items:center;max-width:400px;">
                        <input type="password" id="t4a_twint_settings_certpw" name="t4a_twint_settings_certpw" value="" autocomplete="new-password" <?php echo $has_cert_pw ? 'placeholder="' . esc_attr__( '••••••••', 'pay4all-for-twint' ) . '"' : 'required'; ?> style="flex:1;padding-right:38px;" />
                        <button type="button" id="t4a_certpw_toggle" aria-label="<?php echo esc_attr__( 'Show / hide password', 'pay4all-for-twint' ); ?>" style="position:absolute;right:0;top:0;bottom:0;width:36px;background:none;border:none;cursor:pointer;color:#646970;display:flex;align-items:center;justify-content:center;padding:0;">
                            <svg id="t4a_certpw_eye_show" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
                            <svg id="t4a_certpw_eye_hide" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="display:none;"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
                        </button>
                    </div>
                    <p class="description">
                        <?php esc_html_e( 'Password set when generating the certificate in the TWINT account.', 'pay4all-for-twint' ); ?>
                        <?php if ( $has_cert_pw ) : ?>
                            <br><em><?php esc_html_e( 'A password is already saved - leave this field empty to keep it, or enter a new one to replace it.', 'pay4all-for-twint' ); ?></em>
                        <?php endif; ?>
                    </p>
                </div>

                <?php if ( !T4A_OPENSSL_ACTIVE ) : ?>
                    <p class="t4a-status t4a-status-warn"><?php esc_html_e( 'OpenSSL is not enabled on the server; automatic conversion will not work. Import an already converted .pem file.', 'pay4all-for-twint' ); ?></p>
                <?php endif; ?>
            </section>

            <?php // JS wiring moved to assets/admin/js/wizard.js (wireCertificateUpload). ?>

            <section class="t4a-wizard-card">
                <h2><?php esc_html_e( 'Connected to TWINT', 'pay4all-for-twint' ); ?></h2>

                <?php if ( $test_result === 'success' ) : ?>
                    <p class="t4a-test-line">
                        <span class="t4a-test-icon t4a-test-icon-ok" aria-hidden="true">✓</span>
                        <?php echo esc_html( $test_message !== '' ? $test_message : __( 'TWINT connection is up and running.', 'pay4all-for-twint' ) ); ?>
                        <?php if ( $test_time ) : ?>
                            <span class="t4a-test-time">(<?php echo esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $test_time ) ); ?>)</span>
                        <?php endif; ?>
                    </p>
                <?php elseif ( $test_result === 'error' ) : ?>
                    <p class="t4a-test-line">
                        <span class="t4a-test-icon t4a-test-icon-ko" aria-hidden="true">✕</span>
                        <?php echo esc_html( $test_message !== '' ? $test_message : __( 'Connection test failed.', 'pay4all-for-twint' ) ); ?>
                        <?php if ( $test_time ) : ?>
                            <span class="t4a-test-time">(<?php echo esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $test_time ) ); ?>)</span>
                        <?php endif; ?>
                    </p>
                <?php else : ?>
                    <p class="t4a-test-line t4a-test-line-empty">
                        <?php esc_html_e( 'No test has been run yet. Click the button below to save your credentials and verify the TWINT connection.', 'pay4all-for-twint' ); ?>
                    </p>
                <?php endif; ?>

                <p>
                    <button type="submit" name="t4a_intent" value="test" class="button button-secondary">
                        <?php esc_html_e( 'Test TWINT connection', 'pay4all-for-twint' ); ?>
                    </button>
                </p>
            </section>

            <div class="t4a-actions">
                <a href="<?php echo esc_url( add_query_arg( [ 'page' => 'pay4all-settings', 'step' => 1 ], admin_url( 'admin.php' ) ) ); ?>" class="button button-secondary">
                    <?php esc_html_e( '← Previous', 'pay4all-for-twint' ); ?>
                </a>

                <button type="submit" form="t4a-step2-form" name="t4a_intent" value="continue" class="button button-secondary">
                    <?php esc_html_e( 'Save and continue →', 'pay4all-for-twint' ); ?>
                </button>
            </div>
        </form>
        <?php
    }

    /**
     * Allowed Pay4All Shop post-status slugs on the tab-3 picker.
     * Mirrors Pay4All\Form\Admin\Orders\Statuses::all() minus the terminal
     * "Cancelled" (only a manual action moves an order to cancelled).
     *
     * @return array<string,string>
     */
    public static function p4form_status_choices() : array
    {
        return [
            'p4all_new'        => __( 'New', 'pay4all-for-twint' ),
            'p4all_processing' => __( 'Processing', 'pay4all-for-twint' ),
            'p4all_pickup'     => __( 'Awaiting pickup', 'pay4all-for-twint' ),
            'p4all_shipped'    => __( 'Shipped', 'pay4all-for-twint' ),
            'p4all_completed'  => __( 'Completed', 'pay4all-for-twint' ),
        ];
    }

    /**
     * Step 3: combined Order-status tab.
     *
     * Renders a Pay4All Shop section (writes to `t4a_twint_p4form_status_*`)
     * when pay4all-form is present, and a WooCommerce section (writes to
     * `t4a_twint_settings_order_status_*`, shared with the pay4all-pro
     * WcSettings screen at /admin.php?page=pay4all-twint-woo) when WC is
     * present. The two host plugins never share option keys, so a single
     * POST submission carries both fieldsets and each is saved independently.
     */
    protected function render_step_order()
    {
        $has_p4form = self::has_pay4all_form();
        $has_wc     = self::has_woocommerce();
        $has_pro    = class_exists( '\\Pay4All\\Pro\\Plugin' );

        // Pay4All Shop reads
        $p4form_virtual  = get_option( 't4a_twint_p4form_status_virtual',  'p4all_completed' );
        $p4form_physical = get_option( 't4a_twint_p4form_status_physical', 'p4all_processing' );
        $p4form_choices  = self::p4form_status_choices();
        if ( ! isset( $p4form_choices[ $p4form_virtual ] ) )  { $p4form_virtual  = 'p4all_completed'; }
        if ( ! isset( $p4form_choices[ $p4form_physical ] ) ) { $p4form_physical = 'p4all_processing'; }

        // WooCommerce section : editable selects (same option keys as
        // pay4all-pro's WcSettings screen at /admin.php?page=pay4all-twint-woo,
        // both surfaces write to the same options). The "More settings"
        // button remains as a link to the pay4all-pro screen for the extras
        // (gateway toggle, per-language checkout texts) that the wizard
        // deliberately does not duplicate.
        $wc_settings_url = admin_url( 'admin.php?page=pay4all-twint-woo' );

        if ( $has_wc ) {
            $wc_paid_labels = [
                'on-hold'    => __( 'On hold', 'pay4all-for-twint' ),
                'processing' => __( 'Processing', 'pay4all-for-twint' ),
                'completed'  => __( 'Completed', 'pay4all-for-twint' ),
            ];
            $wc_failed_labels = [
                'pending' => __( 'Pending payment', 'pay4all-for-twint' ),
                'failed'  => __( 'Failed', 'pay4all-for-twint' ),
            ];
            $wc_cancelled_labels = [
                'pending'   => __( 'Pending payment', 'pay4all-for-twint' ),
                'cancelled' => __( 'Cancelled', 'pay4all-for-twint' ),
            ];
            $wc = [
                'status_non_virtual' => get_option( 't4a_twint_settings_order_status_non_virtual', 'processing' ),
                'status_virtual'     => get_option( 't4a_twint_settings_order_status_virtual',     'completed' ),
                'status_deferred'    => get_option( 't4a_twint_settings_deferred_order_status',    'on-hold' ),
                'status_failed'      => get_option( 't4a_twint_settings_order_status_failed',      'pending' ),
                'status_cancelled'   => get_option( 't4a_twint_settings_cancelled_order_status',   'cancelled' ),
            ];
        }
        ?>
        <form method="post" action="" class="t4a-wizard-form">
            <?php wp_nonce_field( 't4a_wizard_step3', 't4a_nonce' ); ?>
            <input type="hidden" name="t4a_action" value="step3_save" />

            <?php if ( $has_p4form ) : ?>
            <section class="t4a-wizard-card">
                <h2><?php esc_html_e( 'Pay4All Shop order status', 'pay4all-for-twint' ); ?></h2>
                <p class="t4a-card-intro">
                    <?php esc_html_e( 'Choose the status the Pay4All Shop order should take as soon as the TWINT payment is confirmed.', 'pay4all-for-twint' ); ?>
                </p>

                <div class="t4a-field-row">
                    <label for="t4a_p4form_status_physical"><?php esc_html_e( 'Order containing at least one physical product', 'pay4all-for-twint' ); ?></label>
                    <select id="t4a_p4form_status_physical" name="t4a_twint_p4form_status_physical">
                        <?php foreach ( $p4form_choices as $val => $lbl ) : ?>
                            <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $p4form_physical, $val ); ?>><?php echo esc_html( $lbl ); ?></option>
                        <?php endforeach; ?>
                    </select>
                    <p class="description"><?php esc_html_e( 'As soon as a single physical product is present, the order must be prepared and shipped before being marked completed.', 'pay4all-for-twint' ); ?></p>
                </div>

                <div class="t4a-field-row">
                    <label for="t4a_p4form_status_virtual"><?php esc_html_e( 'Order containing only virtual products or tickets', 'pay4all-for-twint' ); ?></label>
                    <select id="t4a_p4form_status_virtual" name="t4a_twint_p4form_status_virtual">
                        <?php foreach ( $p4form_choices as $val => $lbl ) : ?>
                            <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $p4form_virtual, $val ); ?>><?php echo esc_html( $lbl ); ?></option>
                        <?php endforeach; ?>
                    </select>
                    <p class="description"><?php esc_html_e( 'Applied only when every product in the order is virtual or a ticket. No logistical preparation is required, the order can be completed automatically.', 'pay4all-for-twint' ); ?></p>
                </div>
            </section>
            <?php endif; ?>

            <?php if ( $has_wc ) : ?>
            <section class="t4a-wizard-card">
                <h2><?php esc_html_e( 'WooCommerce order status', 'pay4all-for-twint' ); ?></h2>
                <p class="t4a-card-intro">
                    <?php esc_html_e( 'Sets which status the WooCommerce order moves to as soon as the TWINT payment is confirmed, based on product type and transaction outcome.', 'pay4all-for-twint' ); ?>
                </p>

                <?php if ( ! $has_pro ) : ?>
                    <p class="t4a-status t4a-status-warn">
                        <?php
                        printf(
                            /* translators: %s: Pay4All Pro brand name */
                            esc_html__( 'These statuses are applied to WooCommerce orders only when the paid %s add-on is active.', 'pay4all-for-twint' ),
                            '<strong>Pay4All Pro</strong>'
                        );
                        ?>
                    </p>
                <?php endif; ?>

                <div class="t4a-field-row">
                    <label for="t4a_status_non_virtual"><?php esc_html_e( 'Order containing at least one physical product', 'pay4all-for-twint' ); ?></label>
                    <select id="t4a_status_non_virtual" name="t4a_twint_settings_order_status_non_virtual">
                        <?php foreach ( $wc_paid_labels as $val => $lbl ) : ?>
                            <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $wc['status_non_virtual'], $val ); ?>><?php echo esc_html( $lbl ); ?></option>
                        <?php endforeach; ?>
                    </select>
                    <p class="description"><?php esc_html_e( 'Recommended: "Processing" (the product needs to be shipped).', 'pay4all-for-twint' ); ?></p>
                </div>

                <div class="t4a-field-row">
                    <label for="t4a_status_virtual"><?php esc_html_e( 'Order containing only virtual / downloadable products', 'pay4all-for-twint' ); ?></label>
                    <select id="t4a_status_virtual" name="t4a_twint_settings_order_status_virtual">
                        <?php foreach ( $wc_paid_labels as $val => $lbl ) : ?>
                            <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $wc['status_virtual'], $val ); ?>><?php echo esc_html( $lbl ); ?></option>
                        <?php endforeach; ?>
                    </select>
                    <p class="description"><?php esc_html_e( 'Recommended: "Completed" (nothing to ship, the customer accesses the product immediately).', 'pay4all-for-twint' ); ?></p>
                </div>

                <div class="t4a-field-row">
                    <label for="t4a_status_deferred"><?php esc_html_e( 'Deferred payment - awaiting confirmation', 'pay4all-for-twint' ); ?></label>
                    <select id="t4a_status_deferred" name="t4a_twint_settings_deferred_order_status">
                        <?php foreach ( $wc_paid_labels as $val => $lbl ) : ?>
                            <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $wc['status_deferred'], $val ); ?>><?php echo esc_html( $lbl ); ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>

                <div class="t4a-field-row">
                    <label for="t4a_status_failed"><?php esc_html_e( 'Payment failed', 'pay4all-for-twint' ); ?></label>
                    <select id="t4a_status_failed" name="t4a_twint_settings_order_status_failed">
                        <?php foreach ( $wc_failed_labels as $val => $lbl ) : ?>
                            <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $wc['status_failed'], $val ); ?>><?php echo esc_html( $lbl ); ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>

                <div class="t4a-field-row">
                    <label for="t4a_status_cancelled"><?php esc_html_e( 'Payment cancelled', 'pay4all-for-twint' ); ?></label>
                    <select id="t4a_status_cancelled" name="t4a_twint_settings_cancelled_order_status">
                        <?php foreach ( $wc_cancelled_labels as $val => $lbl ) : ?>
                            <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $wc['status_cancelled'], $val ); ?>><?php echo esc_html( $lbl ); ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>

                <p style="margin-top:16px;">
                    <a href="<?php echo esc_url( $wc_settings_url ); ?>" class="button button-secondary">
                        <?php esc_html_e( 'More settings', 'pay4all-for-twint' ); ?>
                    </a>
                </p>
            </section>
            <?php endif; ?>

            <div class="t4a-actions">
                <a href="<?php echo esc_url( add_query_arg( [ 'page' => 'pay4all-settings', 'step' => 2 ], admin_url( 'admin.php' ) ) ); ?>" class="button button-secondary">
                    <?php esc_html_e( '← Previous', 'pay4all-for-twint' ); ?>
                </a>
                <button type="submit" name="t4a_intent" value="continue" class="button button-secondary">
                    <?php esc_html_e( 'Save and continue →', 'pay4all-for-twint' ); ?>
                </button>
            </div>
        </form>
        <?php
    }

    /**
     * Returns the Pay4All Shop post-status slug to apply on an order that
     * was just paid with TWINT — reads the merchant's tab-3 preferences
     * (`t4a_twint_p4form_status_virtual` for orders where every item is
     * virtual or a ticket, `t4a_twint_p4form_status_physical` for orders
     * with any physical item).
     */
    public static function paid_status_for_p4all_form_order( int $order_id ) : string
    {
        $virtual  = get_option( 't4a_twint_p4form_status_virtual',  'p4all_completed' );
        $physical = get_option( 't4a_twint_p4form_status_physical', 'p4all_processing' );
        $choices  = self::p4form_status_choices();
        // Guard against stale/invalid stored values so we always route to a
        // registered post status. Anything unknown falls back to the safe
        // "needs work" status (p4all_processing).
        if ( ! isset( $choices[ $virtual ] ) )  { $virtual  = 'p4all_completed'; }
        if ( ! isset( $choices[ $physical ] ) ) { $physical = 'p4all_processing'; }
        return self::p4all_form_order_is_virtual( $order_id ) ? $virtual : $physical;
    }

    /**
     * True when every item on the Pay4All Shop order is a virtual file OR a
     * ticket. A single physical item flips this back to false.
     *
     * An order with no items (edge case) is treated as non-virtual so the
     * merchant sees the physical-status path (safer default - nothing gets
     * auto-terminated by accident).
     */
    private static function p4all_form_order_is_virtual( int $order_id ) : bool
    {
        if ( ! class_exists( '\\Pay4All\\Form\\Domain\\Order' ) )   { return false; }
        if ( ! class_exists( '\\Pay4All\\Form\\Domain\\Product' ) ) { return false; }
        $items = \Pay4All\Form\Domain\Order::items( $order_id );
        if ( empty( $items ) || ! is_array( $items ) ) { return false; }
        foreach ( $items as $item ) {
            $pid = (int) ( $item['product_id'] ?? 0 );
            if ( $pid <= 0 ) { return false; }
            $type = \Pay4All\Form\Domain\Product::type( $pid );
            if ( ! in_array( $type, [ \Pay4All\Form\Domain\Product::TYPE_VIRTUAL, \Pay4All\Form\Domain\Product::TYPE_TICKET ], true ) ) {
                return false;
            }
        }
        return true;
    }

    /**
     * Step 4: read-only status snapshot for the free plugin - only reports on
     * the TWINT credentials + certificate + Pay4All Shop presence. Anything
     * WooCommerce-specific lives in the paid pay4all-pro companion.
     */
    protected function render_step_status()
    {
        $uuid         = get_option( 't4a_twint_settings_uuid', '' );
        $register     = get_option( 't4a_twint_settings_registerid', '' );
        $cert_path    = T4A_CERTIFICATE_PATH;
        $cert_ready   = file_exists( $cert_path );
        $cert_size    = $cert_ready ? size_format( filesize( $cert_path ) ) : null;
        $enrolled     = get_option( 't4a_twint_enrolled' ) === 'yes';
        $version      = defined( 'T4A_PLUGIN_VERSION' ) ? T4A_PLUGIN_VERSION : '';
        $openssl      = T4A_OPENSSL_ACTIVE;
        $soap         = extension_loaded( 'soap' );
        $current_lang = self::current_supported_lang();

        // Quick certificate expiry probe.
        $cert_expiry = null;
        if ( $cert_ready && $openssl ) {
            $contents = @file_get_contents( $cert_path );
            if ( $contents !== false ) {
                $parsed = @openssl_x509_parse( $contents );
                if ( is_array( $parsed ) && !empty( $parsed[ 'validTo_time_t' ] ) ) {
                    $cert_expiry = (int) $parsed[ 'validTo_time_t' ];
                }
            }
        }

        $pill = function ( $ok, $yes_label, $no_label ) {
            $class = $ok ? 't4a-pill-ok' : 't4a-pill-error';
            $label = $ok ? $yes_label : $no_label;
            return '<span class="t4a-pill ' . esc_attr( $class ) . '">' . $label . '</span>';
        };
        ?>
        <section class="t4a-wizard-card">
            <h2><?php esc_html_e( 'Plugin status', 'pay4all-for-twint' ); ?></h2>
            <dl class="t4a-summary t4a-status-grid">
                <dt><?php esc_html_e( 'Plugin version', 'pay4all-for-twint' ); ?></dt>
                <dd><code><?php echo esc_html( $version ); ?></code></dd>

                <dt><?php esc_html_e( 'Detected language', 'pay4all-for-twint' ); ?></dt>
                <dd><code><?php echo esc_html( $current_lang ); ?></code> <span style="color:#646970;font-size:12px;">(<?php echo esc_html( get_locale() ); ?>)</span></dd>
            </dl>
        </section>

        <section class="t4a-wizard-card">
            <h2><?php esc_html_e( 'TWINT connection', 'pay4all-for-twint' ); ?></h2>
            <dl class="t4a-summary t4a-status-grid">
                <dt><?php esc_html_e( 'Shop UID', 'pay4all-for-twint' ); ?></dt>
                <dd><?php echo $uuid ? '<code>' . esc_html( $uuid ) . '</code>' : '<em>' . esc_html__( 'missing', 'pay4all-for-twint' ) . '</em>'; ?></dd>

                <dt><?php esc_html_e( 'Register ID', 'pay4all-for-twint' ); ?></dt>
                <dd><code><?php echo esc_html( $register ?: '-' ); ?></code></dd>

                <dt><?php esc_html_e( 'Certificate installed', 'pay4all-for-twint' ); ?></dt>
                <dd>
                    <?php echo wp_kses_post( $pill( $cert_ready, esc_html__( 'Yes', 'pay4all-for-twint' ), esc_html__( 'Missing', 'pay4all-for-twint' ) ) ); ?>
                    <?php if ( $cert_size ) : ?><span style="color:#646970;font-size:12px;margin-left:8px;"><?php echo esc_html( $cert_size ); ?></span><?php endif; ?>
                </dd>

                <?php if ( $cert_expiry ) :
                    $days_left = (int) floor( ( $cert_expiry - time() ) / DAY_IN_SECONDS );
                    $expired   = $days_left <= 0;
                    $soon      = !$expired && $days_left < 30;
                ?>
                <dt><?php esc_html_e( 'Certificate expiration', 'pay4all-for-twint' ); ?></dt>
                <dd>
                    <?php echo esc_html( date_i18n( get_option( 'date_format' ), $cert_expiry ) ); ?>
                    <?php if ( $expired ) : ?>
                        <span class="t4a-pill t4a-pill-error" style="margin-left:8px;"><?php esc_html_e( 'Expired', 'pay4all-for-twint' ); ?></span>
                    <?php elseif ( $soon ) : ?>
                        <span class="t4a-pill" style="background:#fcf9e8;color:#6b5b00;margin-left:8px;"><?php
                            /* translators: %d: number of days remaining before certificate expiration. */
                            printf( esc_html__( 'in %d days', 'pay4all-for-twint' ), (int) $days_left );
                        ?></span>
                    <?php else : ?>
                        <span style="color:#646970;font-size:12px;margin-left:8px;"><?php
                            /* translators: %d: number of days remaining before certificate expiration. */
                            printf( esc_html__( 'in %d days', 'pay4all-for-twint' ), (int) $days_left );
                        ?></span>
                    <?php endif; ?>
                </dd>
                <?php endif; ?>

                <dt><?php esc_html_e( 'Register connected to TWINT', 'pay4all-for-twint' ); ?></dt>
                <dd><?php echo wp_kses_post( $pill( $enrolled, esc_html__( 'Yes', 'pay4all-for-twint' ), esc_html__( 'No', 'pay4all-for-twint' ) ) ); ?></dd>
            </dl>
        </section>

        <?php
        // -- Compatible plugins -----------------------------------------
        // Pay4All Form ships as pay4all-form/pay4all-form.php (WP.org slug
        // convention) and exposes the P4ALL_FORM_VERSION constant on boot -
        // the constant check is our most reliable signal because it's set
        // before any of our hooks run, regardless of how the merchant
        // installed the folder.
        //
        // This block only runs on the wizard admin page, which is loaded after
        // `plugins_loaded` has completed - pay4all-form has therefore either
        // defined its constant or registered its main class. Testing both is
        // enough : no need to load `wp-admin/includes/plugin.php` for a fall-
        // back `is_plugin_active()` call that could only fire if the file were
        // present but the plugin dormant, which cannot happen at this stage.
        $form_active = defined( 'P4ALL_FORM_VERSION' )
            || class_exists( '\\Pay4All\\Form\\Plugin' );

        $wc_active  = class_exists( 'WooCommerce' );
        $pro_active = class_exists( '\\Pay4All\\Pro\\Plugin' );

        $form_install_url = self_admin_url( 'plugin-install.php?s=pay4all%2520shop&tab=search&type=term' );
        $wc_install_url   = self_admin_url( 'plugin-install.php?s=woocommerce&tab=search&type=term' );
        ?>
        <section class="t4a-wizard-card">
            <h2><?php esc_html_e( 'Compatible plugins', 'pay4all-for-twint' ); ?></h2>
            <dl class="t4a-summary t4a-status-grid">

                <dt>Pay4All Shop</dt>
                <dd>
                    <?php echo wp_kses_post( $pill( $form_active, esc_html__( 'Installed', 'pay4all-for-twint' ), esc_html__( 'Missing', 'pay4all-for-twint' ) ) ); ?>
                    <?php if ( ! $form_active ) : ?>
                        <a href="<?php echo esc_url( $form_install_url ); ?>" class="button" style="margin-left:8px;">
                            <?php esc_html_e( 'Install Pay4All Shop', 'pay4all-for-twint' ); ?>
                        </a>
                    <?php endif; ?>
                    <p class="description" style="margin:4px 0 0;"><?php esc_html_e( 'The simplest way to build an order form with built-in TWINT payment.', 'pay4all-for-twint' ); ?></p>
                </dd>

                <dt>WooCommerce</dt>
                <dd>
                    <?php echo wp_kses_post( $pill( $wc_active, esc_html__( 'Installed', 'pay4all-for-twint' ), esc_html__( 'Missing', 'pay4all-for-twint' ) ) ); ?>
                    <?php if ( ! $wc_active ) : ?>
                        <a href="<?php echo esc_url( $wc_install_url ); ?>" class="button" style="margin-left:8px;">
                            <?php esc_html_e( 'Install WooCommerce', 'pay4all-for-twint' ); ?>
                        </a>
                    <?php endif; ?>
                    <?php if ( $wc_active && ! $pro_active ) : ?>
                        <p class="description" style="margin:4px 0 0;color:#a85a00;">
                            <span class="dashicons dashicons-info" style="font-size:16px;width:16px;height:16px;vertical-align:middle;"></span>
                            <?php
                            printf(
                                /* translators: %s: link to pay4all.ch */
                                wp_kses( __( 'The paid %s add-on is required to enable the TWINT gateway at WooCommerce checkout.', 'pay4all-for-twint' ), [ 'a' => [ 'href' => [], 'target' => [], 'rel' => [] ] ] ),
                                '<a href="https://pay4all.ch/" target="_blank" rel="noopener noreferrer"><strong>Pay4All Pro</strong></a>'
                            );
                            ?>
                        </p>
                        <p style="margin:8px 0 0;">
                            <a class="button button-primary" href="https://pay4all.ch/downloads/pay4all-pro" target="_blank" rel="noopener noreferrer" style="display:inline-flex;align-items:center;gap:6px;">
                                <span class="dashicons dashicons-download" style="font-size:18px;width:18px;height:18px;line-height:1;color:#fff;"></span>
                                <?php esc_html_e( 'Download Pay4all-Pro.zip', 'pay4all-for-twint' ); ?>
                            </a>
                        </p>
                    <?php else : ?>
                        <p class="description" style="margin:4px 0 0;">
                            <?php esc_html_e( 'The WooCommerce TWINT gateway is provided by the paid Pay4All Pro add-on.', 'pay4all-for-twint' ); ?>
                        </p>
                    <?php endif; ?>
                </dd>

            </dl>
        </section>

        <section class="t4a-wizard-card">
            <h2><?php esc_html_e( 'Server environment', 'pay4all-for-twint' ); ?></h2>
            <dl class="t4a-summary t4a-status-grid">
                <dt><?php esc_html_e( 'SOAP extension', 'pay4all-for-twint' ); ?></dt>
                <dd><?php echo wp_kses_post( $pill( $soap, esc_html__( 'Enabled', 'pay4all-for-twint' ), esc_html__( 'Missing', 'pay4all-for-twint' ) ) ); ?></dd>

                <dt><?php esc_html_e( 'OpenSSL extension', 'pay4all-for-twint' ); ?></dt>
                <dd><?php echo wp_kses_post( $pill( $openssl, esc_html__( 'Enabled', 'pay4all-for-twint' ), esc_html__( 'Missing', 'pay4all-for-twint' ) ) ); ?></dd>

                <dt><?php esc_html_e( 'PHP version', 'pay4all-for-twint' ); ?></dt>
                <dd><code><?php echo esc_html( PHP_VERSION ); ?></code></dd>
            </dl>
        </section>

        <?php
    }

    /**
     * Process POST submissions from any wizard step. Returns an optional flash
     * notice and triggers a redirect when appropriate.
     *
     * @return array{type:string,message:string}|null
     */
    protected function handle_wizard_post()
    {
        if ( empty( $_POST[ 't4a_action' ] ) ) {
            return null;
        }
        $action = sanitize_key( wp_unslash( $_POST[ 't4a_action' ] ) );

        switch ( $action ) {

            case 'step2_test':
                if ( !isset( $_POST[ 't4a_nonce' ] ) || !wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[ 't4a_nonce' ] ) ), 't4a_wizard_step2' ) ) {
                    return [ 'type' => 'error', 'message' => __( 'Invalid security token, please try again.', 'pay4all-for-twint' ) ];
                }

                // 1. Save the credential fields exactly like the previous step2_save did.
                if ( isset( $_POST[ 't4a_twint_settings_uuid' ] ) ) {
                    update_option( 't4a_twint_settings_uuid', trim( sanitize_text_field( wp_unslash( $_POST[ 't4a_twint_settings_uuid' ] ) ) ) );
                }
                if ( isset( $_POST[ 't4a_twint_settings_certpw' ] ) ) {
                    // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- certificate password ; sanitize_text_field() would corrupt special characters.
                    $pw = (string) wp_unslash( $_POST[ 't4a_twint_settings_certpw' ] );
                    // Empty submission = "keep current" (the form now
                    // renders the field blank on load so the merchant is
                    // never staring at their own password). Only persist
                    // when a new value was actually typed.
                    if ( $pw !== '' ) {
                        \Twnt4All\Engine\EnvironmentConfig::save_certificate_password( $pw );
                    }
                }
                if ( isset( $_POST[ 't4a_twint_settings_registerid' ] ) ) {
                    update_option( 't4a_twint_settings_registerid', substr( sanitize_text_field( wp_unslash( $_POST[ 't4a_twint_settings_registerid' ] ) ), 0, 50 ) );
                }
                if ( isset( $_POST[ 't4a_twint_settings_email_on_enroll_failure' ] ) ) {
                    update_option( 't4a_twint_settings_email_on_enroll_failure', sanitize_email( wp_unslash( $_POST[ 't4a_twint_settings_email_on_enroll_failure' ] ) ) );
                }

                // 2. Convert/save the certificate if one was uploaded. If the upload
                //    failed, mark the test as failed and stop before contacting TWINT.
                $upload_message = $this->process_certificate_upload();
                if ( $upload_message && $upload_message[ 'type' ] === 'error' ) {
                    update_option( 't4a_twint_last_test_result', 'error' );
                    update_option( 't4a_twint_last_test_message', $upload_message[ 'message' ] );
                    update_option( 't4a_twint_last_test_time', time() );
                    set_transient( 't4a_wizard_flash', $upload_message, 30 );
                    wp_safe_redirect( add_query_arg( [ 'page' => 'pay4all-settings', 'step' => 2 ], admin_url( 'admin.php' ) ) );
                    exit;
                }

                // 3. Test the connection. First call enrollCashRegister if the
                //    cashier has never been enrolled, otherwise just verify the
                //    state via checkSystemStatus (read-only).
                $already_enrolled = get_option( 't4a_twint_enrolled' ) === 'yes';

                // Use the just-updated UID from the option (not the constant,
                // which was frozen at plugin boot with the previous value).
                $fresh_uuid = get_option( 't4a_twint_settings_uuid', '' );

                if ( $already_enrolled ) {
                    $twint = \Twnt4All\Engine\TwintCore::create_for_admin( null, $fresh_uuid );
                    if ( !$twint ) {
                        $result = [ 'status' => false, 'message' => __( 'Unable to initialize the TWINT client. Check the certificate and its password.', 'pay4all-for-twint' ) ];
                    } else {
                        $result = $twint->transactionHandler->test_connection();
                    }
                } else {
                    $result = AdminAjax::enroll_register();
                    if ( !empty( $result[ 'status' ] ) ) {
                        update_option( 't4a_twint_enrolled', 'yes' );
                    }
                }

                // 4. Record the outcome so step 2 can show it and unlock "Continue".
                // Store the untranslated source string for known defaults so the
                // display layer can translate it into the current admin locale.
                $message_raw = !empty( $result[ 'message' ] )
                    ? $result[ 'message' ]
                    : ( $result[ 'status' ] ? 'TWINT connection is up and running.' : 'Connection test failed.' );
                update_option( 't4a_twint_last_test_result', $result[ 'status' ] ? 'success' : 'error' );
                update_option( 't4a_twint_last_test_message', $message_raw );
                update_option( 't4a_twint_last_test_time', time() );

                // Translate the two well-known default messages explicitly; any
                // upstream error string is displayed verbatim (already localised
                // by the SOAP layer or comes back as a technical detail). The
                // legacy FR literals are still matched so upgraded installs with
                // a persisted FR value keep rendering translated text.
                if ( $message_raw === 'TWINT connection is up and running.'
                    || $message_raw === 'La connexion à TWINT est opérationnelle.' ) {
                    $message = __( 'TWINT connection is up and running.', 'pay4all-for-twint' );
                } elseif ( $message_raw === 'Connection test failed.'
                    || $message_raw === 'Le test de connexion a échoué.' ) {
                    $message = __( 'Connection test failed.', 'pay4all-for-twint' );
                } else {
                    $message = $message_raw;
                }
                if ( $upload_message && $upload_message[ 'type' ] === 'success' && $result[ 'status' ] ) {
                    // Concatenate both messages so the user knows the cert was processed.
                    $message = $upload_message[ 'message' ] . ' ' . $message;
                }
                set_transient(
                    't4a_wizard_flash',
                    [ 'type' => $result[ 'status' ] ? 'success' : 'error', 'message' => $message ],
                    30
                );

                // Two submit buttons share this handler:
                //   t4a_intent=test     -> always stay on step 2 (explicit re-test)
                //   t4a_intent=continue -> jump to the settings step (3) once
                //                          the silent test passed.
                $intent = isset( $_POST[ 't4a_intent' ] ) ? sanitize_key( wp_unslash( $_POST[ 't4a_intent' ] ) ) : 'test';
                if ( $intent === 'continue' && $result[ 'status' ] ) {
                    // Jump to the Order tab when a host plugin is present,
                    // else land directly on the final status snapshot.
                    $next = ( self::has_pay4all_form() || self::has_woocommerce() ) ? 3 : 4;
                    wp_safe_redirect( add_query_arg( [ 'page' => 'pay4all-settings', 'step' => $next ], admin_url( 'admin.php' ) ) );
                } else {
                    wp_safe_redirect( add_query_arg( [ 'page' => 'pay4all-settings', 'step' => 2 ], admin_url( 'admin.php' ) ) );
                }
                exit;

            case 'step3_save':
                if ( ! isset( $_POST[ 't4a_nonce' ] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[ 't4a_nonce' ] ) ), 't4a_wizard_step3' ) ) {
                    return [ 'type' => 'error', 'message' => __( 'Invalid security token, please try again.', 'pay4all-for-twint' ) ];
                }

                // Pay4All Shop status selectors (only present in the POST body
                // when the pay4all-form section rendered).
                $allowed_p4form = array_keys( self::p4form_status_choices() );
                if ( isset( $_POST[ 't4a_twint_p4form_status_virtual' ] ) ) {
                    $val = sanitize_key( wp_unslash( $_POST[ 't4a_twint_p4form_status_virtual' ] ) );
                    if ( in_array( $val, $allowed_p4form, true ) ) {
                        update_option( 't4a_twint_p4form_status_virtual', $val );
                    }
                }
                if ( isset( $_POST[ 't4a_twint_p4form_status_physical' ] ) ) {
                    $val = sanitize_key( wp_unslash( $_POST[ 't4a_twint_p4form_status_physical' ] ) );
                    if ( in_array( $val, $allowed_p4form, true ) ) {
                        update_option( 't4a_twint_p4form_status_physical', $val );
                    }
                }

                // WooCommerce status selectors (only present in the POST body
                // when the WC section rendered - meaning WC is active).
                if ( self::has_woocommerce() ) {
                    $allowed_paid      = [ 'on-hold', 'processing', 'completed' ];
                    $allowed_failed    = [ 'pending', 'failed' ];
                    $allowed_cancelled = [ 'pending', 'cancelled' ];

                    $save_select = static function ( string $key, array $allowed, string $fallback ) : void {
                        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce checked at case entry.
                        if ( ! isset( $_POST[ $key ] ) ) { return; }
                        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- same reason.
                        $raw = sanitize_key( wp_unslash( (string) $_POST[ $key ] ) );
                        update_option( $key, in_array( $raw, $allowed, true ) ? $raw : $fallback );
                    };

                    $save_select( 't4a_twint_settings_order_status_non_virtual', $allowed_paid,      'processing' );
                    $save_select( 't4a_twint_settings_order_status_virtual',     $allowed_paid,      'completed' );
                    $save_select( 't4a_twint_settings_deferred_order_status',    $allowed_paid,      'on-hold' );
                    $save_select( 't4a_twint_settings_order_status_failed',      $allowed_failed,    'pending' );
                    $save_select( 't4a_twint_settings_cancelled_order_status',   $allowed_cancelled, 'cancelled' );
                }

                set_transient( 't4a_wizard_flash', [
                    'type'    => 'success',
                    'message' => __( 'Order statuses saved.', 'pay4all-for-twint' ),
                ], 30 );

                $intent = isset( $_POST[ 't4a_intent' ] ) ? sanitize_key( wp_unslash( $_POST[ 't4a_intent' ] ) ) : 'continue';
                $next_step = ( $intent === 'continue' ) ? 4 : 3;
                wp_safe_redirect( add_query_arg( [ 'page' => 'pay4all-settings', 'step' => $next_step ], admin_url( 'admin.php' ) ) );
                exit;

        }
        return null;
    }

    /**
     * Reads a flash notice stored in a transient after a redirect.
     */
    protected function consume_flash()
    {
        $flash = get_transient( 't4a_wizard_flash' );
        if ( $flash ) {
            delete_transient( 't4a_wizard_flash' );
            return $flash;
        }
        return null;
    }

    /**
     * Handles the certificate upload coming from step 1.
     */
    protected function process_certificate_upload()
    {
        // phpcs:disable WordPress.Security.NonceVerification.Missing -- Called from handle_wizard_post() after wp_verify_nonce() on the wizard step nonce.
        if ( empty( $_FILES[ 't4a_twint_cert_file' ][ 'name' ] ) ) {
            return null;
        }
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- wp_handle_upload() validates the upload below ; individual $file fields are sanitized on read (pathinfo() on the name, is_uploaded_file() on tmp_name).
        $file = $_FILES[ 't4a_twint_cert_file' ];
        if ( !empty( $file[ 'error' ] ) ) {
            return [ 'type' => 'error', 'message' => __( 'Certificate upload failed.', 'pay4all-for-twint' ) ];
        }

        // CertificateService detects the file type from the path's extension.
        // PHP upload tmp paths have no extension (e.g. /tmp/phpXXXXXX), so we
        // copy the upload to a sibling path that carries the original extension.
        $original_name = (string) $file[ 'name' ];
        $extension     = strtolower( pathinfo( $original_name, PATHINFO_EXTENSION ) );

        $allowed = [ 'p12', 'pfx', 'pem', 'txt' ];
        if ( !in_array( $extension, $allowed, true ) ) {
            return [ 'type' => 'error', 'message' => sprintf(
                /* translators: %s: uploaded file extension. */
                __( 'Unsupported certificate format ("%s"). Use a .p12, .pfx, .pem or .txt file.', 'pay4all-for-twint' ),
                $extension !== '' ? $extension : __( 'unknown', 'pay4all-for-twint' )
            ) ];
        }

        // .pem files are already in PEM format: route them through the txt branch
        // (which saves them as-is) instead of trying to read them as pkcs12.
        $effective_extension = ( $extension === 'pem' ) ? 'txt' : $extension;

        $tmp_with_ext = $file[ 'tmp_name' ] . '.' . $effective_extension;
        if ( !@copy( $file[ 'tmp_name' ], $tmp_with_ext ) ) {
            return [ 'type' => 'error', 'message' => __( 'Unable to access the uploaded file.', 'pay4all-for-twint' ) ];
        }

        $password = \Twnt4All\Engine\EnvironmentConfig::get_certificate_password();
        $result   = \Twnt4All\Engine\Service\CertificateService::convert_certificate_file( $tmp_with_ext, $password );

        @wp_delete_file( $tmp_with_ext );
        @wp_delete_file( $file[ 'tmp_name' ] );

        if ( empty( $result[ 'status' ] ) ) {
            $error_code = $result[ 'error' ] ?? 'unknown';
            $hint = '';
            switch ( $error_code ) {
                case \Twnt4All\Engine\Service\CertificateService::ERROR_OPENSSL:
                    $hint = __( 'OpenSSL is not enabled on the server.', 'pay4all-for-twint' );
                    break;
                case \Twnt4All\Engine\Service\CertificateService::ERROR_PARSE:
                    $hint = __( 'The password does not match the certificate, or the file is corrupted.', 'pay4all-for-twint' );
                    break;
                case \Twnt4All\Engine\Service\CertificateService::ERROR_FILE_READ_FAILED:
                    $hint = __( 'The server could not read the uploaded file.', 'pay4all-for-twint' );
                    break;
            }
            /* translators: %s: internal certificate error code. */
            $msg = sprintf( __( 'Certificate conversion failed (%s).', 'pay4all-for-twint' ), $error_code );
            if ( $hint ) {
                $msg .= ' ' . $hint;
            }
            return [ 'type' => 'error', 'message' => $msg ];
        }

        \Twnt4All\Engine\EnvironmentConfig::save_certificate_file( $result[ 'file' ] );
        return [ 'type' => 'success', 'message' => __( 'Certificate converted and installed successfully.', 'pay4all-for-twint' ) ];
        // phpcs:enable WordPress.Security.NonceVerification.Missing
    }

    /**
     * Registers the wizard-scoped CSS + JS handles and pushes them into the
     * standard WordPress admin enqueue pipeline. Called at the top of
     * render_admin_page() so both files travel through wp_enqueue_style() /
     * wp_enqueue_script() instead of being embedded as inline tags.
     */
    protected function enqueue_wizard_assets()
    {
        $base = plugin_dir_url( dirname( __DIR__ ) );
        wp_enqueue_style(
            'pay4all-for-twint-wizard',
            $base . 'assets/admin/css/wizard.css',
            array(),
            T4A_PLUGIN_VERSION
        );
        wp_enqueue_script(
            'pay4all-for-twint-wizard',
            $base . 'assets/admin/js/wizard.js',
            array(),
            T4A_PLUGIN_VERSION,
            true
        );
    }

    /**
     * Legacy placeholder kept for backwards compat with older call sites.
     * The wizard CSS moved to assets/admin/css/wizard.css and is enqueued via
     * enqueue_wizard_assets().
     */
    protected function print_wizard_styles()
    {
        // No-op ; the compliant CSS is enqueued in enqueue_wizard_assets().
    }

    /**
     * Returns the Pay4All TWINT tutorials URL, localized to FR / DE / IT / EN
     * based on the current WordPress admin locale (French fallback).
     */
    public static function get_documentation_url()
    {
        $lang = strtolower( substr( (string) determine_locale(), 0, 2 ) );
        if ( ! in_array( $lang, [ 'fr', 'de', 'it', 'en' ], true ) ) {
            $lang = 'fr';
        }
        return 'https://pay4all.ch/p/twint-woocommerce?lang=' . $lang . '#tutorials';
    }
}
