<?php

declare(strict_types=1);

namespace App\Service;

use App\Entity\DrinkType;
use App\Enum\SystemSettingKey;
use App\Repository\DrinkTypeRepository;
use InvalidArgumentException;

readonly class DrinkTypeService
{
    public function __construct(
        private DrinkTypeRepository $drinkTypeRepository,
        private ConfigurationService $configService,
    ) {}

    /**
     * Get all drink types
     *
     * @return DrinkType[]
     */
    public function getAllDrinkTypes(): array
    {
        return $this->drinkTypeRepository->findAll();
    }

    /**
     * Get a drink type by ID
     *
     * @param int $id
     * @return DrinkType|null
     */
    public function getDrinkTypeById(int $id): null|DrinkType
    {
        return $this->drinkTypeRepository->find($id);
    }

    /**
     * Get a drink type by name
     *
     * @param string $name
     * @return DrinkType|null
     */
    public function getDrinkTypeByName(string $name): null|DrinkType
    {
        return $this->drinkTypeRepository->findOneBy([
            'name' => $name,
        ]);
    }

    /**
     * Create a new drink type
     *
     * @param string $name
     * @param string|null $description
     * @param int|null $desiredStock
     * @return DrinkType
     * @throws InvalidArgumentException If a drink type with the same name already exists
     */
    public function createDrinkType(
        string $name,
        null|string $description = null,
        null|int $desiredStock = null,
    ): DrinkType {
        // Check if a drink type with the same name already exists
        if (
            $this->drinkTypeRepository->findOneBy([
                'name' => $name,
            ]) !== null
        ) {
            throw new InvalidArgumentException("A drink type with the name '{$name}' already exists");
        }

        // If no desired stock is provided, use the default from configuration
        if ($desiredStock === null) {
            $desiredStock = (int) $this->configService->getConfigByKey(SystemSettingKey::DEFAULT_DESIRED_STOCK);
        }

        $drinkType = new DrinkType($name, $description, $desiredStock);
        $this->drinkTypeRepository->save($drinkType);

        return $drinkType;
    }

    /**
     * Update an existing drink type
     *
     * @param DrinkType $drinkType
     * @param string|null $name
     * @param string|null $description
     * @param int|null $desiredStock
     * @return DrinkType
     * @throws InvalidArgumentException If a drink type with the same name already exists
     */
    public function updateDrinkType(
        DrinkType $drinkType,
        null|string $name = null,
        null|string $description = null,
        null|int $desiredStock = null,
    ): DrinkType {
        // Update name if provided
        if ($name !== null && $name !== $drinkType->getName()) {
            // Check if a drink type with the same name already exists
            if (
                $this->drinkTypeRepository->findOneBy([
                    'name' => $name,
                ]) !== null
            ) {
                throw new InvalidArgumentException("A drink type with the name '{$name}' already exists");
            }
            $drinkType->setName($name);
        }

        // Update description if provided
        if ($description !== null) {
            $drinkType->setDescription($description);
        }

        // Update desired stock if provided
        if ($desiredStock !== null) {
            $drinkType->setDesiredStock($desiredStock);
        }

        $this->drinkTypeRepository->save($drinkType);

        return $drinkType;
    }

    /**
     * Delete a drink type
     *
     * @param DrinkType $drinkType
     * @return void
     */
    public function deleteDrinkType(DrinkType $drinkType): void
    {
        $this->drinkTypeRepository->remove($drinkType);
    }
}
