<?php
declare(strict_types=1);

namespace FiloBlu\Refilo\Model;

use FiloBlu\Refilo\Api\Data\CustomerOrderRequestInterface;
use FiloBlu\Refilo\Api\Data\GuestOrderRequestInterface;
use FiloBlu\Refilo\Api\OrderMappingManagementInterface;
use FiloBlu\Refilo\Api\OrderMappingRepositoryInterface;
use FiloBlu\Refilo\Helper\OrderMappingHelper;
use Magento\Framework\Api\SearchCriteriaBuilderFactory;
use Magento\Framework\Api\SortOrder;
use Magento\Framework\Api\SortOrderBuilder;
use Magento\Sales\Api\Data\OrderSearchResultInterface;
use Magento\Sales\Api\OrderRepositoryInterface;

/**
 * @class OrderMappingManagement
 * @package FiloBlu\Refilo\Model
 */
class OrderMappingManagement implements OrderMappingManagementInterface
{
    /**
     * @var OrderMappingRepositoryInterface
     */
    private $orderMappingRepository;
    /**
     * @var OrderMappingHelper
     */
    private $orderMappingHelper;
    /**
     * @var OrderRepositoryInterface
     */
    private $orderRepository;
    /**
     * @var SearchCriteriaBuilderFactory
     */
    private $searchCriteriaBuilderFactory;
    /**
     * @var SortOrderBuilder
     */
    private $sortOrderBuilder;

    /**
     * @param OrderMappingRepositoryInterface $orderMappingRepository
     * @param OrderMappingHelper $orderMappingHelper
     * @param OrderRepositoryInterface $orderRepository
     * @param SearchCriteriaBuilderFactory $searchCriteriaBuilderFactory
     * @param SortOrderBuilder $sortOrderBuilder
     */
    public function __construct(
        OrderMappingRepositoryInterface   $orderMappingRepository,
        OrderMappingHelper                $orderMappingHelper,
        OrderRepositoryInterface          $orderRepository,
        SearchCriteriaBuilderFactory      $searchCriteriaBuilderFactory,
        SortOrderBuilder                  $sortOrderBuilder
    )
    {
        $this->orderMappingRepository = $orderMappingRepository;
        $this->orderMappingHelper = $orderMappingHelper;
        $this->orderRepository = $orderRepository;
        $this->searchCriteriaBuilderFactory = $searchCriteriaBuilderFactory;
        $this->sortOrderBuilder = $sortOrderBuilder;
    }

    /**
     * @param GuestOrderRequestInterface $request
     * @return OrderSearchResultInterface|null
     */
    public function getGuestOrder(GuestOrderRequestInterface $request)
    {
        if($request->getStoreId()) {
            return null;
        }

        // Richiesta tramite order id devo avere il protect code
        if($request->getOrderId() && $request->getProtectCode()) {
            return $this->getOrdersByIdAndProtectCode($request);
        }

        if($request->getIncrementId() && $request->getCustomerEmail()) {
            return $this->getOrdersByIncrementAndEmail($request);
        }

        return null;
    }

    /**
     * @param GuestOrderRequestInterface $request
     * @return OrderSearchResultInterface|null
     */
    public function getOrdersByIncrementAndEmail(GuestOrderRequestInterface $request)
    {
        $incrementId = $request->getIncrementId();
        $email = $request->getCustomerEmail();
        $store = $request->getStoreId();

        if(!$incrementId || !$email || !$store){
            return null;
        }

        $searchCriteriaBuilder = $this->searchCriteriaBuilderFactory->create();
        $searchCriteriaBuilder->setPageSize(1);
        $searchCriteriaBuilder->setCurrentPage(1);

        $searchCriteriaBuilder->addFilter('customer_email', $email);
        $searchCriteriaBuilder->addFilter('increment_id', $incrementId);

        $enabledStore = array_keys($this->orderMappingHelper->getEnabledStore());
        $enabledStore[] = $request->getStoreId();
        $searchCriteriaBuilder->addFilter('store_id', $enabledStore, 'in');
        $searchCriteria = $searchCriteriaBuilder->create();

        return $this->orderRepository->getList($searchCriteria);
    }

