<?php

namespace Devurai\AcceptbluePaymentPro\GooglePay;

use Devurai\AcceptbluePaymentPro\Api\AcceptBlueApi;
use Devurai\AcceptbluePaymentPro\DTOs\ABChargeResponse;
use Devurai\AcceptbluePaymentPro\DTOs\AcceptBlueCaptureResponse\AcceptBlueCaptureResponse;
use Devurai\AcceptbluePaymentPro\DTOs\AcceptBlueDigitalWalletResponse\AcceptBlueDigitalWalletChargeResponse;
use Devurai\AcceptbluePaymentPro\DTOs\AcceptBlueRefundResponse\AcceptBlueRefundResponse;
use Devurai\AcceptbluePaymentPro\DTOs\AcceptBlueTransactionResponse\AcceptBlueTransactionResponse;
use Devurai\AcceptbluePaymentPro\DTOs\AcceptBlueVoidResponse\AcceptBlueVoidResponse;
use Devurai\AcceptbluePaymentPro\Order\level_2\BillingInfo;
use Devurai\AcceptbluePaymentPro\Order\level_2\ShippingInfo;
use Devurai\AcceptbluePaymentPro\Order\level_3\WCAmountDetailsFields;
use Devurai\AcceptbluePaymentPro\Order\level_3\WCLineItems;
use Devurai\AcceptbluePaymentPro\Settings\GooglePaySettings;
use Devurai\AcceptbluePaymentPro\Traits\PaymentFunctions;
use Devurai\AcceptbluePaymentPro\Traits\WoocommerceFunctions;

class WC_AcceptBlue_GooglePay_Gateway extends \WC_Payment_Gateway
{
    use PaymentFunctions;
    use WoocommerceFunctions;

    const STATUSES_WRONG = array('error', 'reserve', 'originated', 'returned', 'cancelled', 'declined', 'voided', 'approved', 'blocked');
    const STATUSES_VOID = array('queued', 'captured', 'pending');

    private \WC_Logger $logger;
    private string $merchant_id;
    private bool $is_logging;

    public bool $enabled_debug_mode;

    private AcceptBlueApi $ab_api;

    public function __construct()
    {
        global $wc_acceptblue_logger;

        $this->ab_api = AcceptBlueApi::getInstance(GooglePaySettings::getInstance());

        $this->logger = $wc_acceptblue_logger;

        $this->id = 'acceptblue-google-pay';
        $this->has_fields = true;
        $this->method_title = __('Accept.blue Google pay', 'payment-gateway-accept-blue-pro-for-woocommerce');
        $this->method_description = sprintf(__('Take payments via Google pay with Accept.blue', 'payment-gateway-accept-blue-pro-for-woocommerce'), 'https://accept.blue');

        $this->supports = array(
            'products',
//            'tokenization',
            'refunds',
//            'subscriptions',
//            'subscription_cancellation',
//            'subscription_suspension',
//            'subscription_reactivation',
//            'subscription_amount_changes',
//            'subscription_date_changes',
//            'multiple_subscriptions',
//            'subscription_payment_method_change',
//            'subscription_payment_method_change_customer',
//            'subscription_payment_method_change_admin'
        );

        $this->init_form_fields();
        $this->init_settings();
        $settings = GooglePaySettings::getInstance();

        $this->title = $settings->get_title();
        $this->description = $settings->get_description();
        $this->enabled = $settings->get_enabled();
        $this->enabled_debug_mode = $settings->is_test_mode();
        $this->merchant_id = $settings->get_merchant_ID();

        $this->is_logging = $settings->is_logging();

        add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
        add_action('wp_ajax_acceptblue_gateway_process_payment', array( $this, 'process_payment' ) );
        add_action('wp_ajax_nopriv_acceptblue_gateway_process_payment', array( $this, 'process_payment' ) );

        add_action( 'woocommerce_order_status_on-hold_to_processing', array($this, 'charge_payment') );
        add_action( 'woocommerce_order_status_on-hold_to_completed', array($this, 'charge_payment') );
        add_action( 'woocommerce_order_status_processing_to_completed', array($this, 'charge_payment') );
    }

