<?php

namespace PaymentWebhooks\Core;

use PaymentWebhooks\Contract\AuthenticatorInterface;
use PaymentWebhooks\Contract\OrderIdResolverInterface;
use PaymentWebhooks\Contract\OrderUpdaterInterface;
use PaymentWebhooks\Contract\PayloadValidatorInterface;
use PaymentWebhooks\Contract\PostValidatorInterface;
use PaymentWebhooks\Contract\StatusMapperInterface;
use PaymentWebhooks\Contract\WebhookHandlerInterface;
use PaymentWebhooks\Gateway\Common\PaymentDataResolver;
use Psr\Log\LoggerInterface;
use WP_REST_Request;
use WP_REST_Response;

abstract class AbstractWebhookHandler implements WebhookHandlerInterface
{
    public function __construct(
        protected AuthenticatorInterface $authenticator,
        protected PayloadValidatorInterface $payloadValidator,
        protected PostValidatorInterface $postValidator,
        protected StatusMapperInterface $statusMapper,
        protected OrderUpdaterInterface $updater,
        protected LoggerInterface $logger,
        protected OrderIdResolverInterface $orderIdResolver,
        protected PaymentDataResolver $paymentResolver
    ) {
    }

    public function process(WP_REST_Request $request): WP_REST_Response
    {
        if (!$this->authenticator->authenticate($request)) {
            $this->logger->warning('Authentication failed');
            return new WP_REST_Response(['error' => 'Unauthorized'], 401);
        }

        $payload = $this->payloadValidator->validate($request);
        if ($payload === null) {
            $this->logger->warning('Invalid JSON');
            return new WP_REST_Response(['error' => 'Bad JSON'], 400);
        }
        $this->logger->info('Incoming webhook, payload: ', [$payload]);

        if (!$this->postValidator->verify($payload, $request)) {
            $this->logger->warning('Post-verification failed');
            return new WP_REST_Response(['error' => 'Invalid signature'], 400);
        }

        try {
            $orderId = $this->orderIdResolver->resolve($payload);

            $status = $this->statusMapper->map($payload);
            if (null === $status) {
                $this->logger->info('No update need OrderID ' . $orderId);
                return new WP_REST_Response(['result' => 'ignored'], 202);
            }

            if ($this->paymentResolver->shouldCollect($payload)) {
                $paymentData = $this->paymentResolver->collect($payload);
                $this->updater->updatePaymentData($orderId, $paymentData);
                $this->logger->info('Payment data updated', ['order_id' => $orderId]);
            }

            $this->updater->update($orderId, $status);
            $this->logger->info('Order status updated', [
                'order_id' => $orderId,
                'status' => $status,
            ]);

        } catch (\Throwable $e) {
            $this->logger->error('Error updating order', ['e' => $e->getMessage()]);
            return new WP_REST_Response(['error' => 'Server error'], 500);
        }

        return new WP_REST_Response(['result' => 'accepted'], 202);
    }
}
