<?php

namespace Sibs\Stargate\Cron;

use Sibs\Stargate\Api\CheckStatus;
use Sibs\Stargate\Config\SibsConfig;
use Sibs\Stargate\Constants\Logs;
use Sibs\Stargate\Constants\PaymentStatus;
use Sibs\Stargate\Constants\PaymentTypes;
use Sibs\Stargate\Constants\Plugin;
use Sibs\Stargate\Constants\WoocommerceOrderStatus;
use Sibs\Stargate\Core\SibsGeneralFunctions;
use Sibs\Stargate\Exceptions\SibsException;
use Sibs\Stargate\Loggers\SibsLogger;
use Sibs\Stargate\Models\SibsGeneralModels;

defined('ABSPATH') || exit;

class ProcessWebhookCalls
{
    /**
     * Process.
     */
    public static function process()
    {
        $unprocessed_webhook_calls = SibsGeneralModels::get_unprocessed_webhook_calls();

        foreach ($unprocessed_webhook_calls as $key => $unprocessed_webhook_call) {
            SibsLogger::debug('Execute '.($key + 1).'º Unprocessed', Logs::CRON);
            self::single_process($unprocessed_webhook_call);
        }
    }

    /**
     * Single Process.
     */
    public static function single_process(array $unprocessed_webhook_call)
    {
        $webhook_id = $unprocessed_webhook_call['id'];
        $processed_qty = $unprocessed_webhook_call['processed_qty'];
        $processed_at = $unprocessed_webhook_call['processed_at'];

        SibsLogger::debug("WEBHOOK id: {$webhook_id}", Logs::CRON);
        SibsLogger::debug("WEBHOOK processed_qty: {$processed_qty}", Logs::CRON);
        SibsLogger::debug("WEBHOOK processed_at: {$processed_at}", Logs::CRON);

        try {
            $decoded_payload = $unprocessed_webhook_call['decoded_payload'];

            if (is_null($decoded_payload)) {
                throw new SibsException('WEBHOOK is not decrypted', Logs::CRON);
            }

            $webhook_payload = json_decode($decoded_payload, true);

            $order_id = $webhook_payload['merchant']['transactonId'] ?? '';
            $transactionId = $webhook_payload['transactionID'] ?? '';
            $payment_method = $webhook_payload['paymentMethod'] ?? '';
            $payment_status = $webhook_payload['paymentStatus'] ?? '';
            $payment_type = $webhook_payload['paymentType'] ?? '';
            $amount_value = $webhook_payload['amount']['value'] ?? '';
            $notification_id = $webhook_payload['notificationID'] ?? '';

            SibsLogger::debug("WEBHOOK PAYLOAD order_id: {$order_id}", Logs::CRON);
            SibsLogger::debug("WEBHOOK PAYLOAD transactionId: {$transactionId}", Logs::CRON);
            SibsLogger::debug("WEBHOOK PAYLOAD payment_method: {$payment_method}", Logs::CRON);
            SibsLogger::debug("WEBHOOK PAYLOAD payment_status: {$payment_status}", Logs::CRON);
            SibsLogger::debug("WEBHOOK PAYLOAD payment_type: {$payment_type}", Logs::CRON);
            SibsLogger::debug("WEBHOOK PAYLOAD amount_value: {$amount_value}", Logs::CRON);
            SibsLogger::debug("WEBHOOK PAYLOAD notificationID: {$notification_id}", Logs::CRON);

            if (Plugin::WEBHOOK_TEST === $transactionId) {
                SibsLogger::debug('WEBHOOK is a test and have nothing to process', Logs::CRON);
                SibsGeneralModels::update_webhook_calls($webhook_id, [
                    'processed_qty' => ($processed_qty + 1),
                    'processed_at' => SibsConfig::getStoreCurrentTime(),
                ]);

                return;
            }

            $sibs_transaction = SibsGeneralModels::get_transaction_by_reference_id($transactionId);
            $wc_order = wc_get_order($order_id);

            if (empty($sibs_transaction)) {
                $sibs_transaction = SibsGeneralModels::get_transaction($wc_order);

                if (empty($sibs_transaction)) {
                    throw new SibsException("WEBHOOK Unknown Order #{$order_id} for {$transactionId}", Logs::CRON);
                }
            }

            SibsLogger::debug('WEBHOOK order_no: '.$sibs_transaction['order_no'], Logs::CRON);

            if ($payment_method != $sibs_transaction['payment_brand']) {
                throw new SibsException('Invalid payment method', Logs::CRON);
            }

            if (in_array($payment_status, PaymentStatus::pending())) {
                SibsLogger::debug("WEBHOOK have status {$payment_status} and nothing will process", Logs::CRON);
                SibsGeneralModels::update_webhook_calls($webhook_id, [
                    'processed_qty' => ($processed_qty + 1),
                    'processed_at' => SibsConfig::getStoreCurrentTime(),
                ]);

                return;
            }

            switch ($payment_type) {
                case PaymentTypes::AUTH:
                case PaymentTypes::PURS:
                    self::webhook_payment($wc_order, $webhook_payload, $sibs_transaction, $payment_status, $payment_type);

                    break;

                case PaymentTypes::CAPT:
                    self::webhook_capture($wc_order, $sibs_transaction, $payment_status, $amount_value);

                    break;

                case PaymentTypes::RFND:
                    self::webhook_refund($wc_order, $sibs_transaction, $payment_status, $amount_value);

                    break;

                default:
                    throw new SibsException('Invalid payment type', Logs::CRON);
            }

            SibsGeneralModels::update_webhook_calls($webhook_id, [
                'processed_qty' => ($processed_qty + 1),
                'processed_at' => SibsConfig::getStoreCurrentTime(),
            ]);
        } catch (SibsException $e) {
            SibsLogger::warning('WEBHOOK problem: '.$e->getExceptionMessage(), Logs::CRON);
            SibsGeneralModels::update_webhook_calls($webhook_id, [
                'processed_qty' => ($processed_qty + 1),
                'exception' => $e->getExceptionMessage(),
            ]);
        }
    }