    public function process_payment($order_id): array
    {

        $settings = GooglePaySettings::getInstance();
        $order = wc_get_order($order_id);
        if($this->is_rest_request()){
            $ab_token = json_decode($_POST['ab_token'], true);
        }else {
            $ab_token = json_decode(stripslashes($_POST['ab_token']), true);
        }

//        $billing_and_shipping_info = array();
//        if(function_exists('pgabfw_get_billing_shipping_info')){
//            $billing_and_shipping_info = pgabfw_get_billing_shipping_info($billing_and_shipping_info, $order);
//        }

        @['billing_info' => $billing_info, 'shipping_info' => $shipping_info] = $billing_and_shipping_info;

        $amount = floatval(WC()->cart->total);

        $billing_info = new BillingInfo($order);
        $shipping_info = new ShippingInfo($order);
        
        $data = array(
            "source" => "googlepay",
            'amount' => $amount,
            'token' => $ab_token,
            'capture' => $this->is_capture_transaction($settings),
            'save_card' => $this->order_contains_subscription($order),
            'transaction_details' => (object) array(
                'order_number' => $order->get_order_number(),
            )
        );

        if(!GooglePaySettings::getInstance()->is_level_3_fields_disabled()) {
            $amountDetails = new WCAmountDetailsFields($order);
            $data['amount_details'] = $amountDetails->getAllFields();
        }

//        if($billing_info !== null){
            $data['billing_info'] = $billing_info->getBillingFields();
//        }

//        if($shipping_info !== null){
            $data['shipping_info'] = $shipping_info->getShippingFields();
//        }

        if(is_array($billing_info) || is_array($shipping_info)){
            $data['customer'] = (object) array(
                'email' => $billing_info['email'] ?? $shipping_info['email'] ?? '',
            );

            $first_name = $billing_info['first_name'] ?? $shipping_info['first_name'] ?? '';
            $last_name = $billing_info['last_name'] ?? $shipping_info['last_name'] ?? '';
            $data['name'] = $first_name . ' ' . $last_name;
        }

        if(!GooglePaySettings::getInstance()->is_level_3_fields_disabled()) {
            $lineItems = new WCLineItems($order); // level 3 fields

            if ($lineItems->hasLineItems()) {
                $data['line_items'] = $lineItems->getAllFields();
            }
        }

        $data = apply_filters('pgabfw_google_pay_request_data', $data, $settings->get_settings_id(), $order);
        $result = $this->ab_api->authorize_transaction($data);
        $response = $this->ab_api->is_error($result)? $result : new AcceptBlueDigitalWalletChargeResponse($result);

        return $this->process_response($order_id, $order, $result, $response);
    }

    public function charge_payment($order_id): void
    {
        $order = wc_get_order($order_id);
        $payment_method = $this->get_payment_method_from_order($order);
        if($payment_method === $this->id){
            $my_xrefnum   = get_post_meta( $order_id, '_acceptblue_xrefnum', true );
            $charged = get_post_meta( $order_id, '_acceptblue_transaction_charged', true );

            if ( $my_xrefnum && 'no' === $charged ) {
                if(GooglePaySettings::getInstance()->is_charge_order()){
                    $data = array(
                        'reference_number' => intval($my_xrefnum)
                    );
                    $result = $this->ab_api->charge_transaction($data);

                    $response = $this->ab_api->is_error($result)? $result : new AcceptBlueCaptureResponse($result);

                    if($this->ab_api->is_error($result) || !$response->isApproved()){
                        $error_message = $this->ab_api->is_error($result)? $result->getMessage() : $response->getErrorMessage();
                        $order->add_order_note( __( 'Unable to charge transaction!', 'payment-gateway-accept-blue-pro-for-woocommerce' ) . ' ' . $error_message );
                        pgabfw_log( 'error', $this->id, 'Unable to charge transaction!' . ' #' . $order->get_order_number() . ' ' . $error_message );
                    }else{
                        $order->add_order_note( __( 'Accept.blue transaction charged', 'payment-gateway-accept-blue-pro-for-woocommerce' ) );
                        pgabfw_log( 'info', $this->id, 'Transaction charged! ' . ' #' . $order->get_order_number() );
                        update_post_meta( $order_id, '_acceptblue_transaction_charged', 'yes' );

                        // Store other data such as fees
                        update_post_meta( $order_id, 'Accept.blue Payment ID', $result['reference_number'] );
                        update_post_meta( $order_id, '_transaction_id', $result['reference_number'] );
                        $order->payment_complete( $result['reference_number'] );
                    }
                }
            }

        }
    }

