<?php declare(strict_types=1);

namespace VRPaymentPayment\Core\Api\Transaction\Service;

use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Shopware\Core\{
    Checkout\Cart\CartException,
    Checkout\Cart\LineItem\LineItem,
    Checkout\Order\OrderEntity,
    Checkout\Payment\Cart\PaymentTransactionStruct,
    Framework\Context,
    Framework\DataAbstractionLayer\Search\Criteria,
    Framework\DataAbstractionLayer\Search\Filter\EqualsFilter,
    System\SalesChannel\SalesChannelContext
};
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedEvent;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use VRPayment\Sdk\Model\{
    AddressCreate,
    ChargeAttempt,
    CreationEntityState,
    CriteriaOperator,
    EntityQuery,
    EntityQueryFilter,
    EntityQueryFilterType,
    Gender,
    LineItemAttributeCreate,
    LineItemCreate,
    LineItemType,
    TokenizationMode,
    Transaction,
    TransactionCreate,
    TransactionPending,
    TransactionState,
};
use VRPaymentPayment\Core\{
    Api\OrderDeliveryState\Handler\OrderDeliveryStateHandler,
    Api\Refund\Entity\RefundEntityCollection,
    Api\Refund\Entity\RefundEntityDefinition,
    Api\Transaction\Entity\TransactionEntity,
    Api\Transaction\Entity\TransactionEntityDefinition,
    Settings\Options\Integration,
    Settings\Service\SettingsService,
    Util\LocaleCodeProvider,
    Util\Payload\CustomProducts\CustomProductsLineItemTypes,
    Util\Payload\TransactionPayload,
    Util\Analytics\Analytics
};
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
use Shopware\Core\Framework\Struct\ArrayEntity;
use Shopware\Commercial\Subscription\Framework\Struct\SubscriptionContextStruct;

/**
 * Class TransactionService
 *
 * @package VRPaymentPayment\Core\Api\Transaction\Service
 */
class TransactionService
{
    /**
     * @var \Psr\Container\ContainerInterface
     */
    protected $container;

    /**
     * @var \VRPaymentPayment\Core\Util\LocaleCodeProvider
     */
    private $localeCodeProvider;

    /**
     * @var \Psr\Log\LoggerInterface
     */
    private $logger;

    /**
     * @var \VRPaymentPayment\Core\Settings\Service\SettingsService
     */
    private $settingsService;

    const CARD_HOLDER_KEY = '1456765000789';
    const PSEUDO_CODE_KEY = '1485172176673';
    const CARD_VALIDITY_KEY = '1456765711187';
    const PAY_ID_KEY = '1484042941549';
    const ADDITIONAL_TRANSACTION_DETAILS_ORDER_ID_KEY = '1464680013786';

    /**
     * TransactionService constructor.
     *
     * @param \Psr\Container\ContainerInterface $container
     * @param \VRPaymentPayment\Core\Util\LocaleCodeProvider $localeCodeProvider
     * @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
     */
    public function __construct(
        ContainerInterface $container,
        LocaleCodeProvider $localeCodeProvider,
        SettingsService    $settingsService
    )
    {
        $this->container = $container;
        $this->localeCodeProvider = $localeCodeProvider;
        $this->settingsService = $settingsService;
    }

    /**
     * @param \Psr\Log\LoggerInterface $logger
     *
     * @internal
     * @required
     *
     */
    public function setLogger(LoggerInterface $logger): void
    {
        $this->logger = $logger;
    }

