<?php

namespace Comitium5\DesignerBundle\Controller\Page;

use Comitium5\ApiClientBundle\Client\Client;
use Comitium5\ApiClientBundle\Client\Services\PagesApiService;
use Comitium5\ApiClientBundle\Client\Services\SubsiteApiService;
use Comitium5\ApiClientBundle\ValueObject\IdentifiedValue;
use Comitium5\CommonWidgetsBundle\Controller\AbstractWidgetControllerBase;
use Comitium5\CommonWidgetsBundle\Services\GATemplateDataResolver;
use Comitium5\CommonWidgetsBundle\Utils\Common\CommonUtils;
use Comitium5\DesignerBundle\Controller\BaseController;
use Comitium5\DesignerBundle\Manager\Designer\DesignerManager;
use Comitium5\DesignerBundle\Publisher\Page\PagePublisher;
use Comitium5\DesignerBundle\UseCase\Page\AssignTemplateToPageUseCase;
use Comitium5\DesignerBundle\Validator\Page\PageHasRelatedViewConstraint;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Comitium5\DesignerBundle\Model\Interfaces\Page\PageInterface;
use Comitium5\DesignerBundle\ValueObjects\Common\ErrorObject;

/**
 * Class PageController
 *
 * @author Carles Gómez <carles@bab-soft.com>
 * @package Comitium5\DesignerBundle\Controller\Page
 */
class PageController extends BaseController
{
    /**
     * @param Request $request
     * @param int $pageId
     * @param string $locale
     *
     * @return Response
     * @throws \Exception
     */
    public function previewPageAction(Request $request, int $pageId, string $locale): Response
    {
        $page = $this->getPage($pageId);

        $validationObject = $this->validate($page);

        if (!$validationObject->isValid()) {
            return $this->get("common.factory.error_page_response_factory")(
                $validationObject->getErrorCode(),
                $validationObject->getErrorMessage(),
                "common.errors.preview"
            );
        }

        $subsiteProvider = $this->get("designer.provider.subsite");
        $masterRequest = $this->get('request_stack')->getMasterRequest();

        $previewDate = $masterRequest
            ->query
            ->get("previewDate");

        $locale = $this->getLocale($subsiteProvider, $locale);
        $apiV2client = $this->getApiClient($locale);
        $serializedPage = (new PagesApiService($apiV2client))->find(new IdentifiedValue($page->getId()));
        $pageCacheFile = $this->twigFileName($page, $locale);

        $publisher = $this->get("designer.publisher.page");

        if (!empty($previewDate)) {
            try {
                $previewDate = new \DateTime($previewDate);
                $designerManager = $publisher->getResolver()->getDesignerManager();
                $designerManager->setDateFieldValue($previewDate);
            } catch (\Throwable $e) {
            }
        }

        $this->publishTmpPage($publisher, $page, $subsiteProvider, $locale);
        $this->addMetaDataOnRequest($serializedPage, $request);
        $this->addGAOnRequest($serializedPage, $subsiteProvider, $apiV2client, $request);
        $this->addPageOnRequest($masterRequest, $serializedPage);
        $this->addSubSiteOnRequest($apiV2client, $subsiteProvider, $masterRequest);

        return $this->render(sprintf(DesignerManager::CACHE_TWIG_PATH, $pageCacheFile));
    }

    /**
     * @param Request $request
     * @param string $contentType
     * @param int $contentId
     * @param string $locale
     * @param int $pageId
     *
     * @return Response|null
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @throws \Exception
     */
    public function previewContentAction(Request $request, $contentType, $contentId, $locale, $pageId): Response
    {
        $page = $this->getPage($pageId);

        $validationObject = $this->validate($page);

        if (!$validationObject->isValid()) {
            return $this->get("common.factory.error_page_response_factory")(
                $validationObject->getErrorCode(),
                $validationObject->getErrorMessage(),
                "common.errors.preview"
            );
        }

        $subsiteProvider = $this->get("designer.provider.subsite");
        $masterRequest = $this->get('request_stack')->getMasterRequest();

        $previewDate = $masterRequest
            ->query
            ->get("previewDate");

        $locale = $this->getLocale($subsiteProvider, $locale);
        $apiV2client = $this->getApiClient($locale);

        $pageCacheFile = $this->twigFileName($page, $locale);
        $publisher = $this->get("designer.publisher.page");

        if (!empty($previewDate)) {
            try {
                $previewDate = new \DateTime($previewDate);
                $designerManager = $publisher->getResolver()->getDesignerManager();
                $designerManager->setDateFieldValue($previewDate);
            } catch (\Throwable $e) {
            }
        }

        $entity = $this
            ->get("designer.manager.page")
            ->fetchEntityByPageType(
                $contentId,
                $contentType
            );

        $this->publishTmpPage($publisher, $page, $subsiteProvider, $locale);
        $this->addEntityOnRequest($masterRequest, $entity);
        $this->addMetaDataOnRequest($entity, $request);
        $this->addGAOnRequest($entity, $subsiteProvider, $apiV2client, $request);

        $pageToRequest = (new PagesApiService($apiV2client))->find(new IdentifiedValue($page->getId()));

        $this->addPageOnRequest($masterRequest, $pageToRequest);
        $this->addSubSiteOnRequest($apiV2client, $subsiteProvider, $masterRequest);

        return $this->render(sprintf(DesignerManager::CACHE_TWIG_PATH, $pageCacheFile));
    }

