<?php

declare(strict_types=1);

namespace Gls\GlsPoland\PrestaShop\Checkout;

use Gls\GlsPoland\Entity\Carrier;
use Gls\GlsPoland\PrestaShop\Checkout\Presenter\CheckoutConfigPresenter;
use Gls\GlsPoland\PrestaShop\Helper\CarrierFinder;
use Gls\GlsPoland\PrestaShop\Templating\RendererInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

abstract class AbstractCheckout implements CheckoutInterface
{
    use CarriersByDeliveryOptionFinderTrait;
    use CheckoutTrait;

    public const MAP_SDK_JS_URI = 'https://mapa.gls-poland.com/js/v4.0/maps_sdk.js';
    /**
     * @deprecated since 1.1.0
     */
    public const MAP_SDK_CSS_URI = 'https://mapa.szybkapaczka.pl/css/v3.1/maps_sdk.css';

    private $validator;
    private $persister;
    private $logger;

    public function __construct(RendererInterface $renderer, CheckoutConfigPresenter $dataPresenter, CarrierFinder $carrierFinder, DeliveryOptionValidatorInterface $validator, DeliveryOptionDataPersisterInterface $persister, LoggerInterface $logger)
    {
        $this->renderer = $renderer;
        $this->dataPresenter = $dataPresenter;
        $this->carrierFinder = $carrierFinder;
        $this->validator = $validator;
        $this->persister = $persister;
        $this->logger = $logger;
    }

    protected function validateDeliveryOption(\Cart $cart, array $requestParameters): bool
    {
        if (!isset($requestParameters['delivery_option']) || !is_array($requestParameters['delivery_option'])) {
            $this->logger->error('GLS checkout validation failed: malformed delivery_option payload.', [
                'cartId' => (int) $cart->id,
                'addressId' => (int) $cart->id_address_delivery,
                'requestParameters' => $requestParameters,
            ]);
            $this->addError($this->getTranslator()->trans('Malformed request.', [], 'Modules.Glspoland.Checkout'));

            return false;
        }

        if ([] === $carriers = $this->getCarriersByDeliveryOption($cart, $requestParameters['delivery_option'])) {
            $this->logger->warning('GLS checkout validation found no GLS carriers for the provided delivery option.', [
                'cartId' => (int) $cart->id,
                'addressId' => (int) $cart->id_address_delivery,
                'deliveryOption' => $requestParameters['delivery_option'],
            ]);

            return true;
        }

        foreach ($carriers as $carrierId => $carrier) {
            if (!$this->validateCarrierData($carrier, $requestParameters['gls_poland'][$carrierId] ?? null)) {
                $this->logger->error('GLS checkout validation failed for carrier data.', [
                    'cartId' => (int) $cart->id,
                    'addressId' => (int) $cart->id_address_delivery,
                    'carrierId' => $carrierId,
                    'deliveryOption' => $requestParameters['delivery_option'],
                    'carrierData' => $requestParameters['gls_poland'][$carrierId] ?? null,
                    'glsPoland' => $requestParameters['gls_poland'] ?? null,
                ]);

                return false;
            }
        }

        return true;
    }

    protected function saveDeliveryOption(\Cart $cart, Request $request): void
    {
        if (!$request->request->has('delivery_option')) {
            $this->logger->warning('GLS checkout save skipped because delivery_option is missing from request payload.', [
                'cartId' => (int) $cart->id,
                'addressId' => (int) $cart->id_address_delivery,
                'requestUri' => $request->getRequestUri(),
                'requestParameters' => $request->request->all(),
            ]);

            return;
        }

        $deliveryOption = $request->request->all('delivery_option');
        $data = $request->request->all('gls_poland');
        $carriers = $this->getCarriersByDeliveryOption($cart, $deliveryOption);
        try {
            $this->persister->save($cart, $carriers, $data);
        } catch (\Throwable $e) {
            $this->logger->error('Failed to save GLS delivery option during checkout.', [
                'cartId' => (int) $cart->id,
                'deliveryOption' => $deliveryOption,
                'glsPolandData' => $data,
                'carrierIds' => array_keys($carriers),
                'requestUri' => $request->getRequestUri(),
                'exception' => $e,
            ]);

            throw $e;
        }
    }

    protected function logCheckoutError(string $message, array $context = []): void
    {
        $this->logger->error($message, $context);
    }

    private function validateCarrierData(Carrier $carrier, $data): bool
    {
        try {
            $violations = $this->validator->validate($carrier, $data);
        } catch (UnexpectedTypeException $e) {
            $this->logger->error('GLS checkout validation failed due to unexpected carrier data type.', [
                'carrierClass' => get_class($carrier),
                'carrierData' => $data,
                'exception' => $e,
            ]);
            $this->addError($this->getTranslator()->trans('Malformed request.', [], 'Modules.Glspoland.Checkout'));

            return false;
        }

        if (0 === count($violations)) {
            return true;
        }

        $messages = [];
        foreach ($violations as $violation) {
            $messages[] = $violation->getMessage();
        }

        $this->logger->error('GLS checkout validation returned violations.', [
            'carrierClass' => get_class($carrier),
            'carrierData' => $data,
            'violations' => $messages,
        ]);

        foreach ($violations as $violation) {
            $this->addError($violation->getMessage());
        }

        return false;
    }
}
