<?php

declare(strict_types=1);

namespace External\HannahBundle\SAP\Synchronizer;

use External\HannahBundle\SAP\Exception\SAPException;
use External\HannahBundle\SAP\MappingType;
use External\HannahBundle\SAP\Util\SAPApi;
use External\HannahBundle\SAP\Util\SAPClient;
use KupShop\AdminBundle\Util\ActivityLog;
use KupShop\KupShopBundle\Util\Database\QueryHint;
use KupShop\KupShopBundle\Util\Functional\Mapping;
use KupShop\KupShopBundle\Util\StringUtil;
use KupShop\OrderingBundle\Util\Order\OrderInfo;
use KupShop\ReturnsBundle\Entity\ReturnEntity;
use KupShop\ReturnsBundle\Util\ReturnsUtil;
use Query\Operator;
use Symfony\Component\Routing\Router;
use Symfony\Contracts\Service\Attribute\Required;

class ReturnSynchronizer extends BaseSynchronizer
{
    protected static $type = 'return';

    #[Required]
    public SAPApi $sapApi;
    #[Required]
    public SAPClient $sapClient;

    public ?ReturnsUtil $returnsUtil = null;

    #[Required]
    final public function setReturnsUtil(?ReturnsUtil $returnsUtil = null): void
    {
        $this->returnsUtil = $returnsUtil;
    }

    public function processToSAP(): void
    {
        if (!findModule(\Modules::RETURNS)) {
            return;
        }

        $this->processReturnsToSAP();
    }

    public function processReturn(string $sapId, array $data): void
    {
        if (!findModule(\Modules::RETURNS)) {
            $this->handleReturnWithoutReturnsModule($data);

            return;
        }

        if (!$this->returnsUtil) {
            return;
        }

        if (!($returnId = $this->sapUtil->getMapping(MappingType::RETURNS, $sapId))) {
            if (!($returnId = $this->createReturnFromSAP($sapId, $data))) {
                return;
            }
        }

        /** @var ReturnEntity $return */
        if (!($return = QueryHint::withRouteToMaster(fn () => $this->returnsUtil->getReturn($returnId)))) {
            return;
        }

        // dobropis
        $this->saveOrderCreditNote($data, $return, null);

        // zmena stavu vratky
        $newStatusId = match ($data['OrderState']) {
            'RETOPEN' => 1,
            'RETFIN' => 2,
            default => null,
        };

        if ($newStatusId !== null && $newStatusId != $return->getStatus()) {
            $this->returnsUtil->logHistory($returnId, '[SAP] Změna stavu vratky: '.$data['OrderState'], status: $newStatusId);
            $this->returnsUtil->changeStatus($returnId, $newStatusId);
        }
    }

    protected function processReturnsToSAP(): void
    {
        if (isLocalDevelopment()) {
            return;
        }

        $qb = sqlQueryBuilder()
            ->select('r.id')
            ->from('returns', 'r')
            ->leftJoin('r', 'sap_returns', 'sr', 'sr.id_return = r.id')
            ->andWhere('sr.id_sap IS NULL')
            // do SAPu zapisujeme vratky ve stavu "Zbozi prijato"
            ->andWhere(Operator::inIntArray([1], 'r.status'))
            // ignorujeme vratky s priznakem "NOSAP"
            ->andWhere(Operator::not(
                Operator::findInSet(['NOSAP'], 'r.flags')
            ));

        foreach ($qb->execute() as $item) {
            $return = $this->returnsUtil->getReturn($item['id']);
            $order = $this->returnsUtil->getReturnOrder($return->getId());
            if (!$order->order_no) {
                $order = null;
            }

            try {
                if ($sapReturnId = $this->sapApi->createReturn($return, $order, true)) {
                    $this->returnsUtil->logHistory(
                        $return->getId(),
                        sprintf('[SAP] Vratka byla zapsána do SAPu; SAPID: %s', $sapReturnId),
                        status: 1,
                    );
                }
            } catch (SAPException $e) {
                if (isLocalDevelopment()) {
                    throw $e;
                }

                $this->activityLog->addActivityLog(
                    ActivityLog::SEVERITY_ERROR,
                    ActivityLog::TYPE_SYNC,
                    sprintf('Vratku "%s" se nepodařilo zapsat do SAP, Chyba: %s', $return->getCode(), $e->getMessage()),
                    [
                        'message' => $e->getMessage(),
                    ]
                );
            }
        }
    }

    protected function getHandledFields(): array
    {
        return [];
    }