    /**
     * @param GuestOrderRequestInterface $request
     * @return OrderSearchResultInterface|null
     */
    public function getOrdersByIdAndProtectCode(GuestOrderRequestInterface $request)
    {
        $orderId = $request->getOrderId();
        $store = $request->getStoreId();
        $protectCode = $request->getProtectCode();

        if(!$orderId || !$store || !$protectCode || !$this->orderMappingHelper->checkIfOrderIsValid($orderId, $protectCode)) {
            return null;
        }

        $searchCriteriaBuilder = $this->searchCriteriaBuilderFactory->create();
        $searchCriteriaBuilder->setPageSize(1);
        $searchCriteriaBuilder->setCurrentPage(1);

        $searchCriteriaBuilder->addFilter('entity_id', $orderId);

        $enabledStore = array_keys($this->orderMappingHelper->getEnabledStore());
        $enabledStore[] = $request->getStoreId();
        $searchCriteriaBuilder->addFilter('store_id', $enabledStore, 'in');
        $searchCriteria = $searchCriteriaBuilder->create();

        return $this->orderRepository->getList($searchCriteria);
    }

    /**
     * @param CustomerOrderRequestInterface $request
     * @return OrderSearchResultInterface|null
     */
    public function getCustomerOrder(CustomerOrderRequestInterface $request)
    {
        if (!$request->getCustomerId()) {
            return $this->getEmptyResult();
        }

        $orderIds = $this->getOrderByOrderId($request);
        if(empty($orderIds)) {
            return $this->getEmptyResult();
        }

        $searchCriteriaBuilder = $this->searchCriteriaBuilderFactory->create();
        $pageSize = $request->getPageSize() ? $request->getPageSize() : 1;
        $pageNumber = $request->getPageNumber() ? $request->getPageNumber() : 1;
        $searchCriteriaBuilder->setPageSize($pageSize);
        $searchCriteriaBuilder->setCurrentPage($pageNumber);

        if ($request->getOrderField()) {
            $orderDirection = strtoupper($request->getOrderDirection() ?: 'DESC');

            $sortOrder = $this->sortOrderBuilder
                ->setField($request->getOrderField())
                ->setDirection($orderDirection === 'DESC' ? SortOrder::SORT_DESC : SortOrder::SORT_ASC)
                ->create();

            $searchCriteriaBuilder->addSortOrder($sortOrder);
        }

        $enabledStore = array_keys($this->orderMappingHelper->getEnabledStore());
        $enabledStore[] = $request->getStoreId();

        $searchCriteriaBuilder->addFilter('store_id', $enabledStore, 'in');
        $searchCriteriaBuilder->addFilter('entity_id', $orderIds, 'in');
        $searchCriteria = $searchCriteriaBuilder->create();

        return $this->orderRepository->getList($searchCriteria);

    }

    /**
     * Retrieve order ids for a customer, optionally filtered by a requested order id.
     *
     * Combines mapped orders from the filoblu mapping table and the customer's own orders,
     * then returns either the merged list or the requested order id if it belongs to the customer.
     *
     * @param CustomerOrderRequestInterface $request
     * @return array
     */
    public function getOrderByOrderId(CustomerOrderRequestInterface $request): array
    {
        if (!$request->getCustomerId()) {
            return [];
        }

        // Orders from other websites
        $filobluOrdersMapped = $this->getOrderIdByCustomer($request->getCustomerId());
        $orderIds = $this->orderMappingHelper->getCustomerOrderIds($request->getCustomerId());

        $customerOrderIds = array_merge($filobluOrdersMapped, $orderIds);

        if (!$request->getOrderId()) {
            return $customerOrderIds;
        }

        return in_array($request->getOrderId(), $customerOrderIds) ? [$request->getOrderId()] : [];
    }

    /**
     * @param int $customerId
     * @return array
     */
    public function getOrderIdByCustomer($customerId): array
    {
        if (!$customerId) {
            return [];
        }

        $enabledStore = $this->orderMappingHelper->getEnabledStore();

        return $this->orderMappingRepository->getByCustomerId($customerId, array_keys($enabledStore));
    }

    /**
     * Create and return an empty OrderSearchResultInterface.
     *
     * Magento does not provide a way to directly instantiate an empty
     * OrderSearchResultInterface, so a SearchCriteria matching no results
     * is used and the repository's getList is returned.
     *
     * @return OrderSearchResultInterface
     */
    public function getEmptyResult(): OrderSearchResultInterface
    {
        $searchCriteriaBuilder = $this->searchCriteriaBuilderFactory->create();
        $searchCriteriaBuilder->addFilter('customer_id', '-1');
        $searchCriteria = $searchCriteriaBuilder->create();
        return $this->orderRepository->getList($searchCriteria);
    }
}
