<?php

namespace App\Payment\Gateway;

use App\Config\Repository\SiteConfigRepository;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class SepGateway implements PaymentGatewayInterface
{
    private const TOKEN_URL   = 'https://sep.shaparak.ir/onlinepg/onlinepg';
    private const PAYMENT_URL = 'https://sep.shaparak.ir/OnlinePG/OnlinePG';

    public function __construct(
        private readonly HttpClientInterface   $httpClient,
        private readonly SiteConfigRepository $configRepo,
        private readonly LoggerInterface      $logger,
        private readonly ?string $terminalId = '',
    ) {}

    public function getName(): string
    {
        return 'sep';
    }

    public function isConfigured(): bool
    {
        return $this->cfg('sep_terminal_id', $this->terminalId) !== '';
    }

    private function cfg(string $key, ?string $envFallback): string
    {
        return (string) ($this->configRepo->get($key) ?: $envFallback ?? '');
    }

    public function initiate(int $amountRials, string $orderId, string $callbackUrl): PaymentInitResult
    {
        try {
            $response = $this->httpClient->request('POST', self::TOKEN_URL, [
                'json' => [
                    'action'     => 'token',
                    'TerminalId' => $this->cfg('sep_terminal_id', $this->terminalId),
                    'Amount'     => $amountRials,
                    'ResNum'     => $orderId,
                    'RedirectUrl' => $callbackUrl,
                ],
                'timeout' => 10,
            ]);

            $data = $response->toArray();
            if (($data['status'] ?? -1) !== 1) {
                return new PaymentInitResult(false, errorMessage: $data['errorDesc'] ?? 'SEP error');
            }

            $token       = $data['token'];
            $redirectUrl = self::PAYMENT_URL . '?Token=' . $token . '&GetMethod=true';

            return new PaymentInitResult(true, redirectUrl: $redirectUrl, token: $token);
        } catch (\Throwable $e) {
            $this->logger->error(sprintf('Payment initiate failed (sep): %s @ %s:%d', $e->getMessage(), $e->getFile(), $e->getLine()), ['exception' => $e, 'orderId' => $orderId, 'amount' => $amountRials]);
            return new PaymentInitResult(false, errorMessage: $e->getMessage());
        }
    }

    public function verify(array $callbackData): PaymentVerifyResult
    {
        $state = $callbackData['State'] ?? '';
        if (strtolower($state) === 'canceledbyuser') {
            return new PaymentVerifyResult(false, errorMessage: 'پرداخت توسط کاربر لغو شد', canceled: true);
        }
        if (strtolower($state) !== 'ok') {
            return new PaymentVerifyResult(false, errorMessage: "Payment state: $state");
        }

        $refNum = $callbackData['RefNum'] ?? '';

        try {
            $response = $this->httpClient->request('POST', self::TOKEN_URL, [
                'json' => [
                    'action'     => 'verify',
                    'TerminalId' => $this->cfg('sep_terminal_id', $this->terminalId),
                    'RefNum'     => $refNum,
                ],
                'timeout' => 10,
            ]);

            $data = $response->toArray();
            if (($data['TransactionDetail']['AffectiveAmount'] ?? 0) <= 0) {
                return new PaymentVerifyResult(false, errorMessage: 'SEP verify failed');
            }

            return new PaymentVerifyResult(
                true,
                referenceId: $refNum,
                amountRials: (int) $data['TransactionDetail']['AffectiveAmount']
            );
        } catch (\Throwable $e) {
            $this->logger->error(sprintf('Payment verify failed (sep): %s @ %s:%d', $e->getMessage(), $e->getFile(), $e->getLine()), ['exception' => $e, 'refNum' => $refNum]);
            return new PaymentVerifyResult(false, errorMessage: $e->getMessage());
        }
    }

    public function refund(string $saleOrderId, string $saleReferenceId, int $refundAmountRials): PaymentRefundResult
    {
        return new PaymentRefundResult(false, errorMessage: 'استرداد برای این درگاه پشتیبانی نمی‌شود');
    }

    public function reverse(string $saleOrderId, string $saleReferenceId): PaymentRefundResult
    {
        return new PaymentRefundResult(false, errorMessage: 'برگشت وجه برای این درگاه پشتیبانی نمی‌شود');
    }
}