    private function createReturnFromSAP(string $sapId, array $data): ?int
    {
        try {
            $order = \Order::createFromDbOrderNo($data['OrderId']);
        } catch (\InvalidArgumentException) {
            return null;
        }

        $sapOrderData = $this->sapClient->orderInfo($data['OrderId'], $data['Process'] ?? null);

        if (empty($sapOrderData->Returns)) {
            return null;
        }

        $returnsData = [];
        foreach ($this->sapUtil->prepareSAPItems($sapOrderData->Returns) as $item) {
            $returnsData[$item->ReturnNo][] = $item;
        }

        if (empty($returnsData[$sapId])) {
            return null;
        }

        $orderItems = Mapping::mapKeys($order->getItems(), fn ($k, $v) => [$v->getCode(), $v->getId()]);

        $returnItems = [];
        foreach ($returnsData[$sapId] as $item) {
            if (!($orderItemId = $orderItems[$item->ItemId] ?? null)) {
                continue;
            }

            $returnItems[$orderItemId] = [
                'pieces' => $item->ItemQuantity ?? 1,
                'return_reason' => $this->getReasonBySAPItem($item),
            ];
        }

        if (empty($returnItems)) {
            return null;
        }

        $storeId = null;
        if ($sapOrderData->ShippingPoint ?? null) {
            $storeId = $this->sapUtil->getMapping(MappingType::STORES, $sapOrderData->ShippingPoint);
        }

        return sqlGetConnection()->transactional(function () use ($sapId, $returnItems, $storeId) {
            $returnId = (int) $this->returnsUtil->createReturn('', $returnItems, sendMail: false);

            $this->returnsUtil->recalculate($returnId);
            $this->returnsUtil->getReturn($returnId)->setCustomDataValue('id_store', $storeId ?? 1);

            $this->sapUtil->createMapping(MappingType::RETURNS, $sapId, $returnId);

            return $returnId;
        });
    }

    private function getReasonBySAPItem(object $item): int
    {
        $reasonsByName = Mapping::mapKeys(
            $this->returnsUtil->getReasons(),
            fn ($k, $v) => [StringUtil::slugify($v), $k],
        );

        return (int) ($reasonsByName[StringUtil::slugify($item->ReasonTxt ?? '')] ?? 0);
    }

    private function handleReturnWithoutReturnsModule(array $data): void
    {
        try {
            $order = \Order::createFromDbOrderNo($data['OrderId']);
        } catch (\InvalidArgumentException $e) {
            return;
        }

        $this->saveOrderCreditNote(
            data: $data,
            return: null,
            order: $order,
            sendOrderEmail: true
        );
    }

    /**
     * Uložení dobropisu k vratce/objednávce.
     */
    private function saveOrderCreditNote(array $data, ?ReturnEntity $return, ?\Order $order, bool $sendOrderEmail = false): void
    {
        if (!$order && !$return) {
            throw new \RuntimeException('Invalid call of `saveOrderCreditNote`: one of `$return` and `$order` arguments is required!');
        }

        if (!($creditNoteNumber = $this->sapUtil->getAdditionalFieldValue($data['Additionals'] ?? [], 'CRMEMO_NR'))) {
            return;
        }

        $order ??= QueryHint::withRouteToMaster(fn () => $this->returnsUtil->getReturnOrder($return->getId()));

        // pole s cislama faktur, protoze se muze stat, ze jich v SAPu bylo vytvoreno vic
        if (!($creditNoteNumbers = $order->getData('sapCreditNoteNumbers'))) {
            $creditNoteNumbers = [];
        }

        $creditNoteNumbers[] = $creditNoteNumber;

        // aktualizuju seznam s cislama dobropisu
        $order->setData('sapCreditNoteNumbers', array_unique($creditNoteNumbers));

        $dobropisUrl = path(
            'kupshop_orderingbundle_pdfinvoice',
            [
                'id_order' => $order->id,
                'cf' => $order->getSecurityCode(),
                'invoiceNumber' => $creditNoteNumber,
                'type' => 'creditNote',
            ],
            Router::ABSOLUTE_URL
        );

        $return?->setCustomDataValue('sapCreditNoteUrl', $dobropisUrl);

        // zalogovat cislo dobropisu k vratce
        if ($return) {
            $this->returnsUtil?->logHistory(
                $return->getId(),
                sprintf('[SAP] Číslo dobropisu: <a href="%s" target="_blank">%s</a>', $dobropisUrl, $creditNoteNumber),
                status: $return->getStatus()
            );
        }

        // zalogovat číslo dobropisu k objednavce
        if ($return) {
            $order->logHistory(
                sprintf(
                    '[SAP] K vratce číslo "%s" byl vystaven dobropis: <a href="%s" target="_blank">%s</a>',
                    $return->getCode(),
                    $dobropisUrl,
                    $creditNoteNumber
                )
            );
        } else {
            $order->logHistory(sprintf('[SAP] Číslo dobropisu: <a href="%s" target="_blank">%s</a>', $dobropisUrl, $creditNoteNumber));
        }

        // nastavim odkaz pro dobropis
        $order->setPlaceholder('ODKAZ_DOBROPIS', $dobropisUrl);

        if ($sendOrderEmail && $order->source !== OrderInfo::ORDER_SOURCE_DROPSHIP) {
            // odeslat email s dobropisem
            $order->sendEmail(null, 'dobropis');
        }
    }
}
