<?php declare(strict_types=1);


namespace Atl\DiscountExtension\Subscriber;


use Atl\DiscountExtension\Core\System\RepeatDiscount\RepeatDiscountEntity;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection;
use Shopware\Core\Checkout\Order\OrderEntity;
use Shopware\Core\Checkout\Order\OrderStates;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\System\CustomField\CustomFieldEntity;
use Shopware\Core\System\StateMachine\Event\StateMachineTransitionEvent;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class StateMachineTransitionSubscriber implements EventSubscriberInterface
{
    /**
     * @var SystemConfigService
     */
    private $systemConfigService;

    /**
     * @var EntityRepositoryInterface
     */
    private $orderRepository;

    /**
     * @var EntityRepositoryInterface
     */
    private $customFieldRepository;

    /**
     * @var EntityRepositoryInterface
     */
    private $customerRepository;

    public function __construct
    (
        SystemConfigService $systemConfigService,
        EntityRepositoryInterface $orderRepository,
        EntityRepositoryInterface $customFieldRepository,
        EntityRepositoryInterface $customerRepository
    )
    {
        $this->systemConfigService = $systemConfigService;
        $this->orderRepository = $orderRepository;
        $this->customFieldRepository = $customFieldRepository;
        $this->customerRepository = $customerRepository;
    }

    /**
     * @return string[]
     */
    public static function getSubscribedEvents(): array
    {
        return [
            StateMachineTransitionEvent::class => 'onStateTransition'
        ];
    }

    /**
     * @param StateMachineTransitionEvent $event
     * @return void
     */
    public function onStateTransition(StateMachineTransitionEvent $event): void
    {
        // Early return if order state is not 'completed'
        if ($event->getToPlace()->getTechnicalName() !== OrderStates::STATE_COMPLETED) {
            return;
        }

        $orderId = $event->getEntityId();
        $order = $this->getOrder($orderId, $event);
        $salesChannelId = $order->getSalesChannelId();
        
        // In case no order could be found - return
        if (empty($order)) {
            return;
        }

        $minAmountDiscountableLineItems = $this->systemConfigService->get('AtlDiscountExtension.config.minAmountDiscountableLineItems', $salesChannelId);
        $amountDiscountableLineItems = $this->getAmountDiscountableLineItems($order->getLineItems(), $minAmountDiscountableLineItems, $salesChannelId, $event);
        
        // In case it is not enough - return
        if ($amountDiscountableLineItems < $minAmountDiscountableLineItems) {
            return;
        }
        
        $customer = $order->getOrderCustomer()->getCustomer();
        
        // Tag the customer and trace last order date in 'spwn_repeat_discount' table
        $this->tagCustomer($customer, $salesChannelId, $event);
    }

    /**
     * @param string $orderId
     * @param StateMachineTransitionEvent $event
     * @return OrderEntity
     */
    private function getOrder(string $orderId, StateMachineTransitionEvent $event): OrderEntity
    {
        $criteria = new Criteria();
        $criteria->addFilter(new EqualsFilter('id', $orderId));
        $criteria->addAssociation('lineItems');
        $criteria->addAssociation('orderCustomer.customer');

        return $this->orderRepository->search($criteria, $event->getContext())->first();
    }

    /**
     * @param OrderLineItemCollection $lineItems
     * @param int $minAmountDiscountableLineItems
     * @param string $salesChannelId
     * @param StateMachineTransitionEvent $event
     * @return int
     */
    private function getAmountDiscountableLineItems
    (
        OrderLineItemCollection $lineItems,
        int $minAmountDiscountableLineItems,
        string $salesChannelId,
        StateMachineTransitionEvent $event
    ): int
    {
        $customFieldName = $this->getCustomFieldName($event, $salesChannelId);

        // In case no custom field was assigned in the plugin config - return
        if ($customFieldName === '') {
            return 0;
        }
        
        $amountDiscountableLineItems = 0;
        foreach($lineItems as $lineItem) {
            if (empty($lineItem->getPayload()['customFields'])) {
                continue;
            }
            $customFields = $lineItem->getPayload()['customFields'];
            // Checks if the custom field exists and is set to true
            if (!empty($customFields[$customFieldName]) && $customFields[$customFieldName] === true) {
                $amountDiscountableLineItems += $lineItem->getQuantity();
            }

            // Break if min amount is already reached
            if ($amountDiscountableLineItems >= $minAmountDiscountableLineItems) {
                break;
            }
        }
        return $amountDiscountableLineItems;
    }

    /**
     * @param StateMachineTransitionEvent $event
     * @param string $salesChannelId
     * @return string
     */
    private function getCustomFieldName(StateMachineTransitionEvent $event, string $salesChannelId): string
    {
        $customFieldId = $this->systemConfigService->get('AtlDiscountExtension.config.customField', $salesChannelId);

        $criteria = new Criteria();
        $criteria->addFilter(new EqualsFilter('id', $customFieldId));

        /** @var CustomFieldEntity $customField */
        $customField = $this->customFieldRepository->search($criteria, $event->getContext())->first();

        // In case no custom field was assigned in the plugin config - return
        if (empty($customField)) {
            return '';
        }

        return $customField->getName();
    }

    /**
     * @param CustomerEntity $customer
     * @param string $salesChannelId
     * @param StateMachineTransitionEvent $event
     * @return void
     */
    private function tagCustomer(CustomerEntity $customer, string $salesChannelId, StateMachineTransitionEvent $event): void
    {
        $tagId = $this->systemConfigService->get('AtlDiscountExtension.config.tag', $salesChannelId);

        if (empty($tagId)) {
            return;
        }

        $update = [
            'id' => $customer->getId(),
            'tags' => [
                [
                    'id' => $tagId
                ]
            ],
            'repeatDiscount' => [
                'customerId' => $customer->getId(),
                'lastOrderDate' => new \DateTime()
            ]
        ];

        /** @var RepeatDiscountEntity $repeatDiscount */
        $repeatDiscount = $customer->getExtensions()['repeatDiscount'];

        // If a discount is already applied, update its duration, else create one
        if (!empty($repeatDiscount)) {
            $repeatDiscountId = $repeatDiscount->getId();
            $update['repeatDiscount']['id'] = $repeatDiscountId;
        }

        $this->customerRepository->update([$update], $event->getContext());
    }
}
