<?php

declare(strict_types=1);

namespace App\Service;

use App\Entity\DrinkType;
use App\Entity\Order;
use App\Entity\OrderItem;
use App\Enum\OrderStatus;
use App\Repository\DrinkTypeRepository;
use App\Repository\OrderItemRepository;
use App\Repository\OrderRepository;
use DateTimeImmutable;
use InvalidArgumentException;

readonly class OrderService
{
    public function __construct(
        private OrderRepository $orderRepository,
        private OrderItemRepository $orderItemRepository,
        private DrinkTypeRepository $drinkTypeRepository,
        private InventoryService $inventoryService,
    ) {}

    /**
     * Get all orders
     *
     * @return Order[]
     */
    public function getAllOrders(): array
    {
        return $this->orderRepository->findAll();
    }

    /**
     * Get an order by ID
     *
     * @param int $id
     * @return Order|null
     */
    public function getOrderById(int $id): null|Order
    {
        return $this->orderRepository->find($id);
    }

    /**
     * Get orders by status
     *
     * @return Order[]
     */
    public function getOrdersByStatus(OrderStatus $status): array
    {
        return $this->orderRepository->findByStatus($status);
    }

    public function getActiveOrders(): array
    {
        return $this->orderRepository->findByMultipleStatus([
            OrderStatus::NEW,
            OrderStatus::ORDERED,
            OrderStatus::IN_WORK,
        ]);
    }

    /**
     * Get the most recent order in "new" or "in work" status
     *
     * @return Order|null
     */
    public function getMostRecentActiveOrder(): null|Order
    {
        $newOrders = $this->orderRepository->findByStatus(OrderStatus::NEW);
        $inWorkOrders = $this->orderRepository->findByStatus(OrderStatus::IN_WORK);

        $activeOrders = array_merge($newOrders, $inWorkOrders);

        // Sort by creation date (newest first)
        usort($activeOrders, fn(Order $a, Order $b): int => $b->getCreatedAt() <=> $a->getCreatedAt());

        return $activeOrders[0] ?? null;
    }

    /**
     * Check if there are any orders in "new" or "in work" status
     *
     * @return bool
     */
    public function hasActiveOrders(): bool
    {
        $newOrders = $this->orderRepository->findByStatus(OrderStatus::NEW);
        $inWorkOrders = $this->orderRepository->findByStatus(OrderStatus::IN_WORK);

        return $newOrders !== [] || $inWorkOrders !== [];
    }

    /**
     * Get orders within a specific date range
     *
     * @param DateTimeImmutable $start
     * @param DateTimeImmutable $end
     * @return Order[]
     */
    public function getOrdersByDateRange(DateTimeImmutable $start, DateTimeImmutable $end): array
    {
        return $this->orderRepository->findByDateRange($start, $end);
    }

    /**
     * Create a new order
     *
     * @param array<int, array{drinkTypeId: int, quantity: int}> $items
     * @return Order
     * @throws InvalidArgumentException If a drink type ID is invalid
     */
    public function createOrder(array $items): Order
    {
        $order = new Order();
        $this->orderRepository->save($order);

        foreach ($items as $item) {
            $drinkType = $this->drinkTypeRepository->find($item['drinkTypeId']);
            if ($drinkType === null) {
                throw new InvalidArgumentException("Invalid drink type ID: {$item['drinkTypeId']}");
            }

            $orderItem = new OrderItem($drinkType, $item['quantity'], $order);
            $this->orderItemRepository->save($orderItem);
        }

        return $order;
    }

    /**
     * Create an order based on current stock levels
     *
     * @return Order
     */
    public function createOrderFromStockLevels(): Order
    {
        $lowStockItems = $this->inventoryService->getAllDrinkTypesWithStockLevels();
        $orderItems = [];

        foreach ($lowStockItems as $item) {
            if ($item['currentStock'] < $item['desiredStock']) {
                $orderItems[] = [
                    'drinkTypeId' => $item['drinkType']->getId(),
                    'quantity' => $item['desiredStock'] - $item['currentStock'],
                ];
            }
        }

        return $this->createOrder($orderItems);
    }

    /**
     * Update an order's status
     *
     * @return Order
     * @throws InvalidArgumentException If the status is invalid
     */
    public function updateOrderStatus(Order $order, OrderStatus $status): Order
    {
        $order->setStatus($status);
        $this->orderRepository->save($order);

        return $order;
    }

    /**
     * Add an item to an order
     *
     * @param Order $order
     * @param DrinkType $drinkType
     * @param int $quantity
     * @return OrderItem
     * @throws InvalidArgumentException If the order is not in 'new' or 'in_work' status
     */
    public function addOrderItem(Order $order, DrinkType $drinkType, int $quantity): OrderItem
    {
        if (!in_array($order->getStatus(), [OrderStatus::STATUS_NEW, OrderStatus::STATUS_IN_WORK], true)) {
            throw new InvalidArgumentException(
                "Cannot add items to an order with status '{$order->getStatus()->value}'",
            );
        }

        // Check if the order already has an item for this drink type
        $existingItem = $this->orderItemRepository->findByOrderAndDrinkType($order, $drinkType);

        if ($existingItem instanceof OrderItem) {
            // Update the existing item
            $existingItem->setQuantity($existingItem->getQuantity() + $quantity);
            $this->orderItemRepository->save($existingItem);
            return $existingItem;
        }
        // Create a new item
        $orderItem = new OrderItem($drinkType, $quantity, $order);
        $this->orderItemRepository->save($orderItem);
        return $orderItem;
    }

    /**
     * Remove an item from an order
     *
     * @param Order $order
     * @param OrderItem $orderItem
     * @return void
     * @throws InvalidArgumentException If the order is not in 'new' or 'in_work' status
     */
    public function removeOrderItem(Order $order, OrderItem $orderItem): void
    {
        if (!in_array($order->getStatus(), [OrderStatus::STATUS_NEW, OrderStatus::STATUS_IN_WORK], true)) {
            throw new InvalidArgumentException(
                "Cannot remove items from an order with status '{$order->getStatus()->value}'",
            );
        }

        $order->removeOrderItem($orderItem);
        $this->orderItemRepository->remove($orderItem);
    }

    /**
     * Delete an order
     *
     * @param Order $order
     * @return void
     * @throws InvalidArgumentException If the order is not in 'new' status
     */
    public function deleteOrder(Order $order): void
    {
        if ($order->getStatus() !== OrderStatus::NEW && $order->getStatus() !== OrderStatus::IN_WORK) {
            throw new InvalidArgumentException("Cannot delete an order with status '{$order->getStatus()->value}'");
        }

        $this->orderRepository->remove($order);
    }
}
