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

namespace Simplyin\Payment\Service;

/**
 * Resolves the SimplyIN target payment-method type and selects the matching
 * payment method ID from the API list.
 *
 * Extracted VERBATIM from CreateOrder::execute() to keep the controller within
 * complexity limits. The mapping mirrors the CreateOrder flow exactly
 * (card / cc / saved_card -> add_card) and is intentionally NOT generalized;
 * the sibling ProcessPayment flow uses PaymentProcessingContext.
 */
class PaymentMethodSelector
{
    /**
     * Map the configured payment method type to the SimplyIN target type.
     *
     * Byte-identical to the CreateOrder::execute() inline mapping.
     *
     * @param mixed $paymentMethodType
     * @return mixed
     */
    public function resolveTargetType($paymentMethodType)
    {
        $targetType = $paymentMethodType;
        if ($paymentMethodType == 'card' || $paymentMethodType == 'cc' || $paymentMethodType == 'saved_card') {
            $targetType = 'add_card';
        }

        return $targetType;
    }

    /**
     * Select the first available payment method ID matching the target type.
     *
     * @param array $paymentMethods
     * @param mixed $targetType
     * @return mixed|null
     */
    public function selectMethodId($paymentMethods, $targetType)
    {
        $selectedMethodId = null;
        foreach ($paymentMethods as $method) {
            if (isset($method['type']) && $method['type'] === $targetType) {
                $selectedMethodId = $method['paymentMethodId'];
                break;
            }
        }

        return $selectedMethodId;
    }

    /**
     * Unwrap the created order from the API response envelope.
     *
     * Byte-identical to the CreateOrder success-flow unwrap.
     *
     * @param array $res
     * @return array
     */
    public function unwrapOrder($res)
    {
        if (isset($res['createdOrder'])) {
            return $res['createdOrder'];
        }
        if (isset($res['data'])) {
            return $res['data'];
        }
        return $res;
    }

    /**
     * Whether a BLIK code is required for this target type but missing.
     *
     * Byte-identical to the CreateOrder inline guard
     * (`$targetType === 'blik' && !$blikCode`).
     *
     * @param string $targetType
     * @param mixed $blikCode
     * @return bool
     */
    public function isBlikCodeMissing($targetType, $blikCode)
    {
        return $targetType === 'blik' && !$blikCode;
    }

    /**
     * Select the payment method ID honouring a saved-card selection.
     *
     * Byte-identical to the CreateOrder success-flow selection branch.
     *
     * @param array $paymentMethods
     * @param mixed $targetType
     * @param mixed $useSavedCard
     * @param mixed $savedCardId
     * @return mixed|null
     */
    public function selectMethodIdWithSavedCard($paymentMethods, $targetType, $useSavedCard, $savedCardId)
    {
        if ($useSavedCard && $savedCardId) {
            return $savedCardId;
        }

        return $this->selectMethodId($paymentMethods, $targetType);
    }

    /**
     * Build the base payment payload for the CreateOrder payment-only flow
     * (no orderId, like WooCommerce). Byte-identical to the inline construction.
     *
     * @param string $paymentId
     * @param mixed $selectedMethodId
     * @param array $orderData
     * @param mixed $magentoOrderId
     * @param mixed $amount
     * @param string $shopUserEmail
     * @param array $billingData
     * @param string $returnUrlBase
     * @param string $webhookUrl
     * @return array
     */
    public function buildPaymentOnlyPayload(
        $paymentId,
        $selectedMethodId,
        $orderData,
        $magentoOrderId,
        $amount,
        $shopUserEmail,
        $billingData,
        $returnUrlBase,
        $webhookUrl
    ) {
        $returnUrl = $returnUrlBase;
        if ($magentoOrderId) {
            $returnUrl .= '?orderId=' . $magentoOrderId;
        }

        return [
            'paymentId' => $paymentId,
            'paymentMethodId' => $selectedMethodId,
            // Note: orderId is intentionally omitted - payment works without SimplyIN order
            'title' => 'Order ' . ($orderData['shopOrderNumber'] ?? $magentoOrderId ?? ''),
            'amount' => (float)$amount,
            'currency' => $orderData['currency'] ?? 'PLN',
            'returnUrl' => $returnUrl,
            'webhookUrl' => $webhookUrl,
            'customerPaymentData' => [
                'email' => $shopUserEmail,
                'firstName' => $billingData['name'] ?? 'Guest',
                'lastName' => $billingData['surname'] ?? 'User',
                'phone' => ''
            ]
        ];
    }

    /**
     * Build the base payment payload for the CreateOrder with-order (success) flow
     *
     * Byte-identical to the inline construction.
     *
     * @param string $paymentId
     * @param mixed $selectedMethodId
     * @param mixed $orderId
     * @param array $order
     * @param mixed $amount
     * @param string $currency
     * @param mixed $magentoOrderId
     * @param string $returnUrlBase
     * @param string $webhookUrl
     * @return array
     */
    public function buildWithOrderPayload(
        $paymentId,
        $selectedMethodId,
        $orderId,
        $order,
        $amount,
        $currency,
        $magentoOrderId,
        $returnUrlBase,
        $webhookUrl
    ) {
        // Build return URL with order ID for session restoration
        $returnUrl = $returnUrlBase;
        if ($magentoOrderId) {
            $returnUrl .= '?orderId=' . $magentoOrderId;
        }

        return [
            'paymentId' => $paymentId,
            'paymentMethodId' => $selectedMethodId,
            'orderId' => $orderId,
            'title' => 'Order ' . ($order['shopOrderNumber'] ?? $orderId),
            'amount' => (float)$amount,
            'currency' => $currency,
            'returnUrl' => $returnUrl,
            'webhookUrl' => $webhookUrl,
            'customerPaymentData' => [
                'email' => $order['shopUserEmail'] ?? '',
                'firstName' => $order['billingData']['name'] ?? 'Guest',
                'lastName' => $order['billingData']['surname'] ?? 'User',
                "phone" => ""
            ]
        ];
    }

