<?php

namespace Twnt4All\Engine\Adapter;

use Twnt4All\Engine\Contract\EventListenerInterface;
use Twnt4All\Engine\Contract\LockInterface;
use Twnt4All\Engine\Service\TransactionService;
use Twnt4All\Engine\Support\SemaphoreLock;
use Twnt4All\Support\WooBridge;
use Twnt4All\Support\WpBridge;
use Twnt4All\Engine\Mixin\ConfigAwareTrait;
use Twnt4All\Engine\EnvironmentConfig;

class DomainEvents implements EventListenerInterface
{
    use ConfigAwareTrait;

    /**
     * EventListenerInterface constructor.
     * @param $data_provider
     */
    public function __construct( $data_provider )
    {
        $this->data_provider = $data_provider;
    }

    /**
     * Initialise actions.
     */
    public static function init_actions()
    {
        add_action( T4A_PREFIX . '_confirm_order', __CLASS__ . '::confirm_order', 10, 1 );
        add_action( T4A_PREFIX . '_remove_all_locks', __CLASS__ . '::remove_all_locks', 10, 1 );
    }

    public function on_before_handle_successful_payment()
    {
        // Guard: WooCommerce order helpers live in pay4all-pro.
        if ( ! function_exists( 'wc_get_order' ) ) { return; }
        if ( ! class_exists( '\\Twnt4All\\Support\\WooBridge' ) ) { return; }
        $order_id = $this->data_provider->get_order_id();
        $order    = WpBridge::get_wc_order( $order_id );
        if ( empty( WooBridge::get_order_meta( $order, '_' . T4A_PREFIX . '_payment_successful', true ) ) ) {
            WooBridge::update_order_meta( $order, '_' . T4A_PREFIX . '_payment_successful', time() );
        }
    }

    /**
     * Handles all order updates after a successful payment.
     * @param LockInterface $lock
     */
    public function on_successful_payment( $lock )
    {
        // Guard: WooCommerce order helpers live in pay4all-pro.
        if ( ! function_exists( 'wc_get_order' ) ) { return; }
        if ( ! class_exists( '\\Twnt4All\\Support\\WooBridge' ) ) { return; }
        $order_id    = $this->data_provider->get_order_id();
        $order       = WpBridge::get_wc_order( $order_id );
        $is_deferred = $this->data_provider->is_payment_deferred();

        if ( !$order ) {
            $this->log_error( 'on_successful_payment', 'Order not found for ID ' . $order_id );
            return;
        }

        if ( WooBridge::is_order_complete( $order ) ) {
            $this->log_event( 'on_successful_payment', sprintf( 'Order %1$s already completed.', $order_id ) );
            return;
        }

        $uniqid = uniqid();
        WooBridge::update_order_meta( $order, '_' . T4A_PREFIX . '_payment_complete', $uniqid );
        if ( WooBridge::get_order_meta( $order_id, '_' . T4A_PREFIX . '_payment_complete', true ) != $uniqid ) {
            $this->log_event( 'on_successful_payment', sprintf( 'Order update lock IDS: expected %1$s, got %2$s.', $uniqid, WooBridge::get_order_meta( $order_id, '_' . T4A_PREFIX . '_payment_complete', true ) ) );
            return;
        }

        if ( WooBridge::get_order_meta( $order_id, '_twint_order_payment_status', true ) ) {
            $this->log_event( 'on_successful_payment', 'Order status update locked.' );
        }

        if ( WooBridge::get_order_meta( $order_id, '_' . T4A_PREFIX . '_payment_complete', true ) == $uniqid && !WooBridge::get_order_meta( $order_id, '_twint_order_payment_status', true ) ) {

            $this->log_event( 'Update order status', 'start' );

            WooBridge::update_order_successful( $order, $is_deferred );

            // Update payment status.
            WooBridge::update_order_meta( $order, '_twint_order_payment_status', 'SUCCESS', false );

            // Build the success note the merchant sees in WC > Orders. Format:
            // "Paid via TWINT. Paid on {date} at {time}. Customer IP address: {ip}."
            $paid_at = function_exists( 'wp_date' )
                ? wp_date( get_option( 'date_format', 'F j, Y' ) . ' \a\t G\hi', current_time( 'timestamp' ) )
                : date_i18n( get_option( 'date_format', 'F j, Y' ) . ' \a\t G\hi' );
            $customer_ip = method_exists( $order, 'get_customer_ip_address' ) ? $order->get_customer_ip_address() : '';
            $note = sprintf(
                /* translators: %1$s formatted payment date, %2$s customer IP address. */
                __( 'Paid via TWINT. Paid on %1$s. Customer IP address: %2$s.', 'pay4all-for-twint' ),
                $paid_at,
                $customer_ip ?: __( 'unknown', 'pay4all-for-twint' )
            );
            $order->add_order_note( $note );

            $this->log_event( 'Updated order status', 'complete' );

            WooBridge::delete_order_meta( $order, '_' . T4A_PREFIX . '_payment_successful' );

            // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- T4A_PREFIX equals the plugin's internal prefix (`t4a_twint`) ; the sniffer cannot statically resolve the concatenation.
            do_action( T4A_PREFIX . '_after_payment_complete', $order_id );
        }
//        $lock->release();

        /*     if ( !wp_next_scheduled( T4A_PREFIX . '_remove_all_locks', [ $order_id ] ) ) {
                 wp_schedule_single_event( time() + 600, T4A_PREFIX . '_remove_all_locks', [ $order_id ] );
             }*/
    }

