<?php

declare(strict_types=1);

namespace Gls\GlsPoland\PrestaShop\Hook\Front;

use Gls\GlsPoland\DataProvider\ParcelShopDataProvider;
use Gls\GlsPoland\Entity\OrderParcelShop;
use Gls\GlsPoland\Entity\ShopDeliveryCarrier;
use Gls\GlsPoland\PrestaShop\Helper\CarrierFinder;
use Gls\GlsPoland\PrestaShop\Hook\HookInterface;
use Gls\GlsPoland\Repository\CheckoutSessionRepository;
use Gls\GlsPoland\Repository\OrderParcelShopRepository;

final class ActionValidateOrder implements HookInterface
{
    public const HOOK_NAME = 'actionValidateOrder';

    private $carrierFinder;
    private $checkoutSessionRepository;
    private $parcelShopRepository;
    private $parcelShopDataProvider;

    public function __construct(
        CarrierFinder $carrierFinder,
        CheckoutSessionRepository $checkoutSessionRepository,
        OrderParcelShopRepository $parcelShopRepository,
        ParcelShopDataProvider $parcelShopDataProvider
    ) {
        $this->carrierFinder = $carrierFinder;
        $this->checkoutSessionRepository = $checkoutSessionRepository;
        $this->parcelShopRepository = $parcelShopRepository;
        $this->parcelShopDataProvider = $parcelShopDataProvider;
    }

    /**
     * @param array{cart?: \Cart, order?: \Order} $parameters
     */
    public function execute(array $parameters): void
    {
        $order = $parameters['order'] ?? null;
        $cart = $parameters['cart'] ?? null;

        if (!$order instanceof \Order || !$cart instanceof \Cart) {
            return;
        }

        $carrier = $this->carrierFinder->findOneByCarrierId((int) $order->id_carrier);
        if (!$carrier instanceof ShopDeliveryCarrier) {
            return;
        }

        $parcelShopId = $this->resolveParcelShopId($carrier, $order, $cart);
        if (null === $parcelShopId) {
            return;
        }

        $parcelShop = $this->parcelShopDataProvider->getParcelShop($parcelShopId);
        if (null === $parcelShop) {
            return;
        }

        $entity = $this->parcelShopRepository->find((int) $order->id);
        if ($entity instanceof OrderParcelShop) {
            $entity->updateFromParcelShop($parcelShop);
        } else {
            $entity = new OrderParcelShop((int) $order->id, $parcelShop);
        }

        $this->parcelShopRepository->add($entity);
    }

    private function resolveParcelShopId(ShopDeliveryCarrier $carrier, \Order $order, \Cart $cart): ?string
    {
        $checkoutSession = $this->checkoutSessionRepository->find((int) $cart->id);
        if (null !== $checkoutSession) {
            $parcelShopId = $checkoutSession->getParcelShopId($carrier->getId()->getValue());
            if (null === $parcelShopId && isset($order->id_carrier)) {
                $parcelShopId = $checkoutSession->getParcelShopId((int) $order->id_carrier);
            }

        if (null !== $parcelShopId && '' !== trim((string) $parcelShopId)) {
            return (string) $parcelShopId;
        }
    }

        $parcelShopId = $this->resolveParcelShopIdFromRequest($carrier, $order);
        if (null !== $parcelShopId) {
            return $parcelShopId;
        }

        return $this->resolveParcelShopIdFromCookie($carrier, $order, $cart);
    }

    private function resolveParcelShopIdFromRequest(ShopDeliveryCarrier $carrier, \Order $order): ?string
    {
        $payload = $_REQUEST['gls_poland'] ?? null;
        if (!is_array($payload)) {
            return null;
        }

        $candidateKeys = [];
        if (isset($order->id_carrier)) {
            $candidateKeys[] = (string) (int) $order->id_carrier;
            $candidateKeys[] = (int) $order->id_carrier;
        }
        $candidateKeys[] = (string) $carrier->getId()->getValue();
        $candidateKeys[] = $carrier->getId()->getValue();

        foreach ($candidateKeys as $key) {
            if (!isset($payload[$key]) || !is_array($payload[$key])) {
                continue;
            }

            $parcelShopId = $payload[$key]['parcel_shop_id'] ?? null;
            if (null === $parcelShopId || '' === trim((string) $parcelShopId)) {
                continue;
            }

            return (string) $parcelShopId;
        }

        return null;
    }

    private function resolveParcelShopIdFromCookie(ShopDeliveryCarrier $carrier, \Order $order, \Cart $cart): ?string
    {
        $cookie = \Context::getContext()->cookie;
        if (!isset($cookie->gls_poland_last_selection)) {
            return null;
        }

        $data = json_decode((string) $cookie->gls_poland_last_selection, true);
        if (!is_array($data)) {
            return null;
        }

        $parcelShopId = isset($data['parcelShopId']) ? trim((string) $data['parcelShopId']) : '';
        if ('' === $parcelShopId) {
            return null;
        }

        $timestamp = isset($data['ts']) ? (int) $data['ts'] : 0;
        if (0 < $timestamp && (time() - $timestamp) > 3600) {
            return null;
        }

        $carrierReferenceId = isset($data['carrierReferenceId']) ? (int) $data['carrierReferenceId'] : 0;
        $orderCarrierId = isset($order->id_carrier) ? (int) $order->id_carrier : 0;
        $carrierId = $carrier->getId()->getValue();

        if (
            0 < $carrierReferenceId
            && $carrierReferenceId !== $carrierId
            && $carrierReferenceId !== $orderCarrierId
        ) {
            return null;
        }

        $selectedCartId = isset($data['cartId']) ? (int) $data['cartId'] : 0;
        if (0 < $selectedCartId && $selectedCartId !== (int) $cart->id) {
            // Allow +1 drift for checkouts that duplicate cart around payment confirmation.
            if ($selectedCartId !== ((int) $cart->id + 1) && $selectedCartId !== ((int) $cart->id - 1)) {
                return null;
            }
        }

        return $parcelShopId;
    }
}
