<?php

/**
 * Copyright © Resurs Bank AB. All rights reserved.
 * See LICENSE for license details.
 */

declare(strict_types=1);

namespace Resursbank\MerchantApi\Controller\Admin\Order;

use Configuration;
use InvalidArgumentException;
use JsonException;
use Order;
use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
use PrestaShopDatabaseException;
use PrestaShopException;
use ReflectionException;
use Resursbank\Ecom\Exception\ApiException;
use Resursbank\Ecom\Exception\AttributeCombinationException;
use Resursbank\Ecom\Exception\AuthException;
use Resursbank\Ecom\Exception\ConfigException;
use Resursbank\Ecom\Exception\CurlException;
use Resursbank\Ecom\Exception\Validation\EmptyValueException;
use Resursbank\Ecom\Exception\Validation\IllegalTypeException;
use Resursbank\Ecom\Exception\Validation\IllegalValueException;
use Resursbank\Ecom\Exception\Validation\NotJsonEncodedException;
use Resursbank\Ecom\Exception\ValidationException;
use Resursbank\Ecom\Lib\Model\Payment;
use Resursbank\Ecom\Lib\Validation\StringValidation;
use Resursbank\Ecom\Module\Payment\Repository;
use Resursbank\MerchantApi\Config\Config;
use Resursbank\MerchantApi\Exception\InvalidDataException;
use Resursbank\MerchantApi\Repository\ResursbankOrder;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

/**
 * Basic functionality for all after-shop calls.
 */
abstract class Aftershop extends FrameworkBundleAdminController
{
    /**
     * @param Config $config
     * @param SessionInterface $session
     * @param RequestStack $requestStack
     * @param ResursbankOrder $repository
     */
    public function __construct(
        protected readonly Config $config,
        protected readonly SessionInterface $session,
        protected readonly RequestStack $requestStack,
        protected readonly ResursbankOrder $repository
    ) {
        parent::__construct();
    }

    /**
     * Redirect client back to the order view (or fallback to order list if referrer isn't set).
     */
    protected function redirectBack(): RedirectResponse
    {
        $referer = $this->requestStack->getCurrentRequest()->headers->get(key: 'referer');
        return $this->redirect(url: $referer ?? $this->generateUrl(route: 'admin_orders_index'));
    }

    /**
     * @param Request $request
     * @return Payment
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     * @throws JsonException
     * @throws ReflectionException
     * @throws ApiException
     * @throws AttributeCombinationException
     * @throws AuthException
     * @throws ConfigException
     * @throws CurlException
     * @throws ValidationException
     * @throws EmptyValueException
     * @throws IllegalTypeException
     * @throws IllegalValueException
     * @throws NotJsonEncodedException
     */
    protected function getMapiPayment(Request $request): Payment
    {
        $id = (string) $request->query->get(key: 'payment_id');

        if ($id === '') {
            throw new IllegalValueException(message: 'Missing payment id.');
        }

        (new StringValidation())->isUuid(value: $id);

        return Repository::get(paymentId: $id);
    }

    /**
     * Update Prestashop Order status.
     *
     * @throws NotJsonEncodedException
     * @throws ValidationException
     * @throws CurlException
     * @throws AttributeCombinationException
     * @throws IllegalValueException
     * @throws PrestaShopDatabaseException
     * @throws IllegalTypeException
     * @throws EmptyValueException
     * @throws AuthException
     * @throws JsonException
     * @throws ConfigException
     * @throws PrestaShopException
     * @throws ReflectionException
     * @throws ApiException
     */
    protected function setOrderStatus(Request $request, string $status)
    {
        $order = $this->getOrder(request: $request);

        // Validate order, set new status, and save.
        $order->setCurrentState(id_order_state: Configuration::get(key: $status));
        $order->save();
    }

    /**
     * Resolve Prestashop Order object from MAPI ID in request.
     *
     * @param Request $request
     * @return Order
     * @throws ApiException
     * @throws AttributeCombinationException
     * @throws AuthException
     * @throws ConfigException
     * @throws CurlException
     * @throws EmptyValueException
     * @throws IllegalTypeException
     * @throws IllegalValueException
     * @throws JsonException
     * @throws NotJsonEncodedException
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     * @throws ReflectionException
     * @throws ValidationException
     */
    protected function getOrder(Request $request): Order
    {
        $payment = $this->getMapiPayment(request: $request);
        $order = $this->repository->findByMapiId(mapiId: $payment->id);

        if (!$order->getOrderId()) {
            throw new InvalidArgumentException(message: "Resursbank order not found for payment $payment->id.");
        }

        // Load Order
        $order = new Order(id: $order->getOrderId());

        if (!$order->id) {
            throw new InvalidArgumentException(message: "Order not found for payment $payment->id.");
        }

        return $order;
    }

}
