<?php

namespace FiloBlu\Rma\Model;

use Exception;
use FiloBlu\Rma\Api\Data\EasyReturnInterface;
use FiloBlu\Rma\Helper\ChangeSizeAttributeHelper;
use FiloBlu\Rma\Helper\EasyReturnHelper;
use FiloBlu\Rma\Helper\RmaHelper;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\DataObject;
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Framework\Event\ManagerInterface;
use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Model\AbstractModel;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Pricing\PriceCurrencyInterface;
use Magento\Framework\Registry;
use Magento\Sales\Api\Data\OrderAddressInterface;
use Magento\Sales\Api\Data\OrderAddressInterfaceFactory;
use Magento\Sales\Api\OrderItemRepositoryInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Store\Api\StoreRepositoryInterface;
use Throwable;

/**
 *
 */
class EasyReturn extends AbstractModel implements IdentityInterface, EasyReturnInterface
{
    /**
     * @var string
     */
    const CACHE_TAG = 'filoblu_rma_easyreturn';

    /**
     * @var string
     */
    protected $_cacheTag = 'filoblu_rma_easyreturn';

    /**
     * @var string
     */
    protected $_eventPrefix = 'filoblu_rma_easyreturn';

    /**
     * @var OrderRepositoryInterface
     */
    protected $orderRepository;
    /**
     * @var ChangeSizeAttributeHelper
     */
    protected $changeSizeAttribute;

    /**
     * @var OrderAddressInterfaceFactory
     */
    private $addressInterfaceFactory;

    /**
     * @var EasyReturnHelper
     */
    private $easyReturnHelper;

    /**
     * @var OrderItemRepositoryInterface
     */
    private $orderItemRepository;

    /**
     * @var StoreRepositoryInterface
     */
    private $storeRepository;

    /**
     * @var PriceCurrencyInterface
     */
    private $priceCurrency;
    /**
     * @var OrderLocator
     */
    private $orderLocator;
    /**
     * @var RmaHelper
     */
    private $rmaHelper;
    /**
     * @var ManagerInterface
     */
    private $eventManager;
    /**
     * @var TheoricShippingAmountFactory
     */
    private $theoricShippingAmountFactory;

    /**
     * EasyReturn constructor.
     * @param Context $context
     * @param Registry $registry
     * @param OrderAddressInterfaceFactory $addressInterfaceFactory
     * @param EasyReturnHelper $easyReturnHelper
     * @param OrderRepositoryInterface $orderRepository
     * @param OrderItemRepositoryInterface $orderItemRepository
     * @param StoreRepositoryInterface $storeRepository
     * @param PriceCurrencyInterface $priceCurrency
     * @param ChangeSizeAttributeHelper $changeSizeAttribute
     * @param OrderLocator $orderLocator
     * @param ManagerInterface $eventManager
     * @param TheoricShippingAmountFactory $theoricShippingAmountFactory
     * @param AbstractResource|null $resource
     * @param AbstractDb|null $resourceCollection
     * @param array $data
     */
    public function __construct(
        Context                      $context,
        Registry                     $registry,
        OrderAddressInterfaceFactory $addressInterfaceFactory,
        EasyReturnHelper             $easyReturnHelper,
        OrderRepositoryInterface     $orderRepository,
        OrderItemRepositoryInterface $orderItemRepository,
        StoreRepositoryInterface     $storeRepository,
        PriceCurrencyInterface       $priceCurrency,
        ChangeSizeAttributeHelper    $changeSizeAttribute,
        OrderLocator                 $orderLocator,
        ManagerInterface             $eventManager,
        AbstractResource             $resource = null,
        AbstractDb                   $resourceCollection = null,
        DataObject\Factory           $theoricShippingAmountFactory = null,
        array                        $data = []
    )
    {
        parent::__construct($context, $registry, $resource, $resourceCollection, $data);
        $this->addressInterfaceFactory = $addressInterfaceFactory;
        $this->easyReturnHelper = $easyReturnHelper;
        $this->orderItemRepository = $orderItemRepository;
        $this->storeRepository = $storeRepository;
        $this->priceCurrency = $priceCurrency;
        $this->orderRepository = $orderRepository;
        $this->changeSizeAttribute = $changeSizeAttribute;
        $this->orderLocator = $orderLocator;
        $this->eventManager = $eventManager;
        $this->theoricShippingAmountFactory = $theoricShippingAmountFactory;
    }

