<?php

// phpcs:disable WordPress.WP.I18n.TextDomainMismatch -- Bridge to pay4all-for-twint :
// this WooCommerce integration re-emits the TWINT gateway strings the free
// plugin already ships and translates. Reusing its text domain avoids
// duplicating dozens of msgids × 4 locales in pay4all-pro's .po files.

namespace Twnt4All\Checkout;

use Twnt4All\Checkout\OrderDataRepository;
use Twnt4All\Exception\UnsupportedCurrencyException;
use Twnt4All\Engine\Support\EngineHelper;
use Twnt4All\Support\Journal;
use Twnt4All\Support\WooBridge;
use Twnt4All\Wallet\WalletPaymentHandler;
use Twnt4All\Engine\Adapter\DirectMailer;
use Twnt4All\Engine\TwintCore;
use Twnt4All\Engine\Adapter\ConfigProvider;
use Twnt4All\Engine\Adapter\DirectLogger;

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

/**
 * The TWINT payment gateway
 */
class TwintGateway extends \WC_Payment_Gateway
{
    private   $integration;
    protected $instructions;
    protected $default_enabled;
    protected $default_title;
    protected $default_description;

    private $allow_saved_pm;

    /**
     * TwintGateway constructor.
     */
    public function __construct()
    {
        // Standard gateway fields.
        $this->id                 = 't4a_twint';
        $this->method_title       = 'TWINT';
        $this->method_description = __( 'Payment gateway for payments with TWINT.', 'pay4all-for-twint' );
        $this->has_fields         = false;
        $this->supports           = array(
            'products',
            'refunds',
            'woocommerce_blocks_checkout',
        );

        $allow_saved_pm = get_option( 't4a_twint_settings_save_pm', false );
        $this->allow_saved_pm = $allow_saved_pm && $allow_saved_pm === 'yes' ;

        // WooCommerce Subscriptions + saved-payment-method support was tied
        // to the removed wallet/REST integration ; direct-SOAP only.

        // Default texts (settings).
        $this->default_enabled     = __( 'Enable TWINT payment gateway.', 'pay4all-for-twint' );
        $this->default_title       = __( 'TWINT', 'pay4all-for-twint' );
        $this->default_description = __( 'Pay with your smartphone', 'pay4all-for-twint' );

        // Initialize settings.
        $this->init_form_fields();
        $this->init_settings();

        // Gateway settings.
        $this->title        = $this->settings[ 'title' ];
        $this->description  = $this->settings[ 'description' ];

        // Pick up the locale-specific instructions configured in the wizard.
        // Falls back to the WC settings field when no override is stored.
        $lang        = \Twnt4All\Backend\ConfigPage::current_supported_lang();
        $localized_i = get_option( 't4a_twint_instructions_' . $lang, '' );
        $this->instructions = $localized_i !== ''
            ? $localized_i
            : ( isset( $this->settings[ 'instructions' ] ) ? $this->settings[ 'instructions' ] : '' );
        $this->icon         = T4A_PLUGIN_URL . 'assets/images/twint_logo_q.svg';

        $this->integration = OrderDataRepository::get_integration();

        if ( version_compare( WOOCOMMERCE_VERSION, '2.0.0', '>=' ) ) {
            add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array(
                &$this,
                'process_admin_options'
            ) );
        } else {
            add_action( 'woocommerce_update_options_payment_gateways', array( &$this, 'process_admin_options' ) );
        }

        if ( $this->integration === 'direct' ) {
            add_action( 'woocommerce_receipt_' . $this->id, array( &$this, 'receipt_page' ) );
        }
        add_action( 'woocommerce_thankyou_' . $this->id, array( $this, 'thankyou_page' ) );
        add_action( 'woocommerce_email_before_order_table', array( $this, 'email_instructions' ), 10, 3 );

