<?php

namespace Twnt4All\Engine\Adapter;

use Twnt4All\Checkout\OrderDataRepository;
use Twnt4All\Support\Journal;
use Twnt4All\Engine\EnvironmentConfig;
use Twnt4All\Engine\Contract\ConfigProviderInterface;
use Twnt4All\Support\WooBridge;
use Twnt4All\Support\WpBridge;

class ConfigProvider implements ConfigProviderInterface
{
    /** @var string */
    public $order_id;

    /** @var mixed WooCommerce order instance when WC is present (see pay4all-pro). */
    public $order;

    /** @var bool */
    private $is_deferred;

    /** @var string|int */
    private $process_id;

    /** @var string */
    private $merchant_uuid;

    public function __construct()
    {
        if ( get_option( 't4a_twint_settings_payment_type' ) == 'deferred' ) {
            $this->is_deferred = true;
        }

        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- server-generated float timestamp, never user input.
        $this->process_id = isset( $_SERVER[ 'REQUEST_TIME_FLOAT' ] ) ? (float) $_SERVER[ 'REQUEST_TIME_FLOAT' ] : uniqid();
    }

    public static function create()
    {
        return new self();
    }

    /**
     * @param string $order_id
     * @return ConfigProvider
     */
    public function set_order( $order_id )
    {
        $this->order_id = $order_id;
        // The admin-side "Test connection" flow passes a null order_id (the
        // SOAP call is read-only). WpBridge::get_wc_order() returns null when
        // WooCommerce is temporarily deactivated, so the test can still run.
        $this->order    = $order_id ? WpBridge::get_wc_order( $order_id ) : null;

        return $this;
    }

    /**
     * Returns the currency ISO Code.
     *
     * @return string
     */
    public function get_currency()
    {
        if ( $this->order && $currency = $this->order->get_currency() ) {
            return $currency;
        }

        // Guard: get_woocommerce_currency() lives in WC only.
        $default_currency = function_exists( 'get_woocommerce_currency' ) ? get_woocommerce_currency() : 'CHF';
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- T4A_PREFIX equals the plugin's internal prefix (`t4a_twint`) ; the sniffer cannot statically resolve the concatenation.
        return apply_filters( T4A_PREFIX . '_currency', $default_currency );
    }

    /**
     * Returns saved the TWINT order data.
     *
     * order data =
     * [
     *  status,
     *  pairing_uuid,
     *  order_uuid,
     *  twint_status,
     *  twint_status_reason
     * ]
     *
     * @param null $property
     * @param bool $cache
     * @return array|string|bool
     */
    public function get_order_data( $property = null, $cache = true )
    {
        return OrderDataRepository::get_order_data( $this->order_id, $property );
    }

    /**
     * Returns the order id of the system.
     *
     * @return string|int
     */
    public function get_order_id()
    {
        return $this->order_id;
    }

    /**
     * Returns the Order UUID
     *
     * @param bool $cache
     * @return string|false
     */
    public function get_order_uuid( $cache = true )
    {
        return $this->get_order_data( 'order_uuid' );
    }

    /**
     * Returns the merchant reference for the order.
     *
     * @param bool $reversal
     * @return string
     */
    public function get_or_create_merchant_reference( $reversal = false )
    {
        $reference = OrderDataRepository::get_merchant_reference( $this->order_id );

        if ( !empty( $reference ) ) {

            $reference = mb_substr( $reference, 0, 50, 'utf-8' );

            if ( $reversal ) {
                $reference = mb_substr( $reference, 0, 35, 'utf-8' );
                $reference .= '_' . uniqid();
            }
            return $reference;
        }

        // Guard: WC order helpers live in pay4all-pro.
        $order = WpBridge::get_wc_order( $this->order_id );

        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- T4A_PREFIX equals the plugin's internal prefix (`t4a_twint`) ; the sniffer cannot statically resolve the concatenation.
        $reference = apply_filters( T4A_PREFIX . '_merchant_reference_order_id', $this->order_id, $order );

        // Additional customer data.
        $add_billing_firstname = get_option( 't4a_twint_reference_include_billing_firstname' );
        if ( $add_billing_firstname && $add_billing_firstname == 'yes' ) {
            $bfn = WooBridge::get( 'billing_first_name', $order );
            if ( !empty( $bfn ) ) {
                $reference .= '_' . $bfn;
            }
        }

        $add_billing_lastname = get_option( 't4a_twint_reference_include_billing_lastname' );
        if ( $add_billing_lastname && $add_billing_lastname == 'yes' ) {
            $bln = WooBridge::get( 'billing_last_name', $order );
            if ( !empty( $bln ) ) {
                $reference .= '_' . $bln;
            }
        }

        $add_billing_company = get_option( 't4a_twint_reference_include_billing_company' );
        if ( $add_billing_company && $add_billing_company == 'yes' ) {
            $bc = WooBridge::get( 'billing_company', $order );
            if ( !empty( $bc ) ) {
                $reference .= '_' . $bc;
            }
        }

        $add_shipping_firstname = get_option( 't4a_twint_reference_include_shipping_firstname' );
        if ( $add_shipping_firstname && $add_shipping_firstname == 'yes' ) {
            $sfn = WooBridge::get( 'shipping_first_name', $order );
            if ( !empty( $sfn ) ) {
                $reference .= '_' . $sfn;
            }
        }

        $add_shipping_lastname = get_option( 't4a_twint_reference_include_shipping_lastname' );
        if ( $add_shipping_lastname && $add_shipping_lastname == 'yes' ) {
            $sln = WooBridge::get( 'shipping_last_name', $order );
            if ( !empty( $sln ) ) {
                $reference .= '_' . $sln;
            }
        }

        $add_shipping_company = get_option( 't4a_twint_reference_include_shipping_company' );
        if ( $add_shipping_company && $add_shipping_company == 'yes' ) {
            $sc = WooBridge::get( 'shipping_company', $order );
            if ( !empty( $sc ) ) {
                $reference .= '_' . $sc;
            }
        }

        // Length of uniqid is 13.
        $reference = mb_substr( $reference, 0, 35, 'utf-8' );
        $reference .= '_' . uniqid();

        return $reference;
    }