    /**
     * The pay function will be called after the customer completed the order.
     * Allows to process the order and store additional information.
     *
     * A redirect to the url will be performed
     *
     * @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction
     * @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
     *
     * @return string
     * @throws \VRPayment\Sdk\ApiException
     * @throws \VRPayment\Sdk\Http\ConnectionException
     * @throws \VRPayment\Sdk\VersioningException
     */
    public function create(
        PaymentTransactionStruct $transaction,
        SalesChannelContext      $salesChannelContext
    ): string
    {
        $criteria = new Criteria([$transaction->getOrderTransactionId()]);
        $criteria->addAssociation('order');
        $orderTransaction = $this->container->get('order_transaction.repository')->search($criteria, $salesChannelContext->getContext())->first();

        $salesChannelId = $salesChannelContext->getSalesChannel()->getId();
        $settings = $this->settingsService->getSettings($salesChannelId);
        $apiClient = $settings->getApiClient();

        $transactionId = $_SESSION['transactionId'] ?? null;
        if ($transactionId !== null) {
            $pendingTransaction = $this->read($_SESSION['transactionId'], $salesChannelId);
        }

        if ($transactionId === null || $pendingTransaction === null || $pendingTransaction->getState() !== TransactionState::PENDING) {
            unset($_SESSION['transactionId']);
            $pendingTransactionId = $this->createPendingTransaction($salesChannelContext);
            $pendingTransaction = $this->read($pendingTransactionId, $salesChannelId);
        }

        $transactionPayloadClass = (new TransactionPayload(
            $this->container,
            $this->localeCodeProvider,
            $salesChannelContext,
            $settings,
            $transaction
        ));
        $transactionPayloadClass->setLogger($this->logger);
        $transactionPayload = $transactionPayloadClass->get($pendingTransaction->getVersion());

        $createdTransaction = $apiClient->getTransactionService()
            ->confirm($settings->getSpaceId(), $transactionPayload);

        $this->addVRPaymentTransactionId(
            $transaction,
            $salesChannelContext->getContext(),
            $createdTransaction->getId(),
            $settings->getSpaceId()
        );

        $redirectUrl = $this->container->get('router')->generate(
            'frontend.vrpayment.checkout.pay',
            ['orderId' => $orderTransaction->getOrder()->getId(),],
            UrlGeneratorInterface::ABSOLUTE_URL
        );

        if ($settings->getIntegration() == Integration::PAYMENT_PAGE) {
            $redirectUrl = $apiClient->getTransactionPaymentPageService()
                ->paymentPageUrl($settings->getSpaceId(), $createdTransaction->getId());
        }

        $this->upsert(
            $createdTransaction,
            $salesChannelContext->getContext(),
            $orderTransaction->getPaymentMethodId(),
            $orderTransaction->getOrder()->getSalesChannelId()
        );

        $salesChannelContext->getContext()->addExtension(
            'checkoutState',
            new ArrayEntity([
                'transactionId' => null,
                'addressHash'   => null,
                'currency'      => null,
            ])
        );

        $salesChannelContext->getContext()->addExtension(
            'possibleMethods',
            new ArrayEntity(['ids' => []])
        );


        $this->holdDelivery($orderTransaction->getOrder()->getId(), $salesChannelContext->getContext());

        return $redirectUrl;
    }

    /**
     * Creates the transaction in the portal using the SDK.
     *
     * @return void
     */
    public function createRecurringTransaction(TransactionCreate $sdkTransactionCreate, string $spaceId = ""): Transaction {
        $settings = $this->settingsService->getSettings();
        if (empty($spaceId)) {
            $spaceId = $settings->getSpaceId();
        }

        $apiClient = $settings->getApiClient();
        Analytics::addHeaders($apiClient, [
            Analytics::SUBSCRIPTION_TRANSACTION => true
        ]);

        $sdkTransaction = $apiClient->getTransactionService()->create($spaceId, $sdkTransactionCreate);
        if ($sdkTransaction->valid()) {
            return $apiClient->getTransactionService()->processWithoutUserInteraction($spaceId, $sdkTransaction->getId());
        }

        throw new \Exception("The transacion is not valid and could not be created.");
    }

    /**
     * @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction
     * @param \Shopware\Core\Framework\Context $context
     * @param int $vrpaymentTransactionId
     * @param int $spaceId
     */
    protected function addVRPaymentTransactionId(
        PaymentTransactionStruct $transaction,
        Context                       $context,
        int                           $vrpaymentTransactionId,
        int                           $spaceId
    ): void
    {
        $data = [
            'id' => $transaction->getOrderTransactionId(),
            'customFields' => [
                TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TRANSACTION_ID => $vrpaymentTransactionId,
                TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_SPACE_ID => $spaceId,
            ],
        ];
        $this->container->get('order_transaction.repository')->update([$data], $context);
    }

