<?php
/**
 * Copyright © Simplyin. All rights reserved.
 */
declare(strict_types=1);

namespace Simplyin\Payment\Model\Auth;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Request\Http as HttpRequest;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Store\Model\ScopeInterface;
use Psr\Log\LoggerInterface;

/**
 * Authenticates inbound SimplyIN order webhooks.
 *
 * Enforces HMAC signature verification over the raw body using the per-shop signing secret synced
 * from the backend. There is no IP-allowlist fallback: Magento installs are always fresh (no
 * legacy pre-HMAC shops), so a request received before the secret has synced is rejected rather
 * than allowed through. Keeps the crypto/config wiring out of the controller so the webhook
 * controller stays a thin orchestrator.
 */
class WebhookAuthenticator
{
    /**
     * Config path holding the encrypted per-shop webhook signing secret.
     */
    private const XML_PATH_WEBHOOK_SECRET = 'payment/simplyin_payment/webhook_secret';

    /**
     * @var ScopeConfigInterface
     */
    private ScopeConfigInterface $scopeConfig;

    /**
     * @var EncryptorInterface
     */
    private EncryptorInterface $encryptor;

    /**
     * @var WebhookSignatureVerifier
     */
    private WebhookSignatureVerifier $signatureVerifier;

    /**
     * @var LoggerInterface
     */
    private LoggerInterface $logger;

    /**
     * Constructor
     *
     * @param ScopeConfigInterface $scopeConfig
     * @param EncryptorInterface $encryptor
     * @param WebhookSignatureVerifier $signatureVerifier
     * @param LoggerInterface $logger
     */
    public function __construct(
        ScopeConfigInterface $scopeConfig,
        EncryptorInterface $encryptor,
        WebhookSignatureVerifier $signatureVerifier,
        LoggerInterface $logger
    ) {
        $this->scopeConfig = $scopeConfig;
        $this->encryptor = $encryptor;
        $this->signatureVerifier = $signatureVerifier;
        $this->logger = $logger;
    }

    /**
     * Whether the request is an authentic SimplyIN webhook.
     *
     * Requires a valid HMAC signature. Fails closed (rejects) when no signing secret has synced yet.
     *
     * @param RequestInterface $request
     * @return bool
     */
    public function isAuthentic(RequestInterface $request): bool
    {
        // The HMAC checks rely on HTTP-request methods (getHeader/getContent).
        // Fail closed on any other request implementation rather than fatal.
        if (!$request instanceof HttpRequest) {
            $this->logger->warning('SimplyIN Webhook: unsupported request type; failing closed');
            return false;
        }

        $secret = $this->getSigningSecret();

        if ($secret === '') {
            $this->logger->warning('SimplyIN Webhook: no signing secret synced yet; failing closed');
            return false;
        }

        $signature = (string)($request->getHeader('x-simplyin-webhook-signature') ?: '');
        $timestamp = (string)($request->getHeader('x-simplyin-webhook-timestamp') ?: '');
        $rawBody = (string)$request->getContent();

        if ($this->signatureVerifier->verify($rawBody, $timestamp, $signature, $secret)) {
            $this->logger->debug('SimplyIN Webhook: signature valid (HMAC)');
            return true;
        }

        $this->logger->warning('SimplyIN Webhook: invalid or missing HMAC signature');
        return false;
    }

    /**
     * Whether a per-shop HMAC signing secret has been synced (HMAC is enforced when true).
     *
     * @return bool
     */
    public function hasSigningSecret(): bool
    {
        return $this->getSigningSecret() !== '';
    }

    /**
     * Read and decrypt the stored signing secret.
     *
     * @return string
     */
    private function getSigningSecret(): string
    {
        $stored = (string)$this->scopeConfig->getValue(
            self::XML_PATH_WEBHOOK_SECRET,
            ScopeInterface::SCOPE_STORE
        );

        return $stored !== '' ? (string)$this->encryptor->decrypt($stored) : '';
    }
}