    /**
     * Webhook Payment.
     *
     * @return bool
     */
    protected static function webhook_payment(
        \WC_Order $wc_order,
        array $webhook_payload,
        array $sibs_transaction,
        string $payment_status,
        string $payment_type
    ) {
        SibsLogger::debug('WEBHOOK will process Payment on order #'.$wc_order->get_id(), Logs::CRON);

        $order_status = SibsGeneralFunctions::convertTransactionStatus($payment_status, $payment_type);

        SibsGeneralModels::update_transaction_by_reference_id($sibs_transaction['reference_id'], [
            'payment_status' => $order_status,
        ]);

        SibsLogger::debug("WEBHOOK order_status: {$order_status}", Logs::CRON);

        if (SibsGeneralFunctions::isAlreadyPaidOrAuthorized($wc_order)) {
            SibsLogger::debug('WEBHOOK Order is Already Paid or Authorized and nothing will process', Logs::CRON);

            return true;
        }

        switch ($order_status) {
            case WoocommerceOrderStatus::PROCESSING:
            case WoocommerceOrderStatus::AUTHORIZED:
                try {
                    $wc_order->update_status($order_status);

                    CheckStatus::save_or_update_token_info($webhook_payload);
                } catch (\Throwable $t) {
                    SibsLogger::critical($t->getMessage(), Logs::CRON);
                }

                SibsLogger::debug('Reduce Stock webhook', Logs::CRON);
                wc_reduce_stock_levels($wc_order->get_id());

                break;

            case WoocommerceOrderStatus::ON_HOLD:
                try {
                    $wc_order->update_status($order_status);
                } catch (\Throwable $t) {
                    SibsLogger::critical($t->getMessage(), Logs::CRON);
                }

                wc_increase_stock_levels($wc_order->get_id());

                break;

            default:
                $errorMessage = 'WEBHOOK operation failed.';
                $wc_order->add_order_note($errorMessage);
        }

        SibsLogger::debug('WEBHOOK Payment operation with success', Logs::CRON);

        return true;
    }