    /**
     * @param $response
     * @return void
     */
    public function on_failed_payment( $reason )
    {
        // Guard: WooCommerce order helpers live in pay4all-pro.
        if ( ! function_exists( 'wc_get_order' ) ) { return; }
        if ( $reason !== TransactionService::TWINT_REASON_CLIENT_ABORT && get_option( 't4a_twint_settings_order_status_failed' ) === 'failed' ) {
            $order_id = $this->data_provider->get_order_id();
            $order    = WpBridge::get_wc_order( $order_id );
            $order->update_status( 'failed' );
        }
    }

    /**
     * Handles the cancellation by the customer.
     */
    public function on_cancelled_by_customer()
    {
        // Guard: WooCommerce order helpers live in pay4all-pro.
        if ( ! function_exists( 'wc_get_order' ) ) {
            wp_safe_redirect( $this->data_provider->get_checkout_payment_url() );
            exit;
        }
        $order_id = $this->data_provider->get_order_id();
        $order    = WpBridge::get_wc_order( $order_id );

        if ( get_option( 't4a_twint_settings_cancelled_order_status' ) === 'cancelled' ) {
            wp_safe_redirect( $order->get_cancel_order_url() );
            exit;
        }

        wp_safe_redirect( $this->data_provider->get_checkout_payment_url() );
        exit;
    }

    /**
     * Returns true if a WooCommerce checkout notice of type $type is already enqueued which starts with 'TWINT'.
     *
     * @param string $notice
     * @param string $type
     */
    private function add_checkout_notice( $notice, $type )
    {
        // Guard: WC notices helpers live in pay4all-pro.
        if ( ! function_exists( 'wc_add_notice' ) ) { return; }
        if ( function_exists( 'wc_has_notice' ) && wc_has_notice( $notice, $type ) ) {
            return;
        }

        wc_add_notice( $notice, $type );
    }

    public static function remove_all_locks( $order_id )
    {
        SemaphoreLock::remove_all_locks( 'order-started-' . $order_id, EnvironmentConfig::get_uploads_dir() );
        SemaphoreLock::remove_all_locks( 'update-payment-complete-' . $order_id, EnvironmentConfig::get_uploads_dir() );
    }

