<?php

declare(strict_types=1);

namespace Gls\GlsPoland\Controller\Front;

use Gls\GlsPoland\PrestaShop\Checkout\Exception\ValidationException;
use Gls\GlsPoland\PrestaShop\Checkout\Message\UpdateParcelShopChoiceCommand;
use PrestaShop\PrestaShop\Core\CommandBus\CommandBusInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use Symfony\Component\Routing\Annotation\Route;

/**
 * @Route(path="checkout", name="gls_poland_checkout_")
 */
final class CheckoutController
{
    /**
     * @var \Context
     */
    private $context;

    /**
     * @var CommandBusInterface
     */
    private $bus;

    public function __construct(\Context $context, CommandBusInterface $bus)
    {
        $this->context = $context;
        $this->bus = $bus;
    }

    /**
     * @Route(path="/select-parcel-shop", name="select_parcel_shop", methods={"POST"})
     */
    public function selectParcelShop(Request $request): Response
    {
        $data = $this->decodeRequest($request);
        $cart = $this->resolveCart($data);

        if (!$cart->hasProducts()) {
            throw new ConflictHttpException('Cart is empty.');
        }

        if (!isset($data['parcelShopId'])) {
            throw new BadRequestHttpException('Required parameter "parcelShopId" is missing.');
        }

        $carrierReferenceId = null;
        if (isset($data['carrierReferenceId']) && is_scalar($data['carrierReferenceId'])) {
            $carrierReferenceId = (int) $data['carrierReferenceId'];
            if (0 >= $carrierReferenceId) {
                $carrierReferenceId = null;
            }
        }

        $command = new UpdateParcelShopChoiceCommand(
            $cart,
            (string) $data['parcelShopId'],
            $carrierReferenceId
        );

        try {
            $this->bus->handle($command);
        } catch (ValidationException $e) {
            throw new UnprocessableEntityHttpException($e->getMessage(), $e);
        }

        $this->storeLastParcelShopSelection((string) $data['parcelShopId'], $carrierReferenceId, (int) $cart->id);

        return new JsonResponse(null, Response::HTTP_NO_CONTENT);
    }

    private function decodeRequest(Request $request): array
    {
        $data = json_decode($request->getContent(), true);

        if (null === $data && JSON_ERROR_NONE !== json_last_error()) {
            throw new BadRequestHttpException('Could not decode request as JSON.');
        }

        if (!is_array($data)) {
            throw new BadRequestHttpException('Expected the request to decode into an array.');
        }

        return $data;
    }

    private function resolveCart(array $data): \Cart
    {
        if (isset($data['cartId']) && is_scalar($data['cartId'])) {
            $cartId = (int) $data['cartId'];
            if (0 < $cartId) {
                $cart = new \Cart($cartId);
                if (\Validate::isLoadedObject($cart)) {
                    return $cart;
                }
            }
        }

        return $this->context->cart;
    }

    private function storeLastParcelShopSelection(string $parcelShopId, ?int $carrierReferenceId, int $cartId): void
    {
        $payload = json_encode([
            'parcelShopId' => $parcelShopId,
            'carrierReferenceId' => $carrierReferenceId,
            'cartId' => $cartId,
            'ts' => time(),
        ]);

        if (false === $payload) {
            return;
        }

        $this->context->cookie->__set('gls_poland_last_selection', $payload);
    }
}