    /**
     * @param $locale
     *
     * @return \Comitium5\ApiClientBundle\Client\Client
     */
    private function getApiClient($locale): \Comitium5\ApiClientBundle\Client\Client
    {
        $apiV2client = $this
            ->get("designer.api")
            ->buildClient(
                $this->getParameter("comitium5_api_client.site"),
                $this->getParameter("comitium5_api_client.subsite"),
                $locale->getCode(),
                $this->getParameter("comitium5_api_client.v2_token")
            );

        return $apiV2client;
    }

    /**
     * @param Request|null $masterRequest
     * @param array $entity
     */
    private function addEntityOnRequest(?Request $masterRequest, array $entity): void
    {
        $masterRequest->attributes->set("_entity", $entity);
    }

    /**
     * @param int $pageId
     *
     * @return PageInterface|null
     */
    private function getPage(int $pageId): ?PageInterface
    {
        return $this
            ->get("designer.manager.designer")
            ->getPage($pageId);
    }

    /**
     * @param PageInterface|null $page
     *
     * @return ErrorObject
     */
    private function validate(?PageInterface $page = null): ErrorObject
    {
        $pageValidator = $this->get("page.resolver.page_validator");

        return $pageValidator($page, [
            new PageHasRelatedViewConstraint(),
        ]);
    }

    /**
     * @param PageInterface $page
     * @param $locale
     *
     * @return string
     */
    private function twigFileName(PageInterface $page, $locale): string
    {
        return "{$page->getOriginId()}_{$locale->getCode()}.html.twig";
    }

    /**
     * @param array $serializedPage
     * @param Request $request
     */
    private function addMetaDataOnRequest(array $serializedPage, Request $request): void
    {
        $metaData = CommonUtils::resolveMetaDataV2($serializedPage);

        $request
            ->attributes
            ->set("_metaData", $metaData);
    }

    /**
     * @param array $entity
     * @param object $subsiteProvider
     * @param Client $apiV2client
     * @param Request $request
     *
     * @throws \Exception
     */
    private function addGAOnRequest(
        array $entity,
        object $subsiteProvider,
        Client $apiV2client,
        Request $request
    ): void {
        $gaResolver = new GATemplateDataResolver(
            $entity,
            $subsiteProvider
                ->getSubsite()
                ->getId(),
            2,
            $apiV2client
        );

        $request
            ->attributes
            ->set("_ga_dimensions", $gaResolver->resolve());
    }

    /**
     * @param PagePublisher $publisher
     * @param PageInterface|null $page
     * @param object $subsiteProvider
     * @param $locale
     *
     * @throws \Exception
     */
    private function publishTmpPage(
        PagePublisher $publisher,
        ?PageInterface $page,
        object $subsiteProvider,
        $locale
    ): void {
        $publisher
            ->publishTmp(
                $page,
                $subsiteProvider->getSubsite(),
                $locale
            );
    }

    /**
     * @param Request|null $masterRequest
     * @param array $pageToRequest
     */
    private function addPageOnRequest(?Request $masterRequest, array $pageToRequest): void
    {
        $masterRequest
            ->attributes
            ->set(AbstractWidgetControllerBase::PAGE_REQUEST_KEY, $pageToRequest);
    }

    /**
     * @param Client $apiV2client
     * @param object $subsiteProvider
     * @param Request|null $masterRequest
     *
     * @throws \Exception
     */
    private function addSubSiteOnRequest(
        Client $apiV2client,
        object $subsiteProvider,
        ?Request $masterRequest
    ): void {
        $subsiteToRequest = (new SubsiteApiService($apiV2client))->find(new IdentifiedValue(
                $subsiteProvider
                    ->getSubsite()
                    ->getId())
        );

        $masterRequest
            ->attributes
            ->set(AbstractWidgetControllerBase::SUBSITE_REQUEST_KEY, $subsiteToRequest);
    }

    /**
     * @param object $subsiteProvider
     * @param string $locale
     *
     * @return mixed
     */
    private function getLocale(object $subsiteProvider, string $locale)
    {
        $locale = $subsiteProvider->getSubsiteLocaleFromAttr("code", $locale);

        if ($locale === null) {
            throw $this->createNotFoundException("Locale not found");
        }

        return $locale;
    }

    /**
     * @param int $pageId
     * @param int $templateId
     *
     * @return JsonResponse
     */
    public function assignTemplateAction(int $pageId, int $templateId): JsonResponse
    {
        $assignTemplateUseCase = new AssignTemplateToPageUseCase(
            $this->get("designer.repository.page"),
            $this->get("designer.repository.template")
        );

        return $assignTemplateUseCase($pageId, $templateId);
    }
}