    /**
     * Persist VRPayment transaction
     *
     * @param \VRPayment\Sdk\Model\Transaction $transaction
     * @param \Shopware\Core\Framework\Context $context
     * @param string|null $paymentMethodId
     * @param string|null $salesChannelId
     */
    public function upsert(
        Transaction $transaction,
        Context     $context,
        string      $paymentMethodId = null,
        string      $salesChannelId = null
    ): void
    {
        try {

            $transactionId = $transaction->getId();
            $transactionMetaData = $transaction->getMetaData();

            if (!$salesChannelId) {
                $salesChannelId = $transactionMetaData['salesChannelId'] ?? '';
            }

            $orderId = $transactionMetaData[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
            $orderTransactionId = $transactionMetaData[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];

            $dataParamValue = json_decode(strval($transaction), true);
            $brandName = '';
            if (isset($dataParamValue['paymentConnectorConfiguration'])) {
                $brandName = $dataParamValue['paymentConnectorConfiguration']
                    ? $dataParamValue['paymentConnectorConfiguration']['name']
                    : '';
            }
            $dataParamValue['brandName'] = $brandName;

            $paymentMethodName = '';
            if (isset($dataParamValue['paymentConnectorConfiguration'])) {
                $paymentMethodName = $dataParamValue['paymentConnectorConfiguration']
                    ? $dataParamValue['paymentConnectorConfiguration']['paymentMethodConfiguration']['name']
                    : '';
            }
            $dataParamValue['paymentMethodName'] = $paymentMethodName;

            $chargeAttempt = $this->getChargeAttempt($salesChannelId, $transactionId);

            $erpMerchantId = null;
            if ($chargeAttempt) {
                $creditCardHolder = $this->getChargeAttemptAdditionalData($chargeAttempt, self::CARD_HOLDER_KEY);
                $dataParamValue['creditCardHolder'] = $creditCardHolder ? $creditCardHolder[0] : '';

                $pseudoCardNumber = $this->getChargeAttemptAdditionalData($chargeAttempt, self::PSEUDO_CODE_KEY);
                $dataParamValue['pseudoCardNumber'] = $pseudoCardNumber ? $pseudoCardNumber[0] : '';

                $payId = $this->getChargeAttemptAdditionalData($chargeAttempt, self::PAY_ID_KEY);
                $dataParamValue['payId'] = $payId ? $payId[0] : '';

                $dataParamValue['customerName'] = isset($transactionMetaData[TransactionPayload::VRPAYMENT_METADATA_CUSTOMER_NAME])
                    ? $transactionMetaData[TransactionPayload::VRPAYMENT_METADATA_CUSTOMER_NAME]
                    : '';

                $creditCardValidity = $this->getChargeAttemptAdditionalData($chargeAttempt, self::CARD_VALIDITY_KEY);

                if (isset($creditCardValidity['cardExpireMonth']) && isset($creditCardValidity['cardExpireYear'])) {
                    $creditCardExpireMonth = $creditCardValidity['cardExpireMonth'] ?? null;
                    if (!empty($creditCardExpireMonth)) {
                        $dataParamValue['cardExpireMonth'] = sprintf("%02d", $creditCardExpireMonth);
                    }
                    $creditCardExpireYear = $creditCardValidity['cardExpireYear'] ?? null;
                    if (!empty($creditCardExpireYear)) {
                        $dataParamValue['cardExpireYear'] = $creditCardExpireYear;
                    }
                }

                $erpMerchantId = $this->getChargeAttemptAdditionalData($chargeAttempt, self::ADDITIONAL_TRANSACTION_DETAILS_ORDER_ID_KEY);
                $erpMerchantId = $erpMerchantId ? $erpMerchantId[0] : null;
            }

            $data = [
                'id' => $orderId,
                'erpMerchantId' => $erpMerchantId,
                'data' => $dataParamValue,
                'paymentMethodId' => $paymentMethodId,
                'orderId' => $orderId,
                'orderTransactionId' => $orderTransactionId,
                'spaceId' => $transaction->getLinkedSpaceId(),
                'state' => $transaction->getState(),
                'salesChannelId' => $salesChannelId,
                'transactionId' => $transaction->getId(),
            ];

            $data = array_filter($data);
            $this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')->upsert([$data], $context);

        } catch (\Exception $exception) {
            $this->logger->critical(__CLASS__ . ' : ' . __FUNCTION__ . ' : ' . $exception->getMessage());
        }
    }

    /**
     * Hold delivery
     *
     * @param string $orderId
     * @param \Shopware\Core\Framework\Context $context
     */
    private function holdDelivery(string $orderId, Context $context)
    {
        try {
            /**
             * @var OrderDeliveryStateHandler $orderDeliveryStateHandler
             */
            $orderEntity = $this->getOrderEntity($orderId, $context);
            $orderDeliveryStateHandler = $this->container->get(OrderDeliveryStateHandler::class);
            if (null !== $orderEntity->getDeliveries()->last()) {
                $orderDeliveryStateHandler->hold($orderEntity->getDeliveries()->last()->getId(), $context);
            }
        } catch (\Exception $exception) {
            $this->logger->critical($exception->getTraceAsString());
        }
    }

    /**
     * Get order
     *
     * @param String $orderId
     * @param \Shopware\Core\Framework\Context $context
     *
     * @return \Shopware\Core\Checkout\Order\OrderEntity
     */
    protected function getOrderEntity(string $orderId, Context $context): OrderEntity
    {
        try {
            $criteria = (new Criteria([$orderId]))->addAssociations(['deliveries']);
            $order = $this->container->get('order.repository')->search(
                $criteria,
                $context
            )->first();
            if (is_null($order)) {
                throw CartException::orderNotFound($orderId);
            }
            return $order;
        } catch (\Exception $e) {
            throw CartException::orderNotFound($orderId);
        }

    }

    /**
     * Get transaction entity by orderId
     *
     * @param string $orderId
     * @param \Shopware\Core\Framework\Context $context
     *
     * @return \VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntity
     */
    public function getByOrderId(string $orderId, Context $context): TransactionEntity
    {
        return $this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')
            ->search(new Criteria([$orderId]), $context)
            ->get($orderId);
    }

    /**
     * Read transaction from VRPayment API
     *
     * @param int $transactionId
     * @param string $salesChannelId
     *
     * @return \VRPayment\Sdk\Model\Transaction
     * @throws \VRPayment\Sdk\ApiException
     * @throws \VRPayment\Sdk\Http\ConnectionException
     * @throws \VRPayment\Sdk\VersioningException
     */
    public function read(int $transactionId, string $salesChannelId = ""): Transaction
    {
        $settings = $this->settingsService->getSettings($salesChannelId);
        return $settings->getApiClient()->getTransactionService()->read($settings->getSpaceId(), $transactionId);
    }

    /**
     * Get transaction entity by VRPayment transaction id
     *
     * @param int $transactionId
     * @param \Shopware\Core\Framework\Context $context
     *
     * @return \VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntity|null
     */
    public function getByTransactionId(int $transactionId, Context $context): ?TransactionEntity
    {
        return $this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')
            ->search(
                (new Criteria())->addFilter(new EqualsFilter('transactionId', $transactionId))
                    ->addAssociations(['refunds']), $context
            )
            ->first();
    }

    /**
     * Get transaction entity by VRPayment order transaction id
     *
     * @param string $transactionId
     * @param \Shopware\Core\Framework\Context $context
     *
     * @return \VRPaymentPayment\Core\Api\Transaction\Entity\TransactionEntity|null
     */
    public function getByOrderTransactionId(string $orderTransactionId, Context $context): ?TransactionEntity
    {
        return $this->container->get(TransactionEntityDefinition::ENTITY_NAME . '.repository')
            ->search(
                (new Criteria())->addFilter(new EqualsFilter('orderTransactionId', $orderTransactionId))
                    ->addAssociations(['refunds']), $context
            )
            ->first();
    }

    /**
     * Get transaction entity by VRPayment transaction id
     *
     * @param int $transactionId
     * @param \Shopware\Core\Framework\Context $context
     *
     * @return \VRPaymentPayment\Core\Api\Refund\Entity\RefundEntityCollection
     */
    public function getRefundEntityCollectionByTransactionId(int $transactionId, Context $context): ?RefundEntityCollection
    {
        return $this->container->get(RefundEntityDefinition::ENTITY_NAME . '.repository')
            ->search(
                (new Criteria())->addFilter(new EqualsFilter('transactionId', $transactionId)), $context
            )
            ->getEntities();
    }

    /**
     * @param string $orderId
     * @param float $invoicePaidAmount
     * @param Context $context
     * @return void
     */
    public function updateOrderTotalPriceByInvoiceTotal(string $orderId, float $invoicePaidAmount, Context $context): void
    {
        $price = $this->getOrderEntity($orderId, $context)->getPrice();

        if ($price->getTotalPrice() === $invoicePaidAmount) {
            return;
        }

        $data = [
            'id' => $orderId,
            'price' => [
                'netPrice' => $price->getNetPrice(),
                'rawTotal' => $price->getRawTotal(),
                'taxRules' => $price->getTaxRules(),
                'taxStatus' => $price->getTaxStatus(),
                'totalPrice' => $invoicePaidAmount,
                'positionPrice' => $price->getPositionPrice(),
                'calculatedTaxes' => $price->getCalculatedTaxes()
            ],
        ];

        $this->container->get('order.repository')->update([$data], $context);
    }

    /**
     * @param SalesChannelContext $salesChannelContext
     *
     * @return int
     */
    public function createPendingTransaction(SalesChannelContext $salesChannelContext, $event = null): int
    {
        $expiredTransaction = true;
        $transactionId = $_SESSION['transactionId'] ?? null;
        $settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
        if (!$settings) {
            throw new \Exception('Space settings not configured');
        }

        if ($transactionId) {
            $transactionService = $settings->getApiClient()->getTransactionService();
            $pendingTransaction = $transactionService->read($settings->getSpaceId(), $transactionId);
            if ($pendingTransaction->getState() === TransactionState::PENDING) {
                $expiredTransaction = false;
            }
        }

        if (!$transactionId || $expiredTransaction) {
            $settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());

            $customer = $salesChannelContext->getCustomer();
            $lineItems = [];
            if ($event) {
                if ($event instanceof CheckoutConfirmPageLoadedEvent) {
                    $cartLineItems = $event->getPage()->getCart()->getLineItems()->getElements();
                    foreach ($cartLineItems as $cartLineItem) {
                        if ($cartLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) {
                            continue;
                        }
                        $lineItems[] = $this->createTempLineItem($cartLineItem);
                    }
                } elseif ($event instanceof AccountEditOrderPageLoadedEvent) {
                    $order = $event->getPage()->getOrder();
                    foreach ($order->getLineItems() as $orderLineItem) {
                        $lineItems[] = $this->createTempLineItem($orderLineItem);
                    }
                }
            }

            $customerId = "";
            if ($customer->getGuest() === false) {
                $customerId = $customer->getCustomerNumber();
            }

            $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://';
            $homeUrl = $protocol . $_SERVER['HTTP_HOST'];
            $currency = $salesChannelContext->getCurrency()->getIsoCode();

            $billingAddress = $this->buildAddress($salesChannelContext, $customer->getActiveBillingAddress());
            $shippingAddress = $this->buildAddress($salesChannelContext, $customer->getActiveShippingAddress());

            if (!$settings) {
                throw new \Exception('Space settings not configured');
            }

            $transactionPayload = (new TransactionCreate())
                ->setBillingAddress($billingAddress)
                ->setShippingAddress($shippingAddress)
                ->setLineItems($lineItems)
                ->setCurrency($currency)
                ->setSpaceViewId($settings->getSpaceViewId())
                ->setAutoConfirmationEnabled(false)
                ->setChargeRetryEnabled(false)
                ->setCustomerEmailAddress($customer->getEmail())
                ->setCustomerId($customerId)
                ->setSuccessUrl($homeUrl . '?success')
                ->setFailedUrl($homeUrl . '?fail');

            if($this->isSubscription($salesChannelContext)) {
                $transactionPayload->setTokenizationMode(TokenizationMode::FORCE_CREATION);
            }

            $transactionService = $settings->getApiClient()->getTransactionService();
            $transaction = $transactionService->create($settings->getSpaceId(), $transactionPayload);
            $transactionId = $transaction->getId();
            $_SESSION['transactionId'] = $transactionId;
        }

        return $transactionId;
    }

