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

namespace Simplyin\Payment\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Sales\Model\Order;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Model\ScopeInterface;
use Simplyin\Payment\Helper\ApiClient;
use Simplyin\Payment\Helper\Encryption;
use Simplyin\Payment\Model\ResourceModel\OrderSync\CollectionFactory as OrderSyncCollectionFactory;
use Psr\Log\LoggerInterface;

/**
 * Order status change observer
 * Syncs order status changes to SimplyIN using encrypted API.
 * Only sends update when status has actually changed.
 */
class OrderStatusChange implements ObserverInterface
{
    private const XML_PATH_API_KEY = 'payment/simplyin_payment/api_key';

    /**
     * Statuses that should not trigger sync to SimplyIN
     */
    private const STOP_STATUSES = [
        'pending',
        'pending_payment',
        'processing',
        'holded',
        'on-hold',
        '',
        'cancelled',
        'canceled',
        'refunded',
        'failed',
        'checkout-draft',
        'payment_review',
        'fraud'
    ];

    /**
     * @var ApiClient
     */
    private ApiClient $apiClient;

    /**
     * @var Encryption
     */
    private Encryption $encryption;

    /**
     * @var OrderSyncCollectionFactory
     */
    private OrderSyncCollectionFactory $orderSyncCollectionFactory;

    /**
     * @var ScopeConfigInterface
     */
    private ScopeConfigInterface $scopeConfig;

    /**
     * @var \Magento\Framework\Encryption\EncryptorInterface
     */
    private \Magento\Framework\Encryption\EncryptorInterface $encryptor;

    /**
     * @var LoggerInterface
     */
    private LoggerInterface $logger;

    /**
     * @param ApiClient $apiClient
     * @param Encryption $encryption
     * @param OrderSyncCollectionFactory $orderSyncCollectionFactory
     * @param ScopeConfigInterface $scopeConfig
     * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor
     * @param LoggerInterface $logger
     */
    public function __construct(
        ApiClient $apiClient,
        Encryption $encryption,
        OrderSyncCollectionFactory $orderSyncCollectionFactory,
        ScopeConfigInterface $scopeConfig,
        \Magento\Framework\Encryption\EncryptorInterface $encryptor,
        LoggerInterface $logger
    ) {
        $this->apiClient = $apiClient;
        $this->encryption = $encryption;
        $this->orderSyncCollectionFactory = $orderSyncCollectionFactory;
        $this->scopeConfig = $scopeConfig;
        $this->encryptor = $encryptor;
        $this->logger = $logger;
    }

    /**
     * Execute observer
     *
     * @param Observer $observer
     * @return $this
     */
    public function execute(Observer $observer): static
    {
        try {
            /** @var Order $order */
            $order = $observer->getEvent()->getOrder();

            if (!$order || !$order->getId()) {
                return $this;
            }

            // Only sync if status actually changed
            $newStatus = $order->getStatus();
            $originalStatus = $order->getOrigData('status');
            if ($newStatus === $originalStatus) {
                return $this;
            }

            if (!$this->isOrderSyncedToSimplyIn((int)$order->getId())) {
                return $this;
            }

            $email = $this->getOrderEmail($order);
            if (empty($email)) {
                return $this;
            }

            if (in_array($newStatus, self::STOP_STATUSES, true)) {
                return $this;
            }

            $trackingNumbers = $this->getTrackingNumbers($order);
            $this->sendEncryptedStatusUpdate($order, $newStatus, $trackingNumbers, $email);

        } catch (\Exception $e) {
            $this->logger->error('SimplyIN: Order status change observer error', [
                'error' => $e->getMessage(),
                'order_id' => isset($order) ? $order->getId() : null
            ]);
        }

        return $this;
    }

