<?php

namespace Sibs\Stargate\Webhooks;

use Sibs\Stargate\Constants\Logs;
use Sibs\Stargate\Constants\PaymentStatus;
use Sibs\Stargate\Cron\ProcessWebhookCalls;
use Sibs\Stargate\Loggers\SibsLogger;
use Sibs\Stargate\Models\SibsGeneralModels;

defined('ABSPATH') || exit;

class SibsWebhookProcess
{
    /**
     * Execute.
     *
     * @return \WP_REST_Response
     */
    public static function execute()
    {
        // DECRYPT MESSAGE
        $webhookMerchantUrl = $_SERVER['REQUEST_URI'] ?? '';
        $webhookHeaderInitVec = $_SERVER['HTTP_X_INITIALIZATION_VECTOR'] ?? '';
        $webhookHeaderAuthTag = $_SERVER['HTTP_X_AUTHENTICATION_TAG'] ?? '';
        $webhookBody = file_get_contents('php://input') ?? '';

        try {
            $webhook_call = SibsGeneralModels::get_webhook_call_by_payload($webhookBody);

            $webhook_call_id = is_null($webhook_call)
                ? self::create_webhook_call($webhookMerchantUrl, $webhookHeaderInitVec, $webhookHeaderAuthTag, $webhookBody)
                : self::update_webhook_call($webhook_call);

            SibsLogger::debug('WEBHOOK URL: '.$webhookMerchantUrl, [
                'source' => Logs::WEBHOOK,
            ]);
            $webhook_decryptor = new SibsWebhookDecryptor($webhookBody, $webhookHeaderInitVec, $webhookHeaderAuthTag);
            $webhook_data = $webhook_decryptor->execute();
            $webhook_body = $webhook_data->getContent();

            SibsLogger::debug('WEBHOOK BODY DECODED', [
                'source' => Logs::WEBHOOK,
                'body_decoded' => $webhook_body,
            ]);

            $notificationID = $webhook_data->getNotificationId();

            $webhook_update_data = [
                'notification_id' => $notificationID,
                'decoded_payload' => $webhook_body ?? [],
            ];

            $webhook_updated = SibsGeneralModels::update_webhook_calls($webhook_call_id, $webhook_update_data);

            if (false === $webhook_updated) {
                SibsLogger::warning('WEBHOOK Partially saved on DB', [
                    'source' => Logs::WEBHOOK,
                ]);

                throw new \Exception('Error in update webhook on DB');
            }

            SibsLogger::debug('WEBHOOK Fully saved on DB', [
                'source' => Logs::WEBHOOK,
            ]);

            try {
                $webhook_call = SibsGeneralModels::get_webhook_call($webhook_call_id);

                if (null === $webhook_call) {
                    throw new \Exception('Webhook Call not found');
                }

                ProcessWebhookCalls::single_process($webhook_call);
            } catch (\Throwable $e) {
                SibsLogger::warning('Immediate webhook processing failed: '.$e->getMessage(), [
                    'source' => Logs::WEBHOOK,
                ]);

                // Do nothing.
                // Cron will retry later.
            }

            return self::webhook_success_response($notificationID);
        } catch (\Exception $e) {
            SibsLogger::error('WEBHOOK Process Error: '.$e->getMessage(), [
                'source' => Logs::WEBHOOK,
            ]);

            return self::webhook_unknown_error_response($e->getMessage());
        }
    }

    /**
     * Create Webhook Call.
     *
     * @param string $url
     * @param string $ini_vec
     * @param string $auth_tag
     * @param string $payload
     *
     * @return int
     */
    protected static function create_webhook_call($url, $ini_vec, $auth_tag, $payload)
    {
        SibsLogger::debug('WEBHOOK CREATE', [
            'source' => Logs::WEBHOOK,
        ]);

        $webhook_create_data = [
            'url' => $url,
            'headers' => [
                'HTTP_X_INITIALIZATION_VECTOR' => $ini_vec,
                'HTTP_X_AUTHENTICATION_TAG' => $auth_tag,
            ],
            'payload' => $payload,
        ];

        $webhook_call_id = SibsGeneralModels::save_webhook_calls($webhook_create_data);

        if (false === $webhook_call_id) {
            throw new \Exception('Error in save webhook on DB');
        }

        return $webhook_call_id;
    }

    /**
     * update Webhook Call.
     *
     * @param array $webhook_call
     *
     * @return int
     */
    protected static function update_webhook_call($webhook_call)
    {
        SibsLogger::debug('WEBHOOK UPDATE', [
            'source' => Logs::WEBHOOK,
        ]);

        $webhook_call_id = $webhook_call['id'];

        $webhook_update_data = [
            'arrived' => ($webhook_call['arrived'] + 1),
        ];

        $webhook_updated = SibsGeneralModels::update_webhook_calls($webhook_call_id, $webhook_update_data);

        if (false === $webhook_updated) {
            throw new \Exception('Error in update webhook on DB');
        }

        return $webhook_call_id;
    }

    /**
     * Webhook Success Response.
     *
     * @param string $notificationId
     *
     * @return \WP_REST_Response
     */
    protected static function webhook_success_response($notificationId)
    {
        $success_code = 200;

        return new \WP_REST_Response([
            'status' => $success_code,
            'response' => __(PaymentStatus::SUCCESS),
            'notificationID' => $notificationId,
        ], $success_code, [
            'Content-Type' => 'application/json; charset=utf-8',
        ]);
    }

    /**
     * Webhook Unknown Error Response.
     *
     * @param string $error_message
     * @param int    $error_code
     *
     * @return \WP_REST_Response
     */
    protected static function webhook_unknown_error_response($error_message, $error_code = 500)
    {
        return self::webhook_error_response($error_message, $error_code);
    }

    /**
     * Webhook Error Response.
     *
     * @param string $error_message
     * @param int    $error_code
     *
     * @return \WP_REST_Response
     */
    protected static function webhook_error_response($error_message, $error_code)
    {
        return new \WP_REST_Response([
            'status' => $error_code,
            'response' => __($error_message),
        ], $error_code, [
            'Content-Type' => 'application/json; charset=utf-8',
        ]);
    }
}