    /**
     * @param SalesChannelContext $salesChannelContext
     * @param int $transactionId
     * @return void
     */
    public function updateTempTransaction(SalesChannelContext $salesChannelContext, int $transactionId): void
    {
        $pendingTransaction = new TransactionPending();
        $pendingTransaction->setId($transactionId);

        $settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
        $transaction = $settings->getApiClient()->getTransactionService()->read($settings->getSpaceId(), $transactionId);
        $pendingTransaction->setVersion($transaction->getVersion());

        $currency = $salesChannelContext->getCurrency()->getIsoCode();

        $pendingTransaction->setCurrency($currency);
        $billingAddress = $this->buildAddress($salesChannelContext, $salesChannelContext->getCustomer()->getActiveBillingAddress());
        $shippingAddress = $this->buildAddress($salesChannelContext, $salesChannelContext->getCustomer()->getActiveShippingAddress());

        $pendingTransaction->setBillingAddress($billingAddress);
        $pendingTransaction->setShippingAddress($shippingAddress);

        $settings->getApiClient()->getTransactionService()
            ->update($settings->getSpaceId(), $pendingTransaction);
    }

    /**
     * @param ChargeAttempt|null $chargeAttempt
     * @param string $descriptorKey
     * @return array
     */
    private function getChargeAttemptAdditionalData(?ChargeAttempt $chargeAttempt, string $descriptorKey): array
    {
        if (!$chargeAttempt) {
            return [];
        }

        $labels = $chargeAttempt->getLabels() ?? [];

        if (empty($labels)) {
            return [];
        }

        foreach ($labels as $label) {
            $descriptor = $label->getDescriptor();
            if ((string)$descriptor->getId() !== $descriptorKey) {
                continue;
            }

            switch ($descriptorKey) {
                case self::CARD_HOLDER_KEY:
                    return [$label->getContentAsString()];

                case self::PSEUDO_CODE_KEY:
                    return [$label->getContentAsString()];

                case self::PAY_ID_KEY:
                    return [$label->getContentAsString()];

                case self::ADDITIONAL_TRANSACTION_DETAILS_ORDER_ID_KEY:
                    return [$label->getContentAsString()];

                case self::CARD_VALIDITY_KEY:
                    $validityYear = '';
                    $validityMonth = '';
                    foreach ($label->getContent() as $cardValidityItem) {
                        if (strlen((string)$cardValidityItem) === 1 || strlen((string)$cardValidityItem) === 2) {
                            $validityMonth = $cardValidityItem;
                        } elseif (strlen((string)$cardValidityItem) === 4) {
                            $validityYear = $cardValidityItem;
                        }
                    }

                    if (empty($validityYear) || empty($validityMonth)) {
                        return [];
                    }

                    return [
                        'cardExpireMonth' => $validityMonth,
                        'cardExpireYear' => $validityYear,
                    ];
            }
        }

        return [];
    }

