<?php
/**
 * Copyright since 2025 InPost S.A.
 *
 * For the full license information, please view the LICENSE file bundled with the module.
 *
 * @author InPost S.A.
 * @copyright since 2025 InPost S.A.
 * @license MIT
 */

declare(strict_types=1);

namespace InPost\International\Checkout\Module;

use InPost\International\Asset\Assets;
use InPost\International\Checkout\AbstractCheckout;
use InPost\International\Checkout\CheckoutSessionRepositoryInterface;
use InPost\International\Checkout\Event\StepCompleteValidationEvent;
use InPost\International\Checkout\ModuleCheckoutInterface;
use InPost\International\Delivery\Util\CarrierFinder;
use InPost\International\Entity\Carrier;
use InPost\International\Entity\PointDeliveryCarrier;
use InPost\International\PrestaShop\Module\Util\ControllerChecker;
use Symfony\Component\HttpFoundation\Request;

if (!defined('_PS_VERSION_')) {
    exit;
}

final class TheCheckout extends AbstractCheckout implements ModuleCheckoutInterface
{
    private const MODULE_NAME = 'thecheckout';

    public static function getModuleName(): string
    {
        return self::MODULE_NAME;
    }

    public static function supports(\FrontControllerCore $controller, Request $request): bool
    {
        return ControllerChecker::isModuleController($controller, self::MODULE_NAME, [
            'front',
            'order',
            'checkout',
        ]);
    }

    public static function getSubscribedServices(): array
    {
        return parent::getSubscribedServices() + [
            CarrierFinder::class,
            CheckoutSessionRepositoryInterface::class,
        ];
    }

    public static function getSubscribedEvents(): array
    {
        return [
            StepCompleteValidationEvent::NAME => 'onValidateStepComplete',
        ];
    }

    public function onValidateStepComplete(StepCompleteValidationEvent $event): void
    {
        $cart = $event->getCart();
        $deliveryOption = $cart->getDeliveryOption();

        if ([] === $carriers = $this->get(CarrierFinder::class)->findByDeliveryOption($deliveryOption)) {
            throw new \LogicException('The selected delivery option in not associated with an InPost carrier.');
        }

        try {
            $address = $this->getDeliveryAddress($cart, $event->getRequestParameters());

            if (!$this->validatePhoneNumber($address)) {
                $event->setUncompleted();
            }

            foreach ($carriers as $carrier) {
                if (!$this->validateDeliveryOptions($carrier, $cart)) {
                    $event->setUncompleted();

                    return;
                }
            }
        } catch (\Exception $e) {
            $event->setUncompleted();
            $this->context->controller->errors[] = $this->context->getTranslator()->trans('An error occurred while processing your request.', [], 'Modules.Inpostinternational.Checkout');

            throw $e;
        }
    }

    protected function addCheckoutAssets(Assets $assets): void
    {
        $assets->addJavaScript('js/front/the-checkout.js');
    }

    /**
     * @param array<string, mixed> $request request parameters
     */
    protected function getDeliveryAddress(\Cart $cart, array $request = []): \Address
    {
        if (null !== $address = $this->getAddressFromRequestData($request)) {
            return $address;
        }

        return parent::getDeliveryAddress($cart);
    }

    /**
     * @return bool whether the selected delivery options are valid
     */
    private function validateDeliveryOptions(Carrier $carrier, \Cart $cart): bool
    {
        if (!$carrier instanceof PointDeliveryCarrier) {
            return true;
        }

        if ($this->isDeliveryPointSelected($carrier, (int) $cart->id)) {
            return true;
        }

        $this->context->controller->errors[] = $this->context->getTranslator()->trans('Please select a pickup point.', [], 'Modules.Inpostinternational.Validators');

        return false;
    }

    private function isDeliveryPointSelected(PointDeliveryCarrier $carrier, int $cartId): bool
    {
        if (null === $session = $this->get(CheckoutSessionRepositoryInterface::class)->find($cartId)) {
            return false;
        }

        $carrierId = $carrier->getReferenceId();

        return null !== $session->getPointId($carrierId);
    }

    /**
     * @param array<string, mixed> $request request parameters
     */
    private function getAddressFromRequestData(array $request): ?\Address
    {
        if (!empty($request['deliveryVisible']) && !empty($request['delivery'])) {
            parse_str($request['delivery'], $addressData);
        } elseif (!empty($request['invoiceVisible']) && !empty($request['invoice'])) {
            parse_str($request['invoice'], $addressData);
        } else {
            return null;
        }

        $address = new \Address();

        foreach ($addressData as $name => $value) {
            if (!property_exists($address, $name)) {
                continue;
            }

            $address->{$name} = $value;
        }

        return $address;
    }
}
