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

namespace Simplyin\Payment\Service;

use Psr\Log\LoggerInterface;

/**
 * Enriches raw order data before it is sent to the SimplyIN API.
 *
 * Extracted verbatim from CreateOrder::enrichOrderData() to keep the
 * controller within complexity limits. The logic is CreateOrder-specific
 * and intentionally NOT generalized (the sibling ProcessPayment flow has drifted).
 */
class CreateOrderDataEnricher
{
    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var PaymentDisplayFormatter
     */
    private $paymentDisplayFormatter;

    /**
     * Constructor
     * @param LoggerInterface $logger
     * @param PaymentDisplayFormatter $paymentDisplayFormatter
     */
    public function __construct(
        LoggerInterface $logger,
        PaymentDisplayFormatter $paymentDisplayFormatter
    ) {
        $this->logger = $logger;
        $this->paymentDisplayFormatter = $paymentDisplayFormatter;
    }

    /**
     * Enrich order data with required fields for the CreateOrder flow.
     *
     * Moved VERBATIM from CreateOrder::enrichOrderData(). This flow has DRIFTED
     * from the ProcessPayment flow above (different field handling, logging and
     * apartment/parcel-locker semantics), so it is kept as its own method and
     * intentionally NOT merged with enrichForProcessPayment().
     *
     * @param array $orderData
     * @param bool $isAccountCreation
     * @return array
     */
    public function enrichForCreateOrder($orderData, $isAccountCreation = false)
    {
        // shopOrderNumber should already be set from Magento order increment ID
        // If not set, log a warning
        if (!isset($orderData['shopOrderNumber']) || empty($orderData['shopOrderNumber'])) {
            $this->logger->warning('SimplyIN: shopOrderNumber not set in order data');
        }

        $orderData = $this->createOrderNormalizeTotals($orderData);
        $orderData = $this->createOrderApplyPaymentMethod($orderData);
        $orderData = $this->createOrderNormalizeItems($orderData);

        $orderData['placedDuringAccountCreation'] = $isAccountCreation;

        $orderData = $this->createOrderStripPaymentFields($orderData);
        $orderData = $this->createOrderNormalizeAddresses($orderData);
        $orderData = $this->createOrderApplyParcelLocker($orderData);

        // Clean up internal fields that shouldn't be sent to API
        unset($orderData['deliveryType']);
        unset($orderData['parcelLockerData']);

        return $orderData;
    }

    /**
     * CreateOrder flow: cast monetary totals and ensure a currency is set
     *
     * @param array $orderData
     * @return array
     */
    private function createOrderNormalizeTotals($orderData)
    {
        if (isset($orderData['price'])) {
            $orderData['price'] = (float)$orderData['price'];
        }
        if (isset($orderData['shippingPrice'])) {
            $orderData['shippingPrice'] = (float)$orderData['shippingPrice'];
        }

        if (isset($orderData['payment_method'])) {
            $orderData['paymentMethod'] = $orderData['payment_method'];
            unset($orderData['payment_method']);
        }

        if (isset($orderData['payment_method_title'])) {
            $orderData['paymentMethodName'] = $orderData['payment_method_title'];
            unset($orderData['payment_method_title']);
        }

        if (!isset($orderData['currency']) || (isset($orderData['currency']) && empty($orderData['currency']))) {
            $orderData['currency'] = 'PLN';
        }

        return $orderData;
    }

    /**
     * CreateOrder flow: compose the SimplyIN payment display pair (SIN-1726)
     *
     * @param array $orderData
     * @return array
     */
    private function createOrderApplyPaymentMethod($orderData)
    {
        // SimplyIN order-display contract (SIN-1726 T4), shared with the sync path
        // via PaymentDisplayFormatter. The payment provider is not known on the
        // live path, so payment_method is "Simply.IN"; a later reconciliation sync
        // enriches it to "Simply.IN · {Provider}". Composed from the frontend
        // method type, card last4 and method name before those fields are stripped.
        $methodId = (string) ($orderData['paymentMethod'] ?? '');
        if ($methodId === '' && !empty($orderData['blikCode'])) {
            $methodId = 'blik';
        }
        $last4 = (string) ($orderData['cardLast4'] ?? '');
        $typeLabel = (string) ($orderData['paymentMethodName'] ?? '');

        $orderData['payment_method'] = $this->paymentDisplayFormatter->composeSimplyinMethod('');
        $orderData['payment_method_title'] = $this->paymentDisplayFormatter
            ->composeSimplyinTitle($methodId, $last4, $typeLabel);

        unset($orderData['paymentMethod'], $orderData['paymentMethodName']);

        return $orderData;
    }

    /**
     * CreateOrder flow: cast item monetary/quantity fields and set currency
     *
     * @param array $orderData
     * @return array
     */
    private function createOrderNormalizeItems($orderData)
    {
        if (isset($orderData['items']) && is_array($orderData['items'])) {
            foreach ($orderData['items'] as &$item) {
                $price = isset($item['price']) ? (float)$item['price'] : 0.0;
                $item['price'] = $price;
                $item['currency'] = $orderData['currency'];

                if (!isset($item['price_net'])) {
                    $item['price_net'] = $price;
                } else {
                    $item['price_net'] = (float)$item['price_net'];
                }

                if (!isset($item['tax_amount'])) {
                    $item['tax_amount'] = 0.0;
                } else {
                    $item['tax_amount'] = (float)$item['tax_amount'];
                }

                if (!isset($item['tax_rate'])) {
                    $item['tax_rate'] = 0.0;
                } else {
                    $item['tax_rate'] = (float)$item['tax_rate'];
                }

                $item['quantity'] = isset($item['quantity']) ? (int)$item['quantity'] : 1;
            }
            unset($item);
        }

        return $orderData;
    }