    /**
     * Webhook Capture.
     *
     * @return bool
     */
    protected static function webhook_capture(
        \WC_Order $wc_order,
        array $sibs_transaction,
        string $capture_status,
        float $amount_value
    ) {
        SibsLogger::debug('WEBHOOK will process Capture on order #'.$wc_order->get_id(), Logs::CRON);

        if (SibsGeneralFunctions::isAlreadyCaptured($wc_order)) {
            SibsLogger::debug('WEBHOOK Order is Already Captured and nothing will process', Logs::CRON);

            return true;
        }

        $note_message = (WoocommerceOrderStatus::FAILED === $capture_status)
            ? __('WEBHOOK Capture operation failed', 'sibs-woo-stg')
            : sprintf(
                __('Order was Captured for %s', 'sibs-woo-stg'),
                $amount_value.' '.$wc_order->get_currency()
            );

        $orderRemainingCapture = $wc_order->get_total() - $sibs_transaction['captured_amount'];
        SibsLogger::debug('WEBHOOK order remaining Capture: '.$orderRemainingCapture, Logs::CRON);

        $order_status = ($amount_value >= $orderRemainingCapture)
            ? WoocommerceOrderStatus::PROCESSING
            : WoocommerceOrderStatus::PARTIALLY_CAPTURED;

        try {
            $wc_order->update_status($order_status);
        } catch (\Throwable $t) {
            SibsLogger::critical($t->getMessage(), Logs::CRON);
        }

        $wc_order->add_order_note($note_message);

        SibsGeneralModels::update_transaction($wc_order, [
            'payment_status' => $order_status,
            'capture_status' => SibsGeneralFunctions::convertTransactionStatusCaptRfnd($capture_status),
            'captured_amount' => $amount_value + $sibs_transaction['captured_amount'],
            'capture_information' => $note_message
                .(!empty($sibs_transaction['capture_information']) ? ', '.$sibs_transaction['capture_information'] : ''),
        ]);

        SibsLogger::debug('WEBHOOK Refund operation with success', Logs::CRON);

        return true;
    }

    /**
     * Webhook Refund.
     *
     * @return bool
     */
    protected static function webhook_refund(
        \WC_Order $wc_order,
        array $sibs_transaction,
        string $refund_status,
        float $amount_value
    ) {
        SibsLogger::debug('WEBHOOK will process Refund on order #'.$wc_order->get_id(), Logs::CRON);

        if (SibsGeneralFunctions::isAlreadyRefunded($wc_order)) {
            SibsLogger::debug('WEBHOOK Order is Refunded and nothing will process', Logs::CRON);

            return true;
        }

        $note_message = (WoocommerceOrderStatus::FAILED === $refund_status)
            ? __('WEBHOOK Refund operation failed', 'sibs-woo-stg')
            : sprintf(
                __('Order was Refunded for %s', 'sibs-woo-stg'),
                $amount_value.' '.$wc_order->get_currency()
            );

        $orderRemainingRefund = $wc_order->get_total() - $wc_order->get_total_refunded();
        SibsLogger::debug('WEBHOOK order remaining Refund: '.$orderRemainingRefund, Logs::CRON);

        $order_status = ($amount_value >= $orderRemainingRefund)
            ? WoocommerceOrderStatus::REFUNDED
            : WoocommerceOrderStatus::PARTIALLY_REFUNDED;

        try {
            $wc_order->update_status($order_status);
        } catch (\Throwable $t) {
            SibsLogger::critical($t->getMessage(), Logs::CRON);
        }

        $wc_order->add_order_note($note_message);

        SibsGeneralModels::update_transaction($wc_order, [
            'payment_status' => $order_status,
            'refund_status' => SibsGeneralFunctions::convertTransactionStatusCaptRfnd($refund_status),
            'refunded_amount' => $amount_value + $sibs_transaction['refunded_amount'],
            'refund_information' => $note_message
                .(!empty($sibs_transaction['refund_information']) ? ', '.$sibs_transaction['refund_information'] : ''),
        ]);

        SibsLogger::debug('WEBHOOK Refund operation with success', Logs::CRON);

        return true;
    }
}