    public function on_webhook( $result )
    {
        // Guard: WooCommerce order helpers live in pay4all-pro. Without WC we
        // can't reconcile a WC order - send the user back to a safe URL.
        if ( ! function_exists( 'wc_get_order' ) ) {
            wp_safe_redirect( $this->data_provider->get_checkout_url() );
            die();
        }
        $status = $result[ 'status' ] ?? 'null';
        $reason = $result[ 'reason' ] ?? 'null';

        $this->log_event( 'on_webhook', sprintf( 'Status %1$s, reason %2$s', $status, $reason ) );

        switch ( $status ) {

            case TransactionService::TWINT_STATUS_SUCCESS:

                if ( $reason == TransactionService::TWINT_REASON_ORDER_OK ) {
                    wp_safe_redirect( $this->data_provider->get_order_received_url() );
                    die();
                }

                $this->log_error( 'on_webhook', 'Partial amount approved' );

                $subject = __( 'TWINT payment was successful but only partial amount was approved', 'pay4all-for-twint' );
                /* translators: %1$s dynamic value(s). */
                $message = sprintf( __( 'The TWINT payment for order %1$s was successful but only a partial amount was approved. The WooCommerce order was not successful.', 'pay4all-for-twint' ), $this->data_provider->get_order_id() );
                DirectMailer::send_admin_email( $subject, $message );

                $this->add_checkout_notice( __( 'Payment failed because it was only partially approved. Please contact the shop administrator.', 'pay4all-for-twint' ), 'error' );
                wp_safe_redirect( $this->data_provider->get_checkout_payment_url() );
                die();

            case TransactionService::TWINT_STATUS_IN_PROGRESS:

                switch ( $reason ) {

                    case TransactionService::TWINT_REASON_ORDER_CONFIRMATION_PENDING:

                        if ( !$this->data_provider->is_payment_deferred() ) {

                            // Spawn single cron action to confirm order
                            $order_id = $this->data_provider->get_order_id();
                            if ( !wp_next_scheduled( T4A_PREFIX . '_confirm_order', [ $order_id ] ) ) {
                                wp_schedule_single_event( time() + 300, T4A_PREFIX . '_confirm_order', [ $order_id ] );
                            }
                        }

                        wp_safe_redirect( $this->data_provider->get_order_received_url() );
                        die();

                    case TransactionService::TWINT_REASON_ORDER_RECEIVED:
                    case TransactionService::TWINT_REASON_ORDER_PENDING:
                    default:

                        $this->add_checkout_notice( __( 'The payment with TWINT was not confirmed, but may still have been successful. Please contact the website administrator if you don\'t receive a confirmation email for your order.', 'pay4all-for-twint' ), 'notice' );

                        wp_safe_redirect( $this->data_provider->get_checkout_url() );
                        die();
                }

            case TransactionService::TWINT_STATUS_FAILURE:

                $this->data_provider->save_order_data( [ 'previous_order_uuid' => $this->data_provider->get_order_uuid() ] );
                $this->data_provider->delete_order_data( [ 'order_uuid' ] );

                $this->log_error( 'on_webhook', sprintf( 'Status %1$s, reason %2$s', $status, $reason ) );

                switch ( $reason ) {

                    case TransactionService::TWINT_REASON_CLIENT_ABORT:

                        if ( get_option( 't4a_twint_settings_cancelled_order_status' ) === 'cancelled' ) {
                            $order_id = $this->data_provider->get_order_id();
                            $order    = WpBridge::get_wc_order( $order_id );
                            wp_safe_redirect( $order->get_cancel_order_url() );
                            exit;
                        }

                        wp_safe_redirect( $this->data_provider->get_checkout_payment_url() );
                        die();

                    case TransactionService::TWINT_REASON_MERCHANT_ABORT:
                    case TransactionService::TWINT_REASON_CLIENT_TIMEOUT:
                        $this->add_checkout_notice( __( 'The payment timed out. Please try again or choose a different payment method.', 'pay4all-for-twint' ), 'error' );

                        if ( get_option( 't4a_twint_settings_order_status_failed' ) === 'failed' ) {
//                            $this->log_event('');

                            $order_id = $this->data_provider->get_order_id();
                            $order    = WpBridge::get_wc_order( $order_id );
                            $order->update_status( 'failed' );
                        }

                        wp_safe_redirect( $this->data_provider->get_checkout_url() );
                        die();

                    case TransactionService::TWINT_REASON_GENERAL_ERROR:
                    default:
                        $this->add_checkout_notice( __( 'Payment not successful. Please try again or choose a different payment method.', 'pay4all-for-twint' ), 'error' );

                        if ( get_option( 't4a_twint_settings_order_status_failed' ) === 'failed' ) {
                            $order_id = $this->data_provider->get_order_id();
                            $order    = WpBridge::get_wc_order( $order_id );
                            $order->update_status( 'failed' );
                        }

                        wp_safe_redirect( $this->data_provider->get_checkout_url() );
                        die();
                }

            case TransactionService::STATUS_EXEC_TIMEOUT:

                // The plugin gave up polling monitorOrder before TWINT had a final answer,
                // but the payment is still pending in the customer's TWINT app. Background
                // cron jobs (check_order_statuses) will reconcile the order once TWINT
                // confirms it, so we keep the order in its current "pending payment" state
                // and send the customer back to the payment page with an informational
                // notice rather than the misleading "transaction error" message.
                $this->add_checkout_notice(
                    __( 'The TWINT payment is still in progress. If you confirmed it in the app, your order will be updated automatically within a few minutes.', 'pay4all-for-twint' ),
                    'notice'
                );
                wp_safe_redirect( $this->data_provider->get_checkout_payment_url() );
                die();

            default:
                $this->add_checkout_notice( __( 'Error in transaction. Please contact the shop administrator.', 'pay4all-for-twint' ), 'error' );

                wp_safe_redirect( $this->data_provider->get_checkout_url() );
                die();
        }
    }

