<?php

namespace domain\Order\Order;

use domain\_\AbstractService;
use domain\_\ManagerInterface;
use domain\Distribution\Distribution\DistributionRepository;
use domain\Distribution\Distribution\DistributionSolver;
use domain\Order\Order\Event\OrderDeleteEvent;
use domain\Order\OrderStatus\OrderStatus;
use domain\Order\OrderStatus\OrderStatusRepository;
use domain\Order\OrderStatusHistory\OrderStatusHistoryManager;
use domain\Producer\Producer\Producer;
use domain\Producer\Producer\ProducerSolver;
use domain\Subscription\Subscription\Subscription;
use domain\Subscription\Subscription\SubscriptionRepository;
use domain\Subscription\Subscription\SubscriptionSolver;
use domain\User\User\User;

class OrderManager extends AbstractService implements ManagerInterface
{
    protected ProducerSolver $producerSolver;
    protected OrderBuilder $orderBuilder;
    protected OrderStatusRepository $orderStatusRepository;
    protected OrderStatusHistoryManager $orderStatusHistoryManager;
    protected DistributionRepository $distributionRepository;
    protected \domain\Subscription\Subscription\OrderManager $subscriptionOrderManager;
    protected OrderRepository $orderRepository;
    protected DistributionSolver $distributionSolver;
    protected OrderSolver $orderSolver;
    protected SubscriptionSolver $subscriptionSolver;
    protected SubscriptionRepository $subscriptionRepository;

    public function loadDependencies(): void
    {
        $this->producerSolver = $this->loadService(ProducerSolver::class);
        $this->orderBuilder = $this->loadService(OrderBuilder::class);
        $this->orderStatusRepository = $this->loadService(OrderStatusRepository::class);
        $this->orderStatusHistoryManager = $this->loadService(OrderStatusHistoryManager::class);
        $this->distributionRepository = $this->loadService(DistributionRepository::class);
        $this->subscriptionOrderManager = $this->loadService(\domain\Subscription\Subscription\OrderManager::class);
        $this->orderRepository = $this->loadService(OrderRepository::class);
        $this->distributionSolver = $this->loadService(DistributionSolver::class);
        $this->orderSolver = $this->loadService(OrderSolver::class);
        $this->subscriptionSolver = $this->loadService(SubscriptionSolver::class);
        $this->subscriptionRepository = $this->loadService(SubscriptionRepository::class);
    }

    public function changeOrderStatus(Order $order, string $orderStatusAlias, User $user, \DateTime $date = null)
    {
        $orderStatus = $this->orderStatusRepository->getOrderStatusByAlias($orderStatusAlias);

        $order->setOrderStatus($orderStatus);
        if(!$this->orderSolver->isOrderStatusOrdered($order)) {
            $this->orderBuilder->initDateUpdate($order);
        }
        $this->orderBuilder->update($order);

        $this->orderStatusHistoryManager->create(
            $order,
            $orderStatus,
            is_null($date) ? new \DateTime() : $date,
            $user
        );
    }

    public function deleteOrder(Order $order, User $user, bool $force = false): bool
    {
        $return = false;
        $this->orderBuilder->initOrder($order);

        // delete
        if ($this->producerSolver->getConfig('option_behavior_cancel_order') == Producer::BEHAVIOR_DELETE_ORDER_DELETE ||
            ($this->producerSolver->getConfig('option_behavior_cancel_order') == Producer::BEHAVIOR_DELETE_ORDER_STATUS
                && $this->orderSolver->isOrderStatusCanceled($order))
            || $force) {

            $this->changeOrderStatus($order, OrderStatus::ALIAS_DELETED, $user);
            $return = true;
        }
        // cancel
        elseif ($this->producerSolver->getConfig('option_behavior_cancel_order') == Producer::BEHAVIOR_DELETE_ORDER_STATUS) {
            $this->changeOrderStatus($order, OrderStatus::ALIAS_CANCELED, $user);
            $return = true;
        }

        if($return) {
            $orderDeleteEvent = new OrderDeleteEvent();
            $orderDeleteEvent->order = $order;
            $order->trigger(Order::EVENT_DELETE, $orderDeleteEvent);
        }

        return $return;
    }

    public function updateOrdersIncomingDistributionsFromSubscription(Subscription $subscription, User $userAction, $update = false): array
    {
        $orderArray = [];
        $matchedDistributionsArray = $this->distributionRepository->findDistributionsIncomingMatchWithSubscrtiption($subscription, true);

        if ($update) {
            $this->deleteOrdersIncomingDistributionsFromSubscription($subscription, $userAction);
        }

        if (count($matchedDistributionsArray)) {
            foreach ($matchedDistributionsArray as $distribution) {
                $orderArray[] = $this->createOrderFromSubscription($subscription, $distribution->date, $userAction);
            }
        }

        return $orderArray;
    }

    public function deleteOrdersIncomingDistributionsFromSubscription(Subscription $subscription, User $userAction, bool $deleteAfterDateEnd = false): int
    {
        $ordersArray = $this->orderRepository->findOrdersIncomingBySubscription($subscription, !$deleteAfterDateEnd);
        $countOrdersDeleted = 0;

        if ($ordersArray && count($ordersArray)) {
            foreach ($ordersArray as $order) {
                if ($this->distributionSolver->isDistributionAvailable($order->distribution)) {
                    $theOrder = $this->orderRepository->findOneOrderById($order->id);
                    $this->orderBuilder->initOrder($theOrder);
                    $this->deleteOrder($order, $userAction, true);
                    $countOrdersDeleted++;
                }
            }
        }

        return $countOrdersDeleted;
    }

    public function createOrderFromSubscription(Subscription $subscription, string $date, User $userAction, bool $force = false): ?Order
    {
        $now = date('Y-m-d');
        $distributionDate = date('Y-m-d', strtotime($date));
        $distribution = $this->distributionRepository->findOneDistribution($distributionDate);

        if ($distribution
            && $distribution->active
            && ($distributionDate > $now || $force)
            && count($subscription->productSubscription)
            && $subscription->pointSale) {

            $order = $this->orderBuilder->createOrder($distribution);
            $this->orderBuilder->updateOrderFromSubscription($order, $subscription);
            $this->changeOrderStatus($order, OrderStatus::ALIAS_ORDERED, $userAction);

            return $order;
        }

        return null;
    }

    public function createAllOrdersFromSubscriptions(string $date, User $userAction, bool $force = false): array
    {
        $ordersSubscriptionsArray = [];
        $distribution = $this->distributionRepository->findOneDistribution(date('Y-m-d', strtotime($date)));

        if ($distribution) {
            $orderArray = $this->orderRepository->findOrdersByDistribution($distribution);
            $subscriptionArray = $this->subscriptionRepository->findSubscriptionsByDate($date);
            foreach ($subscriptionArray as $subscription) {
                if (!$this->subscriptionSolver->hasOrderAlreadyExist($subscription, $orderArray)) {
                    $order = $this->createOrderFromSubscription($subscription, $date, $userAction, $force);
                    if ($order) {
                        $ordersSubscriptionsArray[] = $order;
                    }
                }
            }
        }

        return $ordersSubscriptionsArray;
    }
}