<?php

namespace App\Payment\Gateway;

use App\Config\Repository\SiteConfigRepository;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class MellatGateway implements PaymentGatewayInterface
{
    private const PAYMENT_URL = 'https://bpm.shaparak.ir/pgwchannel/startpay.mellat';

    public function __construct(
        private readonly HttpClientInterface   $httpClient,
        private readonly SiteConfigRepository $configRepo,
        private readonly string $terminalId = '',
        private readonly string $username   = '',
        private readonly string $password   = '',
    ) {}

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

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

    public function initiate(int $amountRials, string $orderId, string $callbackUrl): PaymentInitResult
    {
        try {
            $response = $this->httpClient->request('POST',
                'https://bpm.shaparak.ir/pgwchannel/services/pgw?wsdl', [
                    'body' => $this->buildRequestPayload($amountRials, $orderId, $callbackUrl),
                    'headers' => ['Content-Type' => 'text/xml; charset=utf-8'],
                    'timeout' => 10,
                ]
            );

            $resCode = $this->parseResCode($response->getContent());

            if ($resCode !== '0') {
                return new PaymentInitResult(false, errorMessage: "Mellat error: $resCode");
            }

            $refId       = $this->parseRefId($response->getContent());
            $redirectUrl = self::PAYMENT_URL . '?RefId=' . $refId;

            return new PaymentInitResult(true, redirectUrl: $redirectUrl, token: $refId);
        } catch (\Throwable $e) {
            return new PaymentInitResult(false, errorMessage: $e->getMessage());
        }
    }

    public function verify(array $callbackData): PaymentVerifyResult
    {
        $refId   = $callbackData['RefId']   ?? '';
        $resCode = $callbackData['ResCode'] ?? '';

        if ($resCode === '17') {
            return new PaymentVerifyResult(false, errorMessage: 'پرداخت توسط کاربر لغو شد', canceled: true);
        }

        if ($resCode !== '0') {
            return new PaymentVerifyResult(false, errorMessage: "Payment failed: $resCode");
        }

        try {
            $response = $this->httpClient->request('POST',
                'https://bpm.shaparak.ir/pgwchannel/services/pgw?wsdl', [
                    'body' => $this->buildVerifyPayload($refId),
                    'headers' => ['Content-Type' => 'text/xml; charset=utf-8'],
                    'timeout' => 10,
                ]
            );

            $verifyCode = $this->parseResCode($response->getContent());
            if ($verifyCode !== '0') {
                return new PaymentVerifyResult(false, errorMessage: "Verify failed: $verifyCode");
            }

            return new PaymentVerifyResult(true, referenceId: $refId);
        } catch (\Throwable $e) {
            return new PaymentVerifyResult(false, errorMessage: $e->getMessage());
        }
    }

    private function buildRequestPayload(int $amount, string $orderId, string $callbackUrl): string
    {
        $terminalId = $this->cfg('mellat_terminal_id', $this->terminalId);
        $username   = $this->cfg('mellat_username', $this->username);
        $password   = $this->cfg('mellat_password', $this->password);

        return <<<XML
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:int="http://interfaces.core.sw.bsl.com/westernmellat">
  <soapenv:Body>
    <int:bpPayRequest>
      <terminalId>{$terminalId}</terminalId>
      <userName>{$username}</userName>
      <userPassword>{$password}</userPassword>
      <orderId>{$orderId}</orderId>
      <amount>{$amount}</amount>
      <localDate>{$this->date()}</localDate>
      <localTime>{$this->time()}</localTime>
      <additionalData></additionalData>
      <callBackUrl>{$callbackUrl}</callBackUrl>
      <payerId>0</payerId>
    </int:bpPayRequest>
  </soapenv:Body>
</soapenv:Envelope>
XML;
    }

    private function buildVerifyPayload(string $refId): string
    {
        $terminalId = $this->cfg('mellat_terminal_id', $this->terminalId);
        $username   = $this->cfg('mellat_username', $this->username);
        $password   = $this->cfg('mellat_password', $this->password);

        return <<<XML
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:int="http://interfaces.core.sw.bsl.com/westernmellat">
  <soapenv:Body>
    <int:bpVerifyRequest>
      <terminalId>{$terminalId}</terminalId>
      <userName>{$username}</userName>
      <userPassword>{$password}</userPassword>
      <orderId>{$refId}</orderId>
      <saleOrderId>{$refId}</saleOrderId>
      <saleReferenceId>{$refId}</saleReferenceId>
    </int:bpVerifyRequest>
  </soapenv:Body>
</soapenv:Envelope>
XML;
    }

    private function parseResCode(string $xml): string
    {
        preg_match('/<return>(.*?)<\/return>/', $xml, $m);
        $parts = explode(',', $m[1] ?? '');
        return trim($parts[0] ?? '-1');
    }

    private function parseRefId(string $xml): string
    {
        preg_match('/<return>(.*?)<\/return>/', $xml, $m);
        $parts = explode(',', $m[1] ?? '');
        return trim($parts[1] ?? '');
    }

    private function date(): string { return date('Ymd'); }
    private function time(): string { return date('His'); }
}