    public function spawn_monitor_order_task( $time = 0 )
    {
        $bg_immediate = get_option( 't4a_twint_async_bg_immediate' ) ?: T4A_DEFAULT_ASYNC_REQUEST_IMMEDIATE;
        if ( $bg_immediate == 'yes' ) {
            // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- vendor Async-Task action name ; must stay in sync with the ImmediateMonitorJob subscriber which listens on this exact string.
            do_action( 't4a_twint_monitor_order_immediate', $this->data_provider->get_order_id(), $this->data_provider->get_order_uuid(), $time );
        }

        $bg_shutdown = get_option( 't4a_twint_async_bg_shutdown' ) ?: T4A_DEFAULT_ASYNC_REQUEST_SHUTDOWN;
        if ( $bg_shutdown == 'yes' ) {
            // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- vendor Async-Task action name ; must stay in sync with the MonitorJob subscriber which listens on this exact string.
            do_action( 't4a_twint_monitor_order', $this->data_provider->get_order_id(), $this->data_provider->get_order_uuid(), $time );
        }
    }

    /**
     * @param bool $error
     * @return void
     */
    public function return_to_checkout( $error = false )
    {
        if ( $error ) {
            $this->add_checkout_notice( __( 'Error in transaction. Please try again later or choose a different payment method.', 'pay4all-for-twint' ), 'error' );
        }

        wp_safe_redirect( $this->data_provider->get_checkout_url() );
        die();
    }

    public function return_to_payment_page( $error = false )
    {
        if ( $error ) {
            $this->add_checkout_notice( __( 'Error in transaction. Please try again later or choose a different payment method.', 'pay4all-for-twint' ), 'error' );
        }

        wp_safe_redirect( $this->data_provider->get_checkout_payment_url() );
        die();
    }

    public function redirect_to_receipt_page()
    {
        wp_safe_redirect( $this->data_provider->get_order_received_url() );
        die();
    }

    public function fail_order()
    {
        // Guard: WooCommerce order helpers live in pay4all-pro.
        if ( ! function_exists( 'wc_get_order' ) ) { return; }
        $order_id = $this->data_provider->get_order_id();
        $order    = WpBridge::get_wc_order( $order_id );
        if ( !$order ) {
            $this->log_error( 'fail_order', sprintf( 'Order with ID %1$s not found.' ) );
            return;
        }

        if ( $order->get_status() != 'failed' ) {
            $order->update_status( 'failed', __( 'TWINT payment failed.', 'pay4all-for-twint' ) );
        }
    }
}
