<?php

declare(strict_types=1);

/*
 * CoreShop
 *
 * This source file is available under two different licenses:
 *  - GNU General Public License version 3 (GPLv3)
 *  - CoreShop Commercial License (CCL)
 * Full copyright and license information is available in
 * LICENSE.md which is distributed with this source code.
 *
 * @copyright  Copyright (c) CoreShop GmbH (https://www.coreshop.org)
 * @license    https://www.coreshop.org/license     GPLv3 and CCL
 *
 */

namespace CoreShop\Bundle\OrderBundle\StateResolver;

use CoreShop\Bundle\WorkflowBundle\Manager\StateMachineManager;
use CoreShop\Component\Order\InvoiceStates;
use CoreShop\Component\Order\Model\OrderInterface;
use CoreShop\Component\Order\Model\OrderInvoiceInterface;
use CoreShop\Component\Order\OrderInvoiceStates;
use CoreShop\Component\Order\OrderInvoiceTransitions;
use CoreShop\Component\Order\Processable\ProcessableInterface;
use CoreShop\Component\Order\Repository\OrderInvoiceRepositoryInterface;
use CoreShop\Component\Order\StateResolver\StateResolverInterface;

final class OrderInvoiceStateResolver implements StateResolverInterface
{
    public function __construct(
        private StateMachineManager $stateMachineManager,
        private OrderInvoiceRepositoryInterface $orderInvoiceRepository,
        private ProcessableInterface $processable,
    ) {
    }

    public function resolve(OrderInterface $order): void
    {
        if ($order->getInvoiceState() === OrderInvoiceStates::STATE_INVOICED) {
            return;
        }

        $workflow = $this->stateMachineManager->get($order, OrderInvoiceTransitions::IDENTIFIER);

        if ($this->allInvoicesInStateButOrderStateNotUpdated($order, InvoiceStates::STATE_COMPLETE, OrderInvoiceStates::STATE_INVOICED)) {
            $workflow->apply($order, OrderInvoiceTransitions::TRANSITION_INVOICE);
        }

        if ($this->isPartiallyInvoicedButOrderStateNotUpdated($order)) {
            $workflow->apply($order, OrderInvoiceTransitions::TRANSITION_PARTIALLY_INVOICE);
        }
    }

    private function countOrderInvoicesInState(OrderInterface $order, string $invoiceState): int
    {
        $invoices = $this->orderInvoiceRepository->getDocuments($order);

        $items = 0;
        /** @var OrderInvoiceInterface $invoice */
        foreach ($invoices as $invoice) {
            if ($invoice->getState() === $invoiceState) {
                ++$items;
            }
        }

        return $items;
    }

    private function allInvoicesInStateButOrderStateNotUpdated(
        OrderInterface $order,
        string $invoiceState,
        string $orderInvoiceState,
    ): bool {
        $invoiceInStateAmount = $this->countOrderInvoicesInState($order, $invoiceState);
        $invoiceAmount = count($this->orderInvoiceRepository->getDocumentsNotInState($order, OrderInvoiceStates::STATE_CANCELLED));

        return $invoiceAmount === $invoiceInStateAmount &&
            $orderInvoiceState !== $order->getInvoiceState() &&
            $this->processable->isFullyProcessed($order);
    }

    private function isPartiallyInvoicedButOrderStateNotUpdated(OrderInterface $order): bool
    {
        $invoiceInCompleteStateAmount = $this->countOrderInvoicesInState($order, InvoiceStates::STATE_COMPLETE);

        return
            $invoiceInCompleteStateAmount > 0 &&
            !$this->processable->isFullyProcessed($order) &&
            OrderInvoiceStates::STATE_PARTIALLY_INVOICED !== $order->getInvoiceState();
    }
}
