<?php
declare(strict_types=1);

namespace FiloBlu\Refilo\Model;

use FiloBlu\Refilo\Api\MergeCartsInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\Data\CartInterface;
use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
use Magento\Quote\Model\Quote;
use Magento\Store\Model\StoreManagerInterface;
use Psr\Log\LoggerInterface;
use Throwable;

/**
 * Class MergeCart
 * @package FiloBlu\Refilo\Model
 */
class MergeCart implements MergeCartsInterface
{
    /**
     * @var CartRepositoryInterface
     */
    private $cartRepository;
    /**
     * @var StoreManagerInterface
     */
    private $storeManager;
    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var MaskedQuoteIdToQuoteIdInterface
     */
    private $maskedQuoteIdToQuoteId;
    /**
     * @var CustomerRepositoryInterface
     */
    private $customerRepository;

    /**
     * MergeCart constructor.
     * @param StoreManagerInterface $storeManager
     * @param CartRepositoryInterface $cartRepository
     * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
     * @param CustomerRepositoryInterface $customerRepository
     * @param LoggerInterface $logger
     */
    public function __construct(
        StoreManagerInterface           $storeManager,
        CartRepositoryInterface         $cartRepository,
        MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId,
        CustomerRepositoryInterface     $customerRepository,
        LoggerInterface                 $logger
    )
    {
        $this->cartRepository = $cartRepository;
        $this->storeManager = $storeManager;
        $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId;
        $this->logger = $logger;
        $this->customerRepository = $customerRepository;
    }

    /**
     * @param int $userId
     * @param string $storeCode
     * @param string $customerCartId
     * @param string $guestCartId
     * @return void
     */
    public function mergeGuestCartIntoActiveCustomerCart($userId, $storeCode, $customerCartId, $guestCartId)
    {
        try {

            $customer = $this->customerRepository->getById($userId);

            if ((int)$customerCartId === (int)$guestCartId) {

                if ((int)$customerCartId > 0) {

                    try {
                        $customerCart = $this->cartRepository->getActive($customerCartId);
                        // Force update customer for correct customer group switching
                        if((int)$customer->getId() === $customerCart->getCustomer()->getId()) {
                            $customerCart->setCustomer($customer);
                            $this->cartRepository->save($customerCart);
                        }
                        return;
                    } catch (NoSuchEntityException $exception) {
                        $this->logger->error($exception->getMessage(), ['exception' => $exception]);
                    }
                }

                return;

            }

            $storeId = (int)$this->storeManager->getStore($storeCode)->getId();
            $customerCart = $this->cartRepository->getActive($customerCartId);

            $guestCart = $this->execute($guestCartId, null, $storeId);
            /** @var CartInterface| Quote $customerCart */
            $customerCart->merge($guestCart);
            $customerCart->setCustomer($customer);
            $guestCart->setIsActive(false);
            $this->cartRepository->save($guestCart);
            $this->cartRepository->save($customerCart);
        } catch (Throwable $throwable) {
            $this->logger->error($throwable->getMessage(), ['exception' => $throwable]);
        }
    }

    /**
     * Get cart for user
     *
     * @param string $cartHash
     * @param int|null $customerId
     * @param int $storeId
     * @return Quote
     * @throws NoSuchEntityException
     * @throws LocalizedException
     */
    public function execute(string $cartHash, ?int $customerId, int $storeId): Quote
    {
        $cartId = $cartHash;
        try {

            if (!is_numeric($cartHash)) {
                $cartId = $this->maskedQuoteIdToQuoteId->execute($cartHash);
            }
        } catch (NoSuchEntityException $exception) {
            throw new LocalizedException(
                __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash])
            );
        }

        try {
            /** @var Quote $cart */
            $cart = $this->cartRepository->get($cartId);
        } catch (NoSuchEntityException $e) {
            throw new LocalizedException(
                __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash])
            );
        }

        if (false === (bool)$cart->getIsActive()) {
            throw new LocalizedException(__('The cart isn\'t active.'));
        }

        if ((int)$cart->getStoreId() !== $storeId) {
            throw new LocalizedException(
                __(
                    'Wrong store code specified for cart "%masked_cart_id"',
                    ['masked_cart_id' => $cartHash]
                )
            );
        }

        $cartCustomerId = (int)$cart->getCustomerId();

        /* Guest cart, allow operations */
        if (0 === $cartCustomerId && (null === $customerId || 0 === $customerId)) {
            return $cart;
        }

        if ($cartCustomerId !== $customerId) {
            throw new LocalizedException(
                __(
                    'The current user cannot perform operations on cart "%masked_cart_id"',
                    ['masked_cart_id' => $cartHash]
                )
            );
        }
        return $cart;
    }
}