    /**
     * Get order email from various sources
     *
     * @param Order $order
     * @return string|null
     */
    private function getOrderEmail(Order $order): ?string
    {
        $email = $order->getCustomerEmail();

        if (empty($email)) {
            $billingAddress = $order->getBillingAddress();
            if ($billingAddress) {
                $email = $billingAddress->getEmail();
            }
        }

        if (empty($email)) {
            $shippingAddress = $order->getShippingAddress();
            if ($shippingAddress) {
                $email = $shippingAddress->getEmail();
            }
        }

        return $email ?: null;
    }

    /**
     * Check if order is synced to SimplyIN
     *
     * @param int $orderId
     * @return bool
     */
    private function isOrderSyncedToSimplyIn(int $orderId): bool
    {
        $collection = $this->orderSyncCollectionFactory->create();
        $collection->addFieldToFilter('order_id', $orderId);

        return $collection->getSize() > 0;
    }

    /**
     * Get tracking numbers from order shipments in SimplyIN format
     *
     * @param Order $order
     * @return array
     */
    private function getTrackingNumbers(Order $order): array
    {
        $trackingNumbers = [];

        $shipmentsCollection = $order->getShipmentsCollection();
        if ($shipmentsCollection) {
            foreach ($shipmentsCollection as $shipment) {
                foreach ($shipment->getAllTracks() as $track) {
                    $trackingNumbers[] = [
                        'number' => $track->getTrackNumber(),
                        'provider' => $track->getCarrierCode() ?: $track->getTitle()
                    ];
                }
            }
        }

        return $trackingNumbers;
    }

    /**
     * Get API key
     *
     * @return string|null
     */
    private function getApiKey(): ?string
    {
        $encryptedKey = $this->scopeConfig->getValue(
            self::XML_PATH_API_KEY,
            ScopeInterface::SCOPE_STORE
        );

        if (!empty($encryptedKey)) {
            return $this->encryptor->decrypt($encryptedKey);
        }

        return null;
    }

    /**
     * Send encrypted status update to SimplyIN
     *
     * @param Order $order
     * @param string $status
     * @param array $trackingNumbers
     * @param string $email
     * @return void
     */
    private function sendEncryptedStatusUpdate(
        Order $order,
        string $status,
        array $trackingNumbers,
        string $email
    ): void {
        $apiKey = $this->getApiKey();

        $payload = [
            'email' => $email,
            'shopOrderNumber' => $order->getIncrementId(),
            'newOrderStatus' => $this->mapStatus($status),
            'apiKey' => $apiKey
        ];

        if (!empty($trackingNumbers)) {
            $payload['trackings'] = $trackingNumbers;
        }

        $plaintext = json_encode($payload, JSON_UNESCAPED_SLASHES);
        $secretKey = $this->encryption->getSecretKey($email);
        $encryptedContent = $this->encryption->encrypt($plaintext, $secretKey);

        if ($encryptedContent === false) {
            $this->logger->error('SimplyIN: Failed to encrypt order status payload', [
                'order_id' => $order->getId()
            ]);
            return;
        }

        $hashedEmail = $this->encryption->hashEmail($email);
        $response = $this->apiClient->saveEncryptedOrderStatusChange(
            $encryptedContent,
            $hashedEmail,
            $plaintext,
            $email
        );

        if ($response['success']) {
            $this->logger->info('SimplyIN: Order status updated successfully', [
                'order_id' => $order->getId(),
                'status' => $status
            ]);
        } else {
            $this->logger->warning('SimplyIN: Failed to update order status', [
                'order_id' => $order->getId(),
                'error' => $response['error'] ?? 'Unknown error'
            ]);
        }
    }

    /**
     * Map Magento status to SimplyIN status
     *
     * @param string $status
     * @return string
     */
    private function mapStatus(string $status): string
    {
        $statusMap = [
            'complete' => 'Delivered',
            'closed' => 'Delivered',
            'shipped' => 'Shipped',
            'shipping' => 'Shipped',
            'in_transit' => 'InTransit',
            'canceled' => 'cancelled',
            'cancelled' => 'cancelled',
            'processing' => 'processing',
            'pending' => 'pending',
        ];

        return $statusMap[$status] ?? $status;
    }
}