    /**
     * Saves the TWINT order data.
     *
     * @param $data
     * @return void
     */
    public function save_order_data( $data )
    {
        OrderDataRepository::save_order_data( $this->order_id, $data );
    }

    /**
     * Deletes all data in array $data.
     *
     * @param array $data
     */
    public function delete_order_data( $data )
    {
        OrderDataRepository::delete_order_data( $this->order_id, $data );
    }

    /**
     * Saves a transaction belonging to the current order.
     * [operation, reference, amount, fee]
     *
     * @param $data
     * @return mixed
     */
    public function save_transaction( $data )
    {
        OrderDataRepository::save_transaction( $this->order_id, $data );
    }

    /**
     * Returns all transactions for the order.
     *
     * @return array
     */
    public function get_transactions()
    {
        OrderDataRepository::get_transactions( $this->order_id );
    }

    /**
     * Returns the amount in CHF formatted to 2 decimals.
     *
     * @return string
     * @throws \Exception
     */
    public function get_formatted_amount()
    {
        return str_replace( ',', '.', $this->order->get_formatted_order_total() );
    }

    /**
     * Returns true if StartOrder was called for this order.
     *
     * @return bool
     */
    public function is_order_started()
    {
        // Guard: WC order helpers live in pay4all-pro.
        if ( ! class_exists( '\\Twnt4All\\Support\\WooBridge' ) ) { return false; }
//        wp_cache_delete( $this->order_id, 'post_meta' );
        $order = WpBridge::get_wc_order( $this->order_id );

        if ( !$order ) {
            return false;
        }

        if ( true === $order->get_meta( '_' . T4A_PREFIX . '_order_started', true ) ) {
            return true;
        }
        WooBridge::update_order_meta( $order, '_' . T4A_PREFIX . '_order_started', true );
        return false;
    }

    /**
     * Returns true if the current order is complete.
     *
     * @param bool $cache
     * @return bool
     */
    public function is_order_complete( $cache = false )
    {
        // Guard: WC order helpers live in pay4all-pro.
        if ( ! class_exists( '\\Twnt4All\\Support\\WooBridge' ) ) { return false; }
        if ( !$cache ) {

            $order_id = $this->order->get_id();
            Journal::event( 'is_order_complete check for ID ' . $order_id );
            wp_cache_delete( $order_id, 'post_meta' );

            $order = WpBridge::get_wc_order( $order_id );
            if ( !$order ) {
                Journal::error( 'Order not found. Using cached order from ConfigProvider.' );
                $order = $this->order;
            }

            //update_postmeta_cache( [ $this->order->get_id() ] );
            return WooBridge::is_order_complete( $order );
        } else {
            return WooBridge::is_order_complete( $this->order );
        }
    }

    /**
     * Returns true if the payment should be manually captured (PAYMENT_DEFERRED).
     *
     * @return bool
     */
    public function is_payment_deferred()
    {
        return $this->is_deferred;
    }

    /**
     * Returns the URL of the order confirmation page (successful order).
     *
     * @return string
     */
    public function get_order_received_url()
    {
        return $this->order->get_checkout_order_received_url();
    }

    /**
     * Returns the URL of the checkout page.
     *
     * @return string
     */
    public function get_checkout_url()
    {
        // Guard: WC helpers live in pay4all-pro. Fallback to home page.
        return function_exists( 'wc_get_checkout_url' ) ? wc_get_checkout_url() : home_url( '/' );
    }

    /**
     * Returns the payment page of the checkout where the payment method can be selected.
     *
     * @return string
     */
    public function get_checkout_payment_url()
    {
        return $this->order->get_checkout_payment_url();
    }

    /**
     * Returns the URL to redirect to when an order is cancelled.
     *
     * @return string
     */
    public function get_cancel_order_url()
    {
        return $this->order->get_cancel_order_url();
    }