    /**
     * Build the base payment payload for the CreateOrder "Order already exists" retry flow
     *
     * Byte-identical to the inline construction.
     *
     * @param string $paymentId
     * @param mixed $selectedMethodId
     * @param array $orderData
     * @param mixed $magentoOrderId
     * @param mixed $amount
     * @param string $shopUserEmail
     * @param array $billingData
     * @param string $returnUrlBase
     * @param string $webhookUrl
     * @return array
     */
    public function buildRetryPayload(
        $paymentId,
        $selectedMethodId,
        $orderData,
        $magentoOrderId,
        $amount,
        $shopUserEmail,
        $billingData,
        $returnUrlBase,
        $webhookUrl
    ) {
        $returnUrl = $returnUrlBase;
        if ($magentoOrderId) {
            $returnUrl .= '?orderId=' . $magentoOrderId;
        }

        return [
            'paymentId' => $paymentId,
            'paymentMethodId' => $selectedMethodId,
            'title' => 'Order ' . ($orderData['shopOrderNumber'] ?? $magentoOrderId ?? 'N/A'),
            'amount' => (float)$amount,
            'currency' => $orderData['currency'] ?? 'PLN',
            'returnUrl' => $returnUrl,
            'webhookUrl' => $webhookUrl,
            'customerPaymentData' => [
                'email' => $shopUserEmail,
                'firstName' => $billingData['name'] ?? 'Guest',
                'lastName' => $billingData['surname'] ?? 'User',
                'phone' => ''
            ]
        ];
    }

    /**
     * Build paymentMethodData for the CreateOrder payment-only flow.
     *
     * Byte-identical to the CreateOrder payment-only branch. The BLIK-code
     * presence is validated by the caller; this builds the data array or null.
     *
     * @param string $targetType
     * @param mixed $blikCode
     * @param mixed $cardToken
     * @param mixed $cardLast4
     * @param mixed $cardProvider
     * @param mixed $cardName
     * @return array|null
     */
    public function buildPaymentOnlyMethodData($targetType, $blikCode, $cardToken, $cardLast4, $cardProvider, $cardName)
    {
        if ($targetType === 'blik') {
            return ['blikCode' => $blikCode];
        }

        if ($targetType === 'add_card') {
            if ($cardToken) {
                return [
                    'OTT' => $cardToken,
                    'LastDigitsOfCardNumber' => $this->normalizeCardLast4($cardLast4),
                    'cardProvider' => $cardProvider,
                    'cardName' => $cardName,
                    'saveCard' => false // Never save card when not creating account
                ];
            }
        }

        return null;
    }

    /**
     * Build paymentMethodData for the CreateOrder with-order (success) flow.
     *
     * Byte-identical to the CreateOrder success branch. The BLIK-code presence
     * is validated by the caller; this builds the data array or null.
     *
     * @param string $targetType
     * @param mixed $blikCode
     * @param mixed $cardToken
     * @param mixed $cardLast4
     * @param mixed $cardProvider
     * @param mixed $cardName
     * @param mixed $saveCard
     * @param mixed $useSavedCard
     * @param mixed $savedCardId
     * @return array|null
     */
    public function buildWithOrderMethodData(
        $targetType,
        $blikCode,
        $cardToken,
        $cardLast4,
        $cardProvider,
        $cardName,
        $saveCard,
        $useSavedCard,
        $savedCardId
    ) {
        if ($targetType === 'blik') {
            return ['blikCode' => $blikCode];
        }

        if ($targetType === 'add_card' || $targetType === 'saved_card') {
            if ($useSavedCard && $savedCardId) {
                // Saved cards use one-click payment
                return null;
            } elseif ($cardToken) {
                return [
                    'OTT' => $cardToken,
                    'LastDigitsOfCardNumber' => $this->normalizeCardLast4($cardLast4),
                    'cardProvider' => $cardProvider,
                    'cardName' => $cardName,
                    'saveCard' => (bool)$saveCard
                ];
            }
        }

        return null;
    }

    /**
     * Build paymentMethodData for the CreateOrder "Order already exists" retry flow.
     *
     * Byte-identical to the CreateOrder order-exists retry branch (BLIK is set
     * only when present; no error is raised when BLIK code is missing).
     *
     * @param string $targetType
     * @param mixed $blikCode
     * @param mixed $cardToken
     * @param mixed $cardLast4
     * @param mixed $cardProvider
     * @param mixed $cardName
     * @return array|null
     */
    public function buildRetryMethodData($targetType, $blikCode, $cardToken, $cardLast4, $cardProvider, $cardName)
    {
        if ($targetType === 'blik') {
            if ($blikCode) {
                return ['blikCode' => $blikCode];
            }
        } elseif ($targetType === 'add_card' && $cardToken) {
            return [
                'OTT' => $cardToken,
                'LastDigitsOfCardNumber' => $this->normalizeCardLast4($cardLast4),
                'cardProvider' => $cardProvider,
                'cardName' => $cardName,
                'saveCard' => false
            ];
        }

        return null;
    }

    /**
     * Normalize the card last-4 to the API-required minimum of 4 characters.
     *
     * @param mixed $cardLast4
     * @return string
     */
    private function normalizeCardLast4($cardLast4)
    {
        $last4 = (string)$cardLast4;
        if (strlen($last4) < 4) {
            $last4 = $last4 ?: '0000';
            $last4 = str_pad($last4, 4, '0', STR_PAD_LEFT);
        }
        return $last4;
    }
}