    public function init_form_fields(): void
    {
        $this->form_fields = WC_AcceptBlue_GooglePay_Settings::init_gateway_settings();
    }

    public function payment_fields(): void
    {
        GooglePayButton::add_google_pay_button_container();
    }

    public function process_response(int $order_id, \WC_Order $order, $result, $response): array
    {
        /**
         * @var $response ABChargeResponse|\WP_Error
         */
        if($this->ab_api->is_error($result) || !$response->isApproved()){
            $error_message = $this->ab_api->is_error($result)? $result->getMessage() : $response->getErrorMessage();
            $this->make_order_fail($order, $error_message);
            wc_add_notice( $error_message, 'error' );
            if($this->is_rest_request()){
                header('Content-Type: application/json; charset=utf-8', true, 409);
                $error = array(
                    'message' => $error_message,
                    'status' => false
                );

                echo json_encode($error);
                die();
            }else {
                return array(
                    'processingResponse' => array(
                        'message' => $error_message,
                    ),
                    'result' => 'fail',
                    'redirect' => '',
                );
            }
        }else{
            $this->make_order_complete($order_id, $order, $response);
            WC()->cart->empty_cart();
            pgabfw_log( 'info', $this->id, 'The transaction success. #' . $order->get_order_number() . '.' );
            return array(
                'result'   => 'success',
                'redirect' => $this->get_return_url( $order ),
            );
        }
    }
    protected function make_order_fail(\WC_Order $order, $error_message): void
    {
        $order->update_status( 'wc-failed' );
        $order->add_order_note( $error_message );
        pgabfw_log( 'error', $this->id, $error_message . '.' );
    }

    protected function make_order_complete(int $order_id, \WC_Order $order, ABChargeResponse $response): void
    {
        /**
         * @var $response AcceptBlueDigitalWalletChargeResponse;
         */
        try {
            $order->set_transaction_id($response->getTransaction()->getID());

            $reference_number = $response->getReferenceNumber();

            $can_capture = $this->is_capture_transaction(GooglePaySettings::getInstance());

            update_post_meta($order_id, '_acceptblue_xrefnum', $reference_number);
            update_post_meta($order_id, '_acceptblue_transaction_charged', $can_capture ? 'yes' : 'no');

            $card_ref = $response->getCardRef();

            if(!$response->isNull($card_ref)){
                $this->save_payment_method($this->id, $response, $token);
            }

            if($can_capture) {
                update_post_meta($order_id, '_transaction_id', $reference_number, true);
                $order->payment_complete($reference_number);

                $message = sprintf(
                    __(
                        'Accept.blue transaction charged (charge RefNum: %s)',
                        'payment-gateway-accept-blue-pro-for-woocommerce'
                    ),
                    $reference_number
                );

                $order->add_order_note($message);

                pgabfw_log('info', $this->id, 'Success: ' . $message . '.');
            }else{
                if($order->has_status(array( 'pending', 'failed' ))){
                    $this->reduce_order_stock($order_id, $order);
                }

                $order->update_status(
                    'on-hold',
                    sprintf(
                        __(
                            'Accept.blue authorized (ID: %s). Process order to take payment.',
                            'payment-gateway-accept-blue-pro-for-woocommerce'
                        ),
                        $reference_number
                    )
                );
            }
        }catch(\Exception $e){
            pgabfw_log( 'error', $this->id, $e->getMessage() . '.' );
        }
    }