    /**
     * CreateOrder flow: strip card / saved-card / blik fields before sending to API
     *
     * @param array $orderData
     * @return array
     */
    private function createOrderStripPaymentFields($orderData)
    {
        $orderData = $this->createOrderStripCardFields($orderData);
        $orderData = $this->createOrderStripSavedCardFields($orderData);

        unset($orderData['blikCode']);

        return $orderData;
    }

    /**
     * CreateOrder flow: strip new-card entry fields
     *
     * @param array $orderData
     * @return array
     */
    private function createOrderStripCardFields($orderData)
    {
        if (isset($orderData['saveCard'])) {
            unset($orderData['saveCard']);
        }
        if (isset($orderData['cardLast4'])) {
            unset($orderData['cardLast4']);
        }
        if (isset($orderData['cardToken'])) {
            unset($orderData['cardToken']);
        }
        if (isset($orderData['cardName'])) {
            unset($orderData['cardName']);
        }
        if (isset($orderData['cardProvider'])) {
            unset($orderData['cardProvider']);
        }

        return $orderData;
    }

    /**
     * CreateOrder flow: strip saved-card fields
     *
     * @param array $orderData
     * @return array
     */
    private function createOrderStripSavedCardFields($orderData)
    {
        if (isset($orderData['useSavedCard'])) {
            unset($orderData['useSavedCard']);
        }
        if (isset($orderData['savedCardId'])) {
            unset($orderData['savedCardId']);
        }
        if (isset($orderData['savedCardToken'])) {
            unset($orderData['savedCardToken']);
        }
        if (isset($orderData['savedCardCvv'])) {
            unset($orderData['savedCardCvv']);
        }
        if (isset($orderData['cardBrand'])) {
            unset($orderData['cardBrand']);
        }

        return $orderData;
    }

    /**
     * CreateOrder flow: ensure shipping/billing apartment defaults and log _id reuse
     *
     * @param array $orderData
     * @return array
     */
    private function createOrderNormalizeAddresses($orderData)
    {
        // Ensure shippingData has required fields
        if (isset($orderData['shippingData'])) {
            $orderData['shippingData'] = $this->createOrderNormalizeAddress($orderData['shippingData'], 'shipping');
        }

        // Ensure billingData has required fields
        if (isset($orderData['billingData'])) {
            $orderData['billingData'] = $this->createOrderNormalizeAddress($orderData['billingData'], 'billing');
        }

        return $orderData;
    }

    /**
     * CreateOrder flow: default the apartment number and log a preserved address _id
     *
     * @param array $address
     * @param string $label
     * @return array
     */
    private function createOrderNormalizeAddress($address, $label)
    {
        if (!isset($address['appartmentNumber']) || $address['appartmentNumber'] == '') {
            $address['appartmentNumber'] = '';
        }

        // Log if _id is present (address selected from popup)
        if (isset($address['_id']) && !empty($address['_id'])) {
            $this->logger->info('SimplyIn: Using existing ' . $label . ' address ID: ' . $address['_id']);
        }

        return $address;
    }

    /**
     * CreateOrder flow: handle parcel locker delivery (Requirements 6.1, 6.2, 6.3, 6.5, 9.4)
     *
     * @param array $orderData
     * @return array
     */
    private function createOrderApplyParcelLocker($orderData)
    {
        $deliveryType = $orderData['deliveryType'] ?? 'courier';
        $parcelLockerData = $orderData['parcelLockerData'] ?? null;

        if ($deliveryType === 'parcel_locker' && $parcelLockerData) {
            // Build parcelLockerMinimalInfo (Requirements 6.1, 6.2)
            $lockerId = $parcelLockerData['lockerId'] ?? '';
            $providerName = strtolower($parcelLockerData['providerName'] ?? $parcelLockerData['provider'] ?? '');

            if (!empty($lockerId) && !empty($providerName)) {
                $orderData['parcelLockerMinimalInfo'] = [
                    'lockerId' => (string) $lockerId,
                    'providerName' => (string) $providerName
                ];

                $this->logger->info('SimplyIn: Added parcelLockerMinimalInfo', [
                    'lockerId' => $lockerId,
                    'providerName' => $providerName
                ]);
            }

            // Add relatedParcelLockerIds if parcel locker has _id (Requirement 6.3)
            if (isset($parcelLockerData['_id']) && !empty($parcelLockerData['_id'])) {
                if (isset($orderData['billingData'])) {
                    $orderData['billingData']['relatedParcelLockerIds'] = [$parcelLockerData['_id']];
                }
                $this->logger->info('SimplyIn: Added relatedParcelLockerIds', ['_id' => $parcelLockerData['_id']]);
            }

            // Omit shippingData for parcel locker delivery (Requirement 6.5)
            if (isset($orderData['shippingData'])) {
                unset($orderData['shippingData']);
                $this->logger->info('SimplyIn: Omitted shippingData for parcel locker delivery');
            }
        }

        return $orderData;
    }
}
