<?php

namespace PaymentWebhooks\Core;

use PaymentWebhooks\Contract\OrderUpdaterInterface;
use PaymentWebhooks\Gateway\Common\OrderHelper;
use PaymentWebhooks\Infrastructure\Logger;
use Psr\Log\LoggerInterface;
use WC_Order;

class AsynchronousOrderUpdater implements OrderUpdaterInterface
{
    /**
     * Keeps track of which gateways already registered hooks.
     *
     * @var array<string, bool>
     */
    private static array $registered = [];
    private const ASYNC_DELAY = 5;
    private string $hookName;
    private string $paymentHookName;
    private LoggerInterface $logger;

    public function __construct(private string $gateway)
    {
        $this->hookName = 'pw_update_order_' . $this->gateway;
        $this->paymentHookName = 'pw_update_payment_' . $this->gateway;
        $this->logger = Logger::forGateway($gateway);
        if (!isset(self::$registered[$this->gateway])) {
            add_action($this->hookName, [$this, 'process'], 10, 2);
            add_action($this->paymentHookName, [$this, 'processPayment'], 10, 2);
            self::$registered[$this->gateway] = true;
        }
    }

    public function update(?int $orderId, string $newStatus): void
    {
        OrderHelper::checkOrderId($orderId);
        $this->checkScheduleFunc();

        if (!as_has_scheduled_action($this->hookName, [$orderId, $newStatus], 'pw')) {
            as_schedule_single_action(
                time() + self::ASYNC_DELAY,
                $this->hookName,
                [$orderId, $newStatus],
                'pw'
            );
        }
    }

    public function updatePaymentData(?int $orderId, array $data): void
    {
        OrderHelper::checkOrderId($orderId);
        $this->checkScheduleFunc();

        if (!as_has_scheduled_action($this->paymentHookName, [$orderId, $data], 'pw')) {
            as_schedule_single_action(
                time() + self::ASYNC_DELAY,
                $this->paymentHookName,
                [$orderId, $data],
                'pw'
            );
        }
    }

    public function process(int $orderId, string $newStatus): void
    {
        $order = $this->getOrderByIdOrNull($orderId);
        if (!$order) {
            return;
        }

        $order->update_status($newStatus);
        $this->logger->info('Async: order status updated', [
            'order_id' => $orderId,
            'status' => $newStatus,
        ]);
    }

    public function processPayment(int $orderId, array $data): void
    {
        $order = $this->getOrderByIdOrNull($orderId);
        if (!$order) {
            return;
        }

        if (isset($data['payment_method'])) {
            $order->set_payment_method($data['payment_method']);
        }
        if (isset($data['payment_method_title'])) {
            $order->set_payment_method_title($data['payment_method_title']);
        }
        if (isset($data['transaction_id'])) {
            $order->set_transaction_id($data['transaction_id']);
        }

        $order->save();
        $this->logger->info('Async: payment data updated', ['order_id' => $orderId]);
    }

    private function checkScheduleFunc(): void
    {
        if (!function_exists('as_schedule_single_action')) {
            throw new \RuntimeException('There is no function as_schedule_single_action');
        }
    }

    private function getOrderByIdOrNull(int $orderId): ?WC_Order
    {
        $order = wc_get_order($orderId);
        if (!$order instanceof WC_Order) {
            $this->logger->warning('Async: order not found', ['order_id' => $orderId]);
            return null;
        }
        return $order;
    }

}