    public function process_refund($order_id, $amount = null, $reason = '')
    {
        $order = wc_get_order($order_id);

        if( ! $this->can_refund_order( $order ) ) {
            pgabfw_log('error', $this->id, 'Refund failed. #' . $order->get_order_number());
            return new \WP_Error( 'error', __( 'Refund failed.', 'payment-gateway-accept-blue-for-woocommerce' ) );
        }

        if( 0.0 === (float) $amount ){
            pgabfw_log('error', $this->id, 'Refund amount should be more then 0. #' . $order->get_order_number());
            return new \WP_Error( 'error', __( 'Refund amount should be more then 0.', 'payment-gateway-accept-blue-for-woocommerce' ) );
        }

        $ref_num = (int) get_post_meta( $order_id, '_acceptblue_xrefnum', true );

        if($ref_num === 0){
            pgabfw_log('error', $this->id, 'Refund reference number not found. #' . $order->get_order_number());
            return new \WP_Error('error', __( 'Reference number not found.', 'payment-gateway-accept-blue-for-woocommerce'));
        }

        $result = $this->ab_api->get_transaction($ref_num);

        if($this->ab_api->is_error($result)){
            pgabfw_log( 'error', $this->id, 'Refund failed. #' . $order->get_order_number() . '. ' . $result->getMessage() );
            return new \WP_Error( 'error', __( 'Refund failed.', 'payment-gateway-accept-blue-for-woocommerce' ) );
        }

        $response = new AcceptBlueTransactionResponse($result);

        $total = $order->get_total();
        $status = $response->getStatusDetails()->getStatus();

        if(in_array($status, self::STATUSES_WRONG)){
            pgabfw_log('error', $this->id, sprintf('Refund: Transaction has wrong status "%s".', $status) . ' #' . $order->get_order_number());
            return new \WP_Error('error', __( sprintf('Transaction has wrong status "%s".', $status), 'payment-gateway-accept-blue-for-woocommerce'));
        }

        if($amount < $total && in_array($status, self::STATUSES_VOID)){
            return new \WP_Error('error', __( 'Partial refund can be processed after the transaction status is marked as "Settled" in the AcceptBlue merchant panel.', 'payment-gateway-accept-blue-for-woocommerce'));
        }

        $data = array(
            'reference_number' => $ref_num
        );

        if($status === 'settled'){
            $result = $this->ab_api->refund_transaction($data);

            if($this->ab_api->is_error($result)){
                pgabfw_log( 'error', $this->id, 'Refund failed. #' . $order->get_order_number() . '. ' . $result->getMessage() );
                return new \WP_Error( 'error', __( 'Refund failed.', 'payment-gateway-accept-blue-for-woocommerce' ) );
            }

            $response = new AcceptBlueRefundResponse($result);

            if(!$response->isApproved()){
                pgabfw_log('error', $this->id, 'Refund: ' . $response->getErrorMessage() . ' #' . $order->get_order_number());
                return new \WP_Error('error', __( $response->getErrorMessage(), 'payment-gateway-accept-blue-for-woocommerce'));
            }else{
                return true;
            }
        }

        if(in_array($status, self::STATUSES_VOID)){
            $result = $this->ab_api->void_transactions($data);

            if($this->ab_api->is_error($result)){
                pgabfw_log( 'error', $this->id, 'Refund failed. #' . $order->get_order_number() . '. ' . $result->getMessage() );
                return new \WP_Error( 'error', __( 'Refund failed.', 'payment-gateway-accept-blue-for-woocommerce' ) );
            }

            $response = new AcceptBlueVoidResponse($result);

            if(!$response->isApproved()){
                pgabfw_log('error', $this->id, 'Refund: ' . $response->getErrorMessage() . ' #' . $order->get_order_number());
                return new \WP_Error('error', __( $response->getErrorMessage(), 'payment-gateway-accept-blue-for-woocommerce'));
            }else{
                return true;
            }
        }

        //TODO refund subscription logic

        pgabfw_log('error', $this->id, 'Refund: ' . sprintf('Transaction has wrong status "%s".', $status) . ' #' . $order->get_order_number());
        return new \WP_Error('error', __( 'Error! Something wrong.', 'payment-gateway-accept-blue-for-woocommerce'));
    }
}