<?php

namespace KupShop\IncomakerBundle\Util;

use KupShop\IncomakerBundle\Util\XMLElements\CategoryXMLElement;
use KupShop\IncomakerBundle\Util\XMLElements\ContactXMLElement;
use KupShop\IncomakerBundle\Util\XMLElements\CouponTemplateXMLElement;
use KupShop\IncomakerBundle\Util\XMLElements\CouponXMLElement;
use KupShop\IncomakerBundle\Util\XMLElements\IncomakerXMLInterface;
use KupShop\IncomakerBundle\Util\XMLElements\InfoXMLElement;
use KupShop\IncomakerBundle\Util\XMLElements\OrderXMLElement;
use KupShop\IncomakerBundle\Util\XMLElements\ProductXMLElement;
use KupShop\IncomakerBundle\Util\XMLElements\XMLElementInterface;
use KupShop\KupShopBundle\Context\ContextManager;
use KupShop\KupShopBundle\Context\CountryContext;
use KupShop\KupShopBundle\Context\CurrencyContext;
use KupShop\KupShopBundle\Context\VatContext;
use KupShop\KupShopBundle\Util\Contexts;
use Psr\Container\ContainerInterface;
use Query\Operator;
use Query\QueryBuilder;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\Service\ServiceSubscriberInterface;

class XMLResponse implements ServiceSubscriberInterface
{
    /** @var XMLElementInterface */
    protected $xmlElement;

    /** @var ContainerInterface */
    private $locator;

    /** @var VatContext */
    private $vatContext;

    public function __construct(ContainerInterface $locator, VatContext $vatContext)
    {
        $this->locator = $locator;
        $this->vatContext = $vatContext;
    }

    /** @var ContextManager */
    protected $contextManager;

    /** @required */
    public function setContextManager(ContextManager $contextManager)
    {
        $this->contextManager = $contextManager;
    }

    public function getResponse(Request $request, $type)
    {
        if ($this->locator->has($type)) {
            $this->xmlElement = $this->locator->get($type);

            $countryContext = Contexts::get(CountryContext::class);
            $countries = $countryContext->getAll();
            $country = $this->vatContext->getHomeCountry() ?? 'CZ';
            if (!array_key_exists($country, $countries)) {
                $countries = array_keys($countries);
                $country = reset($countries);
            }
            $currency = Contexts::get(CurrencyContext::class)->getDefaultId();
            $this->contextManager->forceEmptyContexts();
            $output = $this->contextManager->activateContexts(
                [
                    CountryContext::class => $country,
                    CurrencyContext::class => $currency,
                ],
                function () use ($request) {
                    // pokud uz pouzivam IncomakerXMLElementInterface, tak filtry definuju uvnitr servisy, aby tady v `applyRequestParams` nemuselo byt X ifu
                    if ($this->xmlElement instanceof IncomakerXMLInterface) {
                        return $this->xmlElement->getXMLOutput($request);
                    }

                    return $this->xmlElement->getXMLOutput($this->applyRequestParams($request));
                });

            return new Response($output, 200, ['Content-Type' => 'text/xml']);
        }

        return new Response(null, 400);
    }

    public function applyRequestParams(Request $request)
    {
        return function (QueryBuilder $qb) use ($request) {
            $from = $qb->getQueryPart('from')[0] ?? [];
            $id_field = $from['alias'].'.id';
            if ($id = $request->query->get('id')) {
                if ($request->query->get('type') == 'order') {
                    $id_field = $from['alias'].'.order_no';
                }
                $qb->andWhere(Operator::equals([$id_field => $id]));
                if (($request->query->get('type') == 'coupon') && ($downloadCount = $request->query->get('downloadCount'))) {
                    $this->xmlElement->setGenerateCount($downloadCount);
                }
            } else {
                $qb->orderBy($id_field, 'ASC');
                if ($limit = $request->query->get('limit')) {
                    $qb->setMaxResults($limit);
                    if ($offset = $request->query->get('offset')) {
                        $qb->setFirstResult($offset);
                    }
                }
                if (($since = $request->query->get('since')) && ($from['table'] != 'sections') && ($from['table'] != 'discounts')) {
                    if (\DateTime::createFromFormat('Y-m-d', $since)) {
                        $date_field = $from['alias'].'.date_updated';
                        if ($from['table'] == 'products') {
                            $date_field = "GREATEST(COALESCE(pv.updated, DATE('2000-01-01')), p.updated)";
                        }
                        $qb->andWhere($date_field." >= '{$since}'");
                    }
                }
            }
        };
    }

    /**
     * Returns an array of service types required by such instances, optionally keyed by the service names used internally.
     *
     * For mandatory dependencies:
     *
     *  * array('logger' => 'Psr\Log\LoggerInterface') means the objects use the "logger" name
     *    internally to fetch a service which must implement Psr\Log\LoggerInterface.
     *  * array('Psr\Log\LoggerInterface') is a shortcut for
     *  * array('Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface')
     *
     * otherwise:
     *
     *  * array('logger' => '?Psr\Log\LoggerInterface') denotes an optional dependency
     *  * array('?Psr\Log\LoggerInterface') is a shortcut for
     *  * array('Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface')
     *
     * @return array The required service types, optionally keyed by service names
     */
    public static function getSubscribedServices(): array
    {
        return [
            'contact' => ContactXMLElement::class,
            'category' => CategoryXMLElement::class,
            'product' => ProductXMLElement::class,
            'order' => OrderXMLElement::class,
            'couponTemplate' => CouponTemplateXMLElement::class,
            'coupon' => CouponXMLElement::class,
            'info' => InfoXMLElement::class,
        ];
    }
}