    /**
     * @return array|string[]
     */
    public function getIdentities()
    {
        return [self::CACHE_TAG . '_' . $this->getId()];
    }

    /**
     * @return int
     */
    public function getRmaEntityId()
    {
        return $this->getData(self::RMA_ENTITY_ID);
    }

    /**
     * @param int $rmaEntityId
     * @return $this
     */
    public function setRmaEntityId(int $rmaEntityId)
    {
        return $this->setData(self::RMA_ENTITY_ID, $rmaEntityId);
    }

    /**
     * @return float
     */
    public function getTheoricShippingAmount()
    {
        return $this->getData(self::THEORIC_SHIPPING_AMOUNT);
    }

    /**
     * @param array $data
     * @return $this
     * @throws InputException
     * @throws NoSuchEntityException
     */
    public function setEasyReturnData(array $data)
    {
        $order = null;

        if ($this->orderLocator->hasOrder()) {
            $order = $this->orderLocator->getCurrentOrder();
        } else {
            $order = $this->orderLocator->load($data);
            $data['country_id'] = $order->getShippingAddress()->getCountryId();
        }

        $this->setData($data);

        $taxPercentage = 0;
        foreach ($order->getItems() as $item) {
            $taxPercentage = max($taxPercentage, $item->getTaxPercent());
        }

        //save theoric shipping amount

        $shippingValue = $this->easyReturnHelper->simulateEasyReturnShippingCost(
            $this->getPickUpAddress(),
            $data['store'],
            $data['weight'],
            $order->getGrandTotal(),
            $taxPercentage,
            'price',
            $data['return_type']
        );

        $theoricShippingAmount = $this->theoricShippingAmountFactory->create();

        $theoricShippingAmount->setValue($shippingValue);

        $this->eventManager->dispatch(
            'filoblu_override_theoric_shipping_amount',
            ['rma_data' => $data, 'order' => $order, 'theoric_shipping_amount' => $theoricShippingAmount]
        );

        $this->setTheoricShippingAmount($theoricShippingAmount->getData('value'));

        return $this;
    }

    /**
     * @return string
     */
    public function getCountryId()
    {
        return $this->getData(self::COUNTRY_ID);
    }

    /**
     * @return OrderAddressInterface
     */
    public function getPickUpAddress()
    {
        $address = $this->addressInterfaceFactory->create();

        if ($this->getFirstName() === null) {
            return $address;
        }

        $address->setCountryId($this->getCountryId());
        $address->setCity($this->getCity())
            ->setRegionId($this->getRegionId())
            ->setPostcode($this->getPostcode())
            ->setFirstname($this->getFirstName())
            ->setLastname($this->getLastName())
            ->setStreet($this->getStreet())
            ->setTelephone($this->getTelephone());
        $address->setAddressType('shipping');

        if ($this->getRegion() != '') {
            $address->setRegion($this->getRegion());
        }

        return $address;
    }

    /**
     * @return string
     */
    public function getFirstName()
    {
        return $this->getData(self::FIRSTNAME);
    }

    /**
     * @param string $countryId
     * @return $this
     */
    public function setCountryId(string $countryId)
    {
        return $this->setData(self::COUNTRY_ID, $countryId);
    }

    /**
     * @param string $telephone
     * @return $this
     */
    public function setTelephone(string $telephone)
    {
        return $this->setData(self::TELEPHONE, $telephone);
    }

    /**
     * @param string $street
     * @return $this
     */
    public function setStreet(string $street)
    {
        return $this->setData(self::STREET, $street);
    }

    /**
     * @param string $lastname
     * @return $this
     */
    public function setLastName(string $lastname)
    {
        return $this->setData(self::LASTNAME, $lastname);
    }

    /**
     * @param string $firstname
     * @return $this
     */
    public function setFirstName(string $firstname)
    {
        return $this->setData(self::FIRSTNAME, $firstname);
    }

    /**
     * @param string $postcode
     * @return $this
     */
    public function setPostcode(string $postcode)
    {
        return $this->setData(self::POSTCODE, $postcode);
    }

    /**
     * @param int $regionId
     * @return $this
     */
    public function setRegionId(int $regionId)
    {
        return $this->setData(self::REGION_ID, $regionId);
    }

    /**
     * @param string $city
     * @return $this
     */
    public function setCity(string $city)
    {
        return $this->setData(self::CITY, $city);
    }