    /**
     * Returns the URL to redirect to when the timeout is reached.
     *
     * @return string
     */
    public function get_timeout_url()
    {
        return $this->order->get_checkout_payment_url();
    }

    /**
     * Returns the URL to redirect to when the payment failed.
     *
     * @return string
     */
    public function get_failure_url()
    {
        return $this->order->get_checkout_payment_url();
    }

    /**
     * Returns the total amount of the order.
     *
     * @return float
     */
    public function get_total()
    {
        // Guard: WC helpers live in pay4all-pro. Fallback to naive number_format.
        if ( ! function_exists( 'wc_format_decimal' ) ) {
            return number_format( (float) $this->order->get_total(), 2, '.', '' );
        }
        return wc_format_decimal( $this->order->get_total(), 2 );
    }

    /**
     * Returns the directory of the current shop where TWINT files are stored.
     *
     * @return string
     */
    public function get_twint_files_dir()
    {
        return EnvironmentConfig::get_uploads_dir();
    }

    /**
     * Returns the timeout in seconds.
     *
     * @return int
     */
    public function get_timeout()
    {
        $timeout = intval( get_option( 't4a_twint_settings_timeout' ) );

        if ( !$timeout || ( $timeout <= 0 ) || $timeout > T4A_TIMEOUT ) {
            // Default timeout
            return T4A_TIMEOUT;
        }

        return $timeout;
    }

    /**
     * Returns the timeout of the server to run scripts.
     *
     * @return mixed
     */
    public function get_max_execution_time()
    {
        $max_exec_time = T4A_MAX_EXEC_TIME;

        if ( function_exists( 'ini_get' ) ) {
            $max_exec_time = ini_get( 'max_execution_time' );
        }

        return max( 0, intval( $max_exec_time ) - 10 );
    }

    /**
     * Returns true if the allowed memory has exceeded.
     *
     * @return bool
     */
    public function has_memory_exceeded()
    {
        $memory_limit = '128M';

        if ( function_exists( 'ini_get' ) ) {
            $memory_limit = ini_get( 'memory_limit' );
        }

        if ( !$memory_limit || -1 === intval( $memory_limit ) ) {
            $memory_limit = '32000M';
        }

        $memory_limit   = intval( $memory_limit ) * 1024 * 1024 * 0.9; // 90 percent
        $current_memory = memory_get_usage( true );

        return $current_memory >= $memory_limit;
    }

    /**
     * Returns the interval in seconds for SOAP requests.
     *
     * @return int
     */
    public function get_soap_interval()
    {
        $interval = intval( get_option( 't4a_twint_settings_soap_request_interval' ) );

        if ( !$interval || ( $interval <= 0 ) ) {
            // Default timeout
            return T4A_SOAP_INTERVAL;
        }

        return $interval;
    }

    public function get_process_id()
    {
        return $this->process_id;
    }

    /**
     * Returns the URL for handling the webhook redirect response.
     *
     * @return mixed
     */
    public function get_webhook_redirect_url( $args = [] )
    {
        $args = array_merge( [
            'order_id'     => $this->order->get_id(),
            'order_number' => $this->order->get_order_number(),
            'uuid'         => $this->get_order_uuid(),
        ], $args );

        return add_query_arg( $args, $this->wc_api_request_url( T4A_PREFIX . '_webhook_redirect' ) );
    }

    /**
     * Guarded wrapper around WooCommerce's api_request_url helper so the class
     * file itself never contains a top-level unguarded reference — Plugin
     * Check WP.org treats the free plugin as WC-free code.
     */
    private function wc_api_request_url( $endpoint )
    {
        if ( ! function_exists( 'WC' ) ) { return home_url( '/' ); }
        $wc = call_user_func( 'WC' );
        return $wc->api_request_url( $endpoint );
    }

    /**
     * Returns the URL for handling the webhook cancel response.
     *
     * @param $args
     * @return mixed
     */
    public function get_webhook_cancel_url( $args = [] )
    {
        $args = array_merge( [
            'order_id'     => $this->order->get_id(),
            'order_number' => $this->order->get_order_number(),
            'uuid'         => $this->get_order_uuid(),
        ], $args );

        return add_query_arg( $args, $this->wc_api_request_url( T4A_PREFIX . '_webhook_cancel' ) );
    }

    public function get_webhook_order_update_notification_url()
    {
        $args = [
            'order_id'     => $this->order->get_id(),
            'order_number' => $this->order->get_order_number(),
            'r'            => OrderDataRepository::get_merchant_reference( $this->order->get_id() ),
        ];
        return add_query_arg( $args, $this->wc_api_request_url( T4A_PREFIX . '_webhook_oun' ) );
    }

    /**
     * @param $merchant_id
     * @return $this|ConfigProviderInterface
     */
    public function set_merchant( $merchant_id )
    {
        $this->merchant_uuid = $merchant_id;

        return $this;
    }

    public function get_merchant_uuid()
    {
        return $this->merchant_uuid;
    }
}
