<?php

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

use Fisrv\Models\TransactionStatus;
use Fisrv\Models\WebhookEvent\WebhookEvent;

/**
 * Class that handles creation of webhook consumers to receive order
 * webhook event sent by Checkout Solution.
 *
 * @package  WooCommerce
 * @category Payment Gateways
 * @author   fiserv
 * @since    1.0.0
 */
final class WC_Fiserv_Webhook_Handler
{
    /**
     * Receive event from fiserv checkout solution
     *
     * @param  WP_REST_Request<array<string, mixed>> $request Event data
     * @return WP_REST_Response Reponse acknowledging sent data
     * @return WP_Error 403 Code if request has failed
     */
    public static function consume_events(WP_REST_Request $request): WP_REST_Response|WP_Error
    {
        $request_body = $request->get_body();
        $order_id = $request->get_param('wc_order_id');
        $signature = $request->get_param('_sign');

        try {
            if (!is_string($order_id)) {
                throw new Exception('Query parameter (order ID) is malformed');
            }
            if (!is_string($signature) || (base64_decode($signature) !== $order_id)) {
                throw new Exception('Signature is invalid ' . base64_encode($order_id));
            }
            $webhook_event = new WebhookEvent($request_body);
            self::update_order($order_id, $webhook_event);

            $response = new WP_REST_Response(
                [
                    'wc_order_id' => $order_id,
                    'events' => $webhook_event,
                ]
            );
            $response->set_status(200);

            return $response;
        } catch (Exception $e) {
            WC_Fiserv_Logger::generic_log('Webhook call has failed: ' . $e->getMessage());

            return new WP_Error('Webhook call has failed', $e->getMessage(), ['status' => 403]);
        }
    }

    /**
     * Register POST route at /wp-json/fiserv_woocommerce_plugin/v1/api.
     * Receive from events from fiserv checkout solution
     */
    public static function register_consume_events(): void
    {
        register_rest_route(
            WC_Fiserv_Rest_Routes::$plugin_rest_path,
            '/events',
            [
                'methods' => 'POST',
                'callback' => [self::class, 'consume_events']
            ]
        );
    }

    /**
     * Store event log data into WordPress table as order
     * meta data.
     *
     * @param  string       $order_id Identifier of corresponding order
     * @param  WebhookEvent $event    Webhook event sent from checkout solution
     * @throws Exception Order not found
     */
    private static function update_order(string $order_id, WebhookEvent $event): void
    {
        $order = wc_get_order($order_id);

        if (!$order instanceof WC_Order) {
            /* translators: %s: Order ID */
            throw new Exception(sprintf('Order with ID %s has not been found.', esc_html($order_id)));
        }

        $stored_events_list = json_decode(strval($order->get_meta('_fiserv_plugin_webhook_event')), true);
        $events_list = [];

        if (is_array($stored_events_list)) {
            $events_list = $stored_events_list;
        }

        array_unshift($events_list, $event);
        $event_list_json = wp_json_encode($events_list);

        if (is_string($event_list_json)) {
            $order->update_meta_data('_fiserv_plugin_webhook_event', $event_list_json);
        }

        $ipgTransactionStatus = $event->transactionStatus;
        $generic_gateway = new WC_Fiserv_Payment_Generic();
        $is_autocomplete = $generic_gateway->get_option('autocomplete') === 'yes';

        switch ($ipgTransactionStatus) {
            case TransactionStatus::WAITING:
                $wc_status = 'wc-on-hold';

                break;
            case TransactionStatus::PARTIAL:
                $wc_status = 'wc-on-hold';

                break;
            case TransactionStatus::APPROVED:
                if ($is_autocomplete) {
                    $wc_status = 'wc-processing';
                } else {
                    $wc_status = 'wc-completed';
                    WC_Fiserv_Logger::log($order, 'Order completed via auto-complete');
                    $order->payment_complete();
                }

                break;
            case TransactionStatus::PROCESSING_FAILED:
                $wc_status = 'wc-failed';

                break;
            case TransactionStatus::VALIDATION_FAILED:
                $wc_status = 'wc-failed';

                break;
            case TransactionStatus::DECLINED:
                $wc_status = 'wc-cancelled';

                break;
            default:
                $wc_status = 'wc-pending';

                break;
        }

        $wc_status_unprefixed = substr($wc_status, 3);

        if ($order->has_status('completed') || $order->has_status('cancelled')) {
            /* translators: %1$s: Prior status ID %2$s: Current status */
            WC_Fiserv_Logger::log($order, sprintf('Attempted to change status of order that has been processed already. Prior status: %1$s Attempted status change: %2$s', $order->get_status(), $wc_status_unprefixed));

            return;
        }

        $order->update_status($wc_status, __('Transaction status changed via webhook', 'fiserv-checkout-for-woocommerce'));
        /* translators: %s: Staus without prefix */
        $order->add_order_note(sprintf(__('Fiserv webhook has updated order to %s', 'fiserv-checkout-for-woocommerce'), $wc_status_unprefixed));
        /* translators: %1$s: Order ID %2$s: Order status */
        WC_Fiserv_Logger::log($order, sprintf('Order %s changed to status %s via webhook', $order->get_id(), $order->get_status()));

        $order->save_meta_data();
    }
}