    /**
     * @param string $salesChannelId
     * @param int $transactionId
     * @return ChargeAttempt|null
     */
    private function getChargeAttempt(string $salesChannelId, int $transactionId): ?ChargeAttempt
    {
        /** @noinspection PhpParamsInspection */
        $entityQueryFilter = (new EntityQueryFilter())
            ->setType(EntityQueryFilterType::LEAF)
            ->setOperator(CriteriaOperator::EQUALS)
            ->setFieldName('charge.transaction')
            ->setValue($transactionId);

        $query = (new EntityQuery())->setFilter($entityQueryFilter);

        $settings = $this->settingsService->getSettings($salesChannelId);

        $chargeAttempts = $settings->getApiClient()->getChargeAttemptService()->search($settings->getSpaceId(), $query);

        return $chargeAttempts ? $chargeAttempts[0] : null;
    }

	private function createTempLineItem($productData): LineItemCreate
	{
		$lineItem = new LineItemCreate();

		if ($productData instanceof LineItem) {
			$lineItem->setName($productData->getLabel());
			$lineItem->setUniqueId($productData->getId());
			$lineItem->setSku($productData->getReferencedId() ?? $productData->getId());
			$lineItem->setQuantity($productData->getQuantity());
			$lineItem->setAmountIncludingTax($productData->getPrice()->getUnitPrice());
		} elseif ($productData instanceof OrderLineItemEntity) {
			$lineItem->setName($productData->getLabel());
			$lineItem->setUniqueId($productData->getId());
			$lineItem->setSku($productData->getProductId() ?? $productData->getIdentifier() ?? $productData->getId());
			$lineItem->setQuantity($productData->getQuantity());
			$lineItem->setAmountIncludingTax($productData->getUnitPrice());
		} else {
			throw new \InvalidArgumentException('Unsupported line item type: ' . get_class($productData));
		}

		$lineItem->setType(LineItemType::PRODUCT);

		return $lineItem;
	}

