<?php

namespace PaymentWebhooks\Gateway\PayPal;

use PaymentWebhooks\Contract\PostValidatorInterface;
use PaymentWebhooks\Infrastructure\Config;
use WP_REST_Request;

final class PayPalPostValidator implements PostValidatorInterface
{
    public function verify(array $payload, WP_REST_Request $request): bool
    {
        $body = $request->get_body();
        $headers = $request->get_headers();
        return $this->verify_paypal_signature($body, $headers);
    }

    private function verify_paypal_signature(string $responseBody, array $responseHeaders): bool
    {
        $transmissionId = $responseHeaders['paypal_transmission_id'][0] ?? '';
        $timestamp = $responseHeaders['paypal_transmission_time'][0] ?? '';
        $certUrl = $responseHeaders['paypal_cert_url'][0] ?? '';
        $signature = $responseHeaders['paypal_transmission_sig'][0] ?? '';
        $webhookId = Config::get('PW_PAYPAL_WEBHOOK_ID') ?? '<from when the listener URL was subscribed>';

        $crc = hash('crc32b', $responseBody);
        $crcDecimal = hexdec($crc);

        $message = "{$transmissionId}|{$timestamp}|{$webhookId}|{$crcDecimal}";

        $certPem = $this->download_and_cache($certUrl);
        $signatureBinary = base64_decode($signature);

        $publicKey = openssl_get_publickey($certPem);
        if ($publicKey === false) {
            return false;
        }

        $result = openssl_verify($message, $signatureBinary, $publicKey, OPENSSL_ALGO_SHA256);
        return $result === 1;
    }

    private function download_and_cache(string $url, ?string $cacheKey = null): string
    {
        $uploadDir = wp_upload_dir();
        $certDir = Config::get('PW_PAYPAL_CERT_FOLDER_NAME', 'paypal_cert_cache');
        $cacheDir = $uploadDir['basedir'] . '/' . $certDir;
        if (!file_exists($cacheDir)) {
            mkdir($cacheDir, 0755, true);
        }

        if ($cacheKey === null) {
            $cacheKey = preg_replace('/\W+/', '-', $url);
        }

        $filePath = $cacheDir . '/' . $cacheKey;

        if (file_exists($filePath)) {
            return file_get_contents($filePath);
        }

        $data = file_get_contents($url);
        file_put_contents($filePath, $data);
        return $data;
    }

}