    /**
     * @return string
     */
    public function getCity()
    {
        return $this->getData(self::CITY);
    }

    /**
     * @return int
     */
    public function getRegionId()
    {
        return $this->getData(self::REGION_ID);
    }

    /**
     * @return string
     */
    public function getPostcode()
    {
        return $this->getData(self::POSTCODE);
    }

    /**
     * @return string
     */
    public function getLastName()
    {
        return $this->getData(self::LASTNAME);
    }

    /**
     * @return string
     */
    public function getStreet()
    {
        return $this->getData(self::STREET);
    }

    /**
     * @return string
     */
    public function getTelephone()
    {
        return $this->getData(self::TELEPHONE);
    }

    /**
     * @return string
     */
    public function getRegion()
    {
        return $this->getData(self::REGION);
    }

    /**
     * @param string $region
     * @return $this
     */
    public function setRegion(string $region)
    {
        return $this->setData(self::REGION, $region);
    }

    /**
     * @param float $amount
     * @return $this
     */
    public function setTheoricShippingAmount(float $amount)
    {
        return $this->setData(self::THEORIC_SHIPPING_AMOUNT, $amount);
    }

    /**
     * @param mixed $data
     * @return string
     * @throws InputException
     * @throws NoSuchEntityException
     */
    public function estimateReturnShippingCost($data)
    {
        $currency = '';
        $addCurrency = !isset($data['add_currency']) || $data['add_currency'];

        $type = $data['price_type'] ?? 'price';
        $order = null;

        try {
            $this->orderLocator->load($data);

            $address = $this->addressInterfaceFactory->create();

            $address
                ->setCountryId($data['country_id'])
                ->setRegionId($data['region_id'])
                ->setCity($data['city'])
                ->setPostcode($data['postcode']);

            $weight = 0;
            $storeId = 0;
            $orderTotal = null;
            $taxPercentage = 0;
            $returnType = 'easy_return';
            $changeSizeCount = 0;
            foreach ($data['items'] as $item) {

                if (empty($item['order_item_id'])) {
                    continue;
                }

                $orderItem = $this->orderItemRepository->get($item['order_item_id']);

                if ($orderTotal === null) {
                    $order = $this->orderRepository->get($orderItem->getOrderId());
                    $orderTotal = $order->getGrandTotal();
                }

                $taxPercentage = max($taxPercentage, $orderItem->getTaxPercent());

                $storeId = $orderItem->getStoreId();
                if ($orderItem->getWeight()) {
                    if ($item['qty_requested'] == '') {
                        $item['qty_requested'] = 1;
                    }
                    $weight += $orderItem->getWeight() * $item['qty_requested'];
                }

                if ($item['resolution'] == $this->changeSizeAttribute->getChangeSizeAttributeOptionId()) {
                    $changeSizeCount++;
                }
            }

            $store = $this->storeRepository->getById($storeId);

            if ($addCurrency) {
                $currency = $this->priceCurrency->getCurrencySymbol($storeId) ?? $this->priceCurrency->getCurrency($storeId)->getCurrencyCode();
            }

            if ($changeSizeCount) {
                $returnType = 'change_size';
            }

            $shippingAmount = $this->easyReturnHelper->simulateEasyReturnShippingCost(
                $address,
                $store,
                $weight,
                $orderTotal,
                $taxPercentage,
                $type,
                $returnType,
                $order->getId(),
                $data['items']
            );

            $data['theoric_shipping_amount'] = $shippingAmount;

            $theoricShippingAmount = $this->theoricShippingAmountFactory->create();
            $theoricShippingAmount->setData('value', $shippingAmount);

            $this->eventManager->dispatch(
                'filoblu_override_theoric_shipping_amount',
                ['rma_data' => $data, 'order' => $order, 'theoric_shipping_amount' => $theoricShippingAmount]
            );

            return number_format($theoricShippingAmount->getData('value'), 2, ',', '') . " $currency";
        } catch (Exception $e) {
            $this->_logger->critical($e->getMessage(), ['exception' => $e]);
            return "0.00 $currency";
        } catch (Throwable $t) {
            $this->_logger->critical($t->getMessage(), ['exception' => $t]);
            return "0.00 $currency";
        }
    }

    /**
     *
     * @noinspection MagicMethodsValidityInspection
     */
    protected function _construct()
    {
        $this->_init(ResourceModel\EasyReturn::class);
    }
}