    /**
     * Build a VRPayment address from Shopware customer address.
     *
     * @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
     * @param \Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity $addressEntity
     * @return \VRPayment\Sdk\Model\AddressCreate
     */
    private function buildAddress(
        SalesChannelContext $salesChannelContext,
        \Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity $addressEntity
    ): AddressCreate {
        $customer = $salesChannelContext->getCustomer();

        $address = new AddressCreate();

        $address->setFamilyName($addressEntity->getLastName() ?: $customer->getLastName() ?: '');
        $address->setGivenName($addressEntity->getFirstName() ?: $customer->getFirstName() ?: '');
        $address->setOrganizationName($addressEntity->getCompany());
        $address->setPhoneNumber($addressEntity->getPhoneNumber());
        $address->setCountry($addressEntity->getCountry()->getIso());

        $postalState = $addressEntity?->getCountryState()?->getName()
            ?: $addressEntity?->getCountryState()?->getShortCode()
            ?: '';
        $address->setPostalState($postalState);

        $address->setPostCode($addressEntity->getZipcode());
        $address->setStreet($addressEntity->getStreet());
        $address->setEmailAddress($customer->getEmail());

        if (!empty($customer->getBirthday())) {
            $birthday = (new \DateTimeImmutable())
                ->setTimestamp($customer->getBirthday()->getTimestamp())
                ->format('Y-m-d');
            $address->setDateOfBirth($birthday);
        }

        $salutationEntity = $addressEntity->getSalutation() ?: $customer->getSalutation();
        $address->setSalutation($salutationEntity?->getDisplayName() ?? '');
        $address->setGender(
            strtolower($salutationEntity?->getSalutationKey() ?? '') === 'mr'
            ? Gender::MALE
            : Gender::FEMALE
        );

        return $address;
    }

    /**
     * Checks if it's subscription context.
     *
     * @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
     * @return bool
     */
    private function isSubscription(SalesChannelContext $salesChannelContext): bool {
        $extensionName = 'subscription';
        if (class_exists(\Shopware\Commercial\Subscription\Framework\Struct\SubscriptionContextStruct::class)) {
            $extensionName = SubscriptionContextStruct::SUBSCRIPTION_EXTENSION;
        }
        if ($salesChannelContext->hasExtension($extensionName)) {
            return true;
        }
        return false;
    }
}