        add_action( 'wp_enqueue_scripts', array( $this, 'scripts' ) );

    }

    public function scripts()
    {
        if ( is_checkout() ) {
            wp_enqueue_style( 't4a_twint-frontend', plugin_dir_url( dirname( dirname( __FILE__ ) ) ) . 'assets/css/twint-frontend.css', [], T4A_PLUGIN_VERSION );

            if ( $this->integration === 'direct' ) {
                $bg_ajax = get_option( 't4a_twint_async_bg_ajax' ) ?: T4A_DEFAULT_ASYNC_REQUEST_AJAX;
                if ( $bg_ajax === 'yes' ) {
                    wp_register_script( T4A_PREFIX . '-frontend', T4A_PLUGIN_URL . 'assets/js/frontend.js', [ 'jquery' ], T4A_PLUGIN_VERSION, true );
                    wp_localize_script( T4A_PREFIX . '-frontend', 't4aTwintFrontend', array(
                        'ajaxUrl'        => admin_url( 'admin-ajax.php' ),
                        'texts'          => [
                            'overlayAlt'     => __( 'Redirecting to TWINT', 'pay4all-for-twint' ),
                            'overlayMessage' => __( 'You will be redirected to the payment page of TWINT.', 'pay4all-for-twint' ),
                        ],
                        'overlayIconUrl' => T4A_PLUGIN_URL . 'assets/images/twint_logo_q.svg',
                        'checkoutUrl'    => wc_get_checkout_url(),
                    ) );

                    wp_enqueue_script( T4A_PREFIX . '-frontend' );
                }
            }
            wp_enqueue_script( T4A_PREFIX . '-checkout', T4A_PLUGIN_URL . 'assets/js/checkout.js', [ 'jquery' ], T4A_PLUGIN_VERSION, true );
        }
    }

    /**
     * Initialize settings fields
     */
    function init_form_fields()
    {
        $this->form_fields = array(
            'enabled'      => array(
                'title'   => __( 'Enable/disable', 'pay4all-for-twint' ),
                'type'    => 'checkbox',
                'label'   => $this->default_enabled,
                'default' => 'no'
            ),
            'title'        => array(
                'title'       => __( 'Title:', 'pay4all-for-twint' ),
                'type'        => 'text',
                'description' => __( 'The title of the payment method shown in the fronted.', 'pay4all-for-twint' ),
                'default'     => $this->default_title
            ),
            'description'  => array(
                'title'       => __( 'Description:', 'pay4all-for-twint' ),
                'type'        => 'textarea',
                'description' => __( 'The description of the payment method which will be visible at the checkout page.', 'pay4all-for-twint' ),
                'default'     => $this->default_description
            ),
            'instructions' => array(
                'title'       => __( 'Instructions', 'pay4all-for-twint' ),
                'type'        => 'textarea',
                'description' => __( 'Instructions that will be added to the thank you page and emails.', 'pay4all-for-twint' ),
                'default'     => '',
                'desc_tip'    => true,
            ),
        );
    }

    /**
     * Payment gateway settings.
     */
    public function admin_options()
    {
        echo '<h3>' . esc_html__( 'TWINT Payment Gateway', 'pay4all-for-twint' ) . '</h3>';
        echo '<p>' . esc_html__( 'Payment gateway for online payments by TWINT', 'pay4all-for-twint' ) . '</p>';

        echo '<table class="form-table">';
        $this->generate_settings_html();
        echo '</table>';
    }

    /**
     * TWINT payment fields.
     */
    function payment_fields()
    {
        if ( $this->description ) {
            echo wp_kses_post( wpautop( wptexturize( $this->description ) ) );
        }
        // Saved-payment-method UI + subscriptions checkbox were tied to the
        // removed wallet/REST integration (twnt mode). Direct-SOAP does not
        // support tokenization.
    }

    public function saved_payment_methods()
    {
        // Dead code kept for signature compatibility with WooCommerce ; the
        // wallet/REST integration that fed it has been removed. Direct-SOAP
        // mode does not persist tokens.
    }

    /**
     * Adds a checkbox to the checkout to save the payment method.
     */
    public function save_payment_method_checkbox()
    {
        // Dead code — see saved_payment_methods().
        return;
        // @phpstan-ignore-next-line
        printf(
            '<p class="form-row woocommerce-SavedPaymentMethods-saveNew">
                    <input id="wc-%1$s-new-payment-method" name="wc-%1$s-new-payment-method" type="checkbox" value="true" style="width:auto;" />
                    <label for="wc-%1$s-new-payment-method" style="display:inline;">%2$s</label>
                </p>',
            esc_attr( $this->id ),
            esc_html( apply_filters( /* phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WooCommerce-style filter documented for theme overrides ; renaming would break existing customizations. */ 'wc_t4a_twint_save_to_account_text', __( 'Save payment information to my account for future purchases.', 'pay4all-for-twint' ) ) )
        );

        printf(
            '<p id="twnt_tw_wc_new_payment_method_description" class="form-row woocommerce-SavedPaymentMethods-saveNew-description" style="display:none;">
                    <input id="wc-%1$s-new-payment-method-description" name="wc-%1$s-new-payment-method-description" type="text" value="" style="width:auto; display:block;" />
                    <label for="wc-%1$s-new-payment-method-description" style="display:inline;">%2$s</label>
                </p>',
            esc_attr( $this->id ),
            esc_html( apply_filters( /* phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WooCommerce-style filter (wc_ prefix, `save_to_account_description`) documented for theme overrides ; renaming would break existing customizations. */ 'wc_t4a_twint_save_to_account_description', __( 'Enter a description to make it easier to identify the saved payment method (optional).', 'pay4all-for-twint' ) ) )
        );
    }

    public function receipt_page( $order_id )
    {
        $bg_ajax = get_option( 't4a_twint_async_bg_ajax' ) ?: T4A_DEFAULT_ASYNC_REQUEST_AJAX;

        if ( $bg_ajax !== 'yes' ) {
            parent::receipt_page( $order_id );
            return;
        }

        if ( $url = $this->start_twint_payment_process( $order_id ) ) {

            $this->schedule_background_task( $order_id );

            $error_url = add_query_arg( [
                'order_id' => $order_id,
            ], \WC()->api_request_url( T4A_PREFIX . '_webhook_redirect' ) );

            $args = [
                'redirect_url' => $url,
                'error_url'    => $error_url,
                'order_id'     => $order_id
            ];

            pay4all_for_twint_get_template( 'payment-page.php', $args );
        } else {

            DirectMailer::send_start_order_failed_email( $order_id );

            $order = wc_get_order( $order_id );
            wp_safe_redirect( $order->get_checkout_payment_url() );
            die();
        }
    }

    /**
     * Order received page output.
     */
    public function thankyou_page()
    {
        if ( $this->instructions ) {
            echo wp_kses_post( wpautop( wptexturize( $this->instructions ) ) );
        }
    }

    /**
     * Add content to the WC emails.
     *
     * @access public
     * @param \WC_Order $order Order object.
     * @param bool $sent_to_admin Sent to admin.
     * @param bool $plain_text Email format: plain text or HTML.
     */
    public function email_instructions( $order, $sent_to_admin, $plain_text = false )
    {
        if ( $this->instructions && !$sent_to_admin && $this->id === $order->get_payment_method() ) {
            echo wp_kses_post( wpautop( wptexturize( $this->instructions ) ) . PHP_EOL );
        }
    }

    /**
     * Start the TWINT payment process.
     *
     * @param int $order_id
     * @global $woocommerce
     *
     */
    public function start_twint_payment_process( $order_id )
    {
        DirectLogger::log_event( 'Start payment', 'Payment process started', ConfigProvider::create()->set_order( $order_id ) );

        $order = wc_get_order( $order_id );

        // Set flag: Initiated with TWINT.
        WooBridge::update_order_meta( $order, '_' . T4A_PREFIX . '_payment_initiated', time() );

        $twint = TwintCore::create_for_transaction( $order_id, T4A_MERCHANT_UUID );

        if ( !$twint ) {
            return false;
        }

        return $twint->transactionHandler->start_order();
    }

    /**
     * Reads and validates the (order_id, uuid) tuple from the incoming
     * webhook query string.
     *
     * These endpoints have no possible WP nonce (called by the TWINT server
     * or from the customer's browser after leaving the checkout), so
     * origin-check is done via the transaction UUID stored on the order:
     * we require the caller to know the exact UUID we handed to TWINT.
     * Uses hash_equals to avoid timing-based enumeration.
     *
     * @return array{order_id:int,uuid:string}|null null when the request is
     *   malformed or the UUID does not match; the caller should abort.
     */
    private static function read_and_validate_webhook_query()
    {
        // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Webhook called by TWINT server ; origin is validated below via the order UUID stored server-side, no WP nonce possible.
        if ( !isset( $_GET[ 'order_id' ] ) || !isset( $_GET[ 'uuid' ] ) ) {
            return null;
        }
        $order_id = absint( wp_unslash( $_GET[ 'order_id' ] ) );
        $uuid_get = sanitize_text_field( wp_unslash( (string) $_GET[ 'uuid' ] ) );
        // phpcs:enable WordPress.Security.NonceVerification.Recommended

        if ( $order_id <= 0 || $uuid_get === '' ) {
            return null;
        }

        $expected = (string) OrderDataRepository::get_order_data( $order_id, 'order_uuid' );
        if ( $expected === '' || !hash_equals( $expected, $uuid_get ) ) {
            return null;
        }

        return [ 'order_id' => $order_id, 'uuid' => $uuid_get ];
    }

    /**
     * Handles the redirect from TWINT via webhook.
     *
     * @return void
     */
    public static function handle_webhook_redirect()
    {
        $ctx = static::read_and_validate_webhook_query();
        if ( $ctx === null ) {
            DirectLogger::log_error( 'handle_webhook_redirect', 'Missing or invalid order_id/uuid pair' );
            status_header( 403 );
            die();
        }

        DirectLogger::log_event( 'handle_webhook_redirect', 'start', $ctx[ 'order_id' ] );

        $twint = TwintCore::create_for_transaction( $ctx[ 'order_id' ], T4A_MERCHANT_UUID );

        if ( !$twint ) {

            $order = wc_get_order( $ctx[ 'order_id' ] );
            wc_add_notice( __( 'TWINT is not available at the moment. Please try another payment method or contact the website administrator.', 'pay4all-for-twint' ), 'error' );
            wp_safe_redirect( $order->get_checkout_payment_url() );
            die();
        }

        $twint->transactionHandler->handle_webhook_response( $ctx[ 'uuid' ] );
    }

    /**
     * Handles the cancel request from TWINT via webhook.
     *
     * @return void
     */
    public static function handle_webhook_cancel()
    {
        $ctx = static::read_and_validate_webhook_query();
        if ( $ctx === null ) {
            DirectLogger::log_error( 'handle_webhook_cancel', 'Missing or invalid order_id/uuid pair' );
            status_header( 403 );
            die();
        }

        DirectLogger::log_event( 'handle_webhook_cancel', 'start', $ctx[ 'order_id' ] );

        $twint = TwintCore::create_for_transaction( $ctx[ 'order_id' ], T4A_MERCHANT_UUID );

        if ( !$twint ) {

            $order = wc_get_order( $ctx[ 'order_id' ] );
            wc_add_notice( __( 'TWINT is not available at the moment. Please try another payment method or contact the website administrator.', 'pay4all-for-twint' ), 'error' );
            wp_safe_redirect( $order->get_checkout_payment_url() );
            die();
        }

        $twint->transactionHandler->handle_webhook_cancel( $ctx[ 'uuid' ] );
    }

    /**
     * Handles TWINT Order Update Notifications (OUN).
     *
     * Unlike the redirect/cancel webhooks (which return the customer's browser
     * to a checkout page after the call), OUN is a server-to-server call from
     * TWINT and must terminate with a bare HTTP 200 - no body, no redirect.
     * TWINT retries the OUN with exponential backoff if it does not see 200,
     * so the success path stays cheap and silent.
     */
    public static function handle_webhook_order_update_notification()
    {
        $ctx = static::read_and_validate_webhook_query();
        if ( $ctx === null ) {
            DirectLogger::log_error( 'handle_webhook_order_update_notification', 'Missing or invalid order_id/uuid pair' );
            status_header( 400 );
            die();
        }

        DirectLogger::log_event( 'handle_webhook_order_update_notification', 'start', $ctx[ 'order_id' ] );

        $twint = TwintCore::create_for_transaction( $ctx[ 'order_id' ], T4A_MERCHANT_UUID );
        if ( !$twint ) {
            DirectLogger::log_error( 'handle_webhook_order_update_notification', 'Could not create TWINT client.', $ctx[ 'order_id' ] );
            status_header( 500 );
            die();
        }

        // Re-uses the same polling/handling pipeline as the redirect webhook so
        // the WC order ends up in the same state regardless of which channel
        // notified us first.
        $twint->transactionHandler->handle_webhook_response( $ctx[ 'uuid' ] );

        // Acknowledge to TWINT so the OUN is not retried.
        status_header( 200 );
        die();
    }

    public static function handle_transaction_redirect()
    {
        // Wallet/REST integration removed ; the customer-facing return URL from
        // the removed twnt mode. Direct-SOAP mode uses its own redirect handled
        // by the `woocommerce_receipt_t4a_twint` action wired above.
        Journal::event( 'handle_transaction_redirect: legacy endpoint hit, ignored (wallet mode removed).' );
    }

    public static function handle_transaction_cancel()
    {
        Journal::event( 'handle_transaction_cancel: legacy endpoint hit, ignored (wallet mode removed).' );
    }

    public static function handle_transaction_webhook()
    {
        Journal::event( 'handle_transaction_webhook: legacy endpoint hit, ignored (wallet mode removed).' );
        // phpcs:enable WordPress.Security.NonceVerification.Recommended
    }

    /**
     * Runs the background task to check the TWINT order.
     *
     * @param $order_id
     * @param $order_uuid
     * @param $time
     * @return void
     */
    public static function monitor_order_background_task( $order_id, $order_uuid, $time )
    {
        DirectLogger::log_event( 'monitor_order_background_task', 'start', $order_id );

        $twint = TwintCore::create_for_transaction( $order_id, T4A_MERCHANT_UUID );
        if ( !$twint ) {
            DirectLogger::log_error( 'monitor_order_background_task', 'Could not create TWINT object.', $order_id );
            return;
        }

        $saved_order_uuid = $twint->dataProvider->get_order_uuid( false );

        $iteration = 0;
        while ( !$saved_order_uuid && $iteration < 5 ) {
            $saved_order_uuid = $twint->dataProvider->get_order_uuid( false );

            DirectLogger::log_error( 'monitor_order_background_task', 'No order UUID', $order_id );
            session_write_close();
            sleep( 2 );
            $iteration++;
        }

        if ( empty( $order_uuid ) || empty( $saved_order_uuid ) || $saved_order_uuid != $order_uuid ) {
            DirectLogger::log_error( 'monitor_order_background_task', sprintf( 'Wrong order UUID: expected %1$s, given %2$s.', $saved_order_uuid, $order_uuid ), $order_id );
            return;
        }

        $twint->transactionHandler->background_task( $time );
    }

    /**
     * Process the payment and return the result.
     *
     * @param $order_id
     */
    function process_payment( $order_id )
    {
        $bg_ajax = get_option( 't4a_twint_async_bg_ajax' ) ?: T4A_DEFAULT_ASYNC_REQUEST_AJAX;
        if ( $bg_ajax === 'yes' ) {
            $order = wc_get_order( $order_id );

            return [
                'result'   => 'success',
                'redirect' => $order->get_checkout_payment_url( true ),
            ];
        }

        if ( $url = $this->start_twint_payment_process( $order_id ) ) {

            $this->schedule_background_task( $order_id );

            return [
                'result'   => 'success',
                'redirect' => $url,
            ];
        } else {
            DirectMailer::send_start_order_failed_email( $order_id );
        }
    }

    /**
     * Processes the request to save a new payment method on the customer payment method screen.
     */
    public function add_payment_method()
    {
        // The wallet/REST integration that supported "add payment method" for
        // recurring TWINT payments has been removed. Direct-SOAP is one-shot.
        return;
    }

    /**
     * Handles refunds. Refunds with $amount higher than the paid amount are possible.
     *
     * @param $order_id
     * @param null $amount
     * @param string $reason
     * @return bool
     */
    public function process_refund( $order_id, $amount = null, $reason = '' )
    {
        $amount = wc_format_decimal( $amount, 2 );

        $twint = TwintCore::create_for_admin( $order_id, T4A_MERCHANT_UUID );

        if ( !$twint ) {
            return false;
        }

        $transaction_data = $twint->transactionHandler->create_refund( $amount );

        if ( !$transaction_data ) {
            return false;
        }

        return true;
    }

    private function schedule_background_task( $order_id )
    {
        $bg_cron = get_option( 't4a_twint_async_bg_cron' ) ?: T4A_DEFAULT_ASYNC_REQUEST_CRON;
        if ( $bg_cron == 'yes' ) {
            if ( !wp_next_scheduled( T4A_PREFIX . '_bg_cron_task_single', [ $order_id ] ) ) {
                wp_schedule_single_event( time() + 300, T4A_PREFIX . '_bg_cron_task_single', [ $order_id ] );
            }
        }
    }

    public static function run_scheduled_background_task( $order_id )
    {
        DirectLogger::log_event( 'run_scheduled_background_task', 'start', $order_id );

        $twint = TwintCore::create_for_transaction( $order_id, T4A_MERCHANT_UUID );
        if ( !$twint ) {
            DirectLogger::log_error( 'run_scheduled_background_task', 'Could not create TWINT object.', $order_id );
            return;
        }

        $order_uuid = $twint->dataProvider->get_order_uuid();

        if ( empty( $order_uuid ) ) {
            DirectLogger::log_error( 'run_scheduled_background_task', 'No order UUID.' );
            return;
        }

        $twint->transactionHandler->background_task();
    }
}
