<?php

namespace Comitium5\DesignerBundle\Controller\Designer;

use Comitium5\ApiClientBundle\Client\Services\BlockContentApiService;
use Comitium5\ApiClientBundle\Client\Services\PagesApiService;
use Comitium5\ApiClientBundle\ValueObject\IdentifiedValue;
use Comitium5\ApiClientBundle\ValueObject\ParametersValue;
use Comitium5\DesignerBundle\Controller\BaseController;
use Comitium5\DesignerBundle\Entity\Locale;
use Comitium5\DesignerBundle\Entity\Page;
use Comitium5\DesignerBundle\Model\Interfaces\LocaleInterface;
use Comitium5\DesignerBundle\Model\Interfaces\Page\PageInterface;
use Comitium5\DesignerBundle\Service\BlockContent\BlockContentService;
use Comitium5\DesignerBundle\ValueObjects\Common\ErrorObject;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * Class DesignerController
 *
 * @author Carles Gómez <carles@bab-soft.com>
 * @package Comitium5\DesignerBundle\Controller\Designer
 */
class DesignerController extends BaseController
{
    /**
     * @param int $pageId
     * @param Request $request
     *
     * @return Response
     * @throws \Exception
     */
    public function showPageAction(Request $request, $pageId)
    {
        $page = null;
        $template = null;
        $breakpoints = null;
        $widgets = [];

        $locale = $request->get("locale");

        $manager = $this->get('designer.manager.designer');

        $hasAssignedTemplate = false;

        $temlateRepo = $this->get("designer.repository.template");

        if (!empty($manager) && $pageId > 0) {
            $page = $manager->getPage($pageId);

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

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

            $template = $page->getTemplate();

            if (!empty($template)) {
                $hasAssignedTemplate = true;

                $template->setWidth(
                    $template->getWidth(true)
                );
                $template->setColumns(
                    $template->getColumns(true)
                );
                $template->setGutterWidth(
                    $template->getGutterWidth(true)
                );
                $template->setCss(
                    $template->getCss(true, true)
                );
                $template->setLibraries(
                    $template->getLibraries(true, true)
                );
                $breakpoints = $manager->getBreakpoints($template);
            }

            $this->hydratePage($page);

            $widgets = array_reverse(
                $manager->getWidgets("", $this->getUser(), false)
            );
        }

        return $this->render(
            'Comitium5DesignerBundle:Designer:designer-page.html.twig',
            [
                "hasAssignedTemplate" => $hasAssignedTemplate,
                "page"                => $page,
                "template"            => $template,
                "breakpoints"         => $breakpoints,
                "templatesList"       => $temlateRepo->findAll(),
                "jsonUrl"             => $this->generateUrl(
                    'designer_generate_json_page',
                    ['pageId' => $pageId, "locale" => $locale]
                ),
                "widgetsUrl"          => $this->generateUrl(
                    'designer_page_widgets',
                    ['pageId' => $pageId]
                ),
                'widgets'             => $widgets,
            ]
        );
    }

    /**
     * @param  Request $request
     * @param  int $pageId
     *
     * @return JsonResponse
     */
    public function generateJsonPageAction(Request $request, $pageId)
    {
        $status = Response::HTTP_NOT_FOUND;
        $data = '';

        try {
            $locale = $request->get('locale');
            $manager = $this->get('designer.manager.designer');
            $subsiteProvider = $this->get('designer.provider.subsite');

            if (!empty($manager) && $pageId > 0) {
                $page = $manager->getPage($pageId);
                $page->getTemplate()->setBreakpoints(
                    $page->getTemplate()->getBreakpoints(true)
                );
                $page->getTemplate()->setWidth(
                    $page->getTemplate()->getWidth(true)
                );
                $page->getTemplate()->setColumns(
                    $page->getTemplate()->getColumns(true)
                );
                $page->getTemplate()->setGutterWidth(
                    $page->getTemplate()->getGutterWidth(true)
                );
                $data = $page->toArray([], ['parent', 'children', 'categories', 'tags'], [
                    'layoutPages',
                    'template',
                ]);
                $data["template"]['locales'] = [];

                $subsite = $subsiteProvider->getSubSite();

                /**
                 * @var Locale $localeDomain
                 */
                foreach ($subsite->getLocaleDomains() as $localeDomain) {
                    $localeData = [
                        "default"        => $localeDomain->isDefault(),
                        "id"             => $localeDomain->getId(),
                        "locale"         => $localeDomain->getCode(),
                        "subsite"        => $subsite->getId(),
                        "subsiteDefault" => $subsite->isDefault(),
                    ];

                    if (
                        ($localeDomain->isDefault() && empty($locale))
                        || ($locale == $localeDomain->getCode())
                    ) {
                        $data["template"]['defaultLocale'] = $localeData;
                    } else {
                        $data["template"]['locales'][] = $localeData;
                    }
                }

                $status = Response::HTTP_OK;
            }
        } catch (\Throwable $e) {
            if ($this->container->getParameter("kernel.environment") !== "prod") {
                return new JsonResponse(['message' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
            }
        }

        return new JsonResponse($data, $status);
    }

    /**
     * @param  Request $request
     * @param  int $pageId
     *
     * @return Response
     */
    public function generatePageAction(Request $request, $pageId)
    {
        $status = Response::HTTP_NOT_FOUND;
        $code = '';

        try {
            $manager = $this->get('designer.manager.designer');
            $metaCode = (intval($request->get('metaCode', 0)) == 1);
            if (!empty($manager) && $pageId > 0) {
                $page = $manager->getPage($pageId);
                $code = $manager->generateHtmlPage($page, $metaCode);

                $status = Response::HTTP_OK;
            }
        } catch (\Throwable $e) {
            if ($this->container->getParameter("kernel.environment") !== "prod") {
                return new Response($e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR);
            }
        }

        return new Response($code, $status);
    }

    /**
     * @param  Request $request
     * @param  int $pageId
     *
     * @return Response
     */
    public function savePageAction(Request $request, $pageId)
    {
        $status = Response::HTTP_NOT_FOUND;

        try {
            $content = $request->get('content', '');
            $manager = $this->get('designer.manager.designer');
            $subsiteProvider = $this->get('designer.provider.subsite');
            $pagesApiSrv = new PagesApiService($this->get("designer.api"));


            if (!empty($manager) && $pageId > 0 && !empty($content)) {
                $success = $manager->saveContent(
                    json_decode($content, true), null, $pageId
                );

                $publish = (intval($request->get('publish', 0)) == 1);

                /** @var Page $page */
                $page = $manager->getPage($pageId);

                $success = !empty($page);

                if ($success && $publish) {
                    $localeAcronym =   $request->get("locale");

                    /** @var LocaleInterface $locale */
                    $locale = null;

                    if (!empty($localeAcronym)) {
                        foreach ($subsiteProvider->getSubSite()->getLocaleDomains() as $localeDomain) {
                            if (
                                ($localeDomain->isDefault() && empty($localeAcronym))
                                || ($localeAcronym == $localeDomain->getCode())
                            ) {
                                $locale = $localeDomain;
                            }
                        }
                    }

                    if (empty($localeAcronym) || !empty($locale)) {
                        $pagesApiSrv->publish(
                            new IdentifiedValue($page->getOriginId()),
                            new ParametersValue([
                                "locale" => $locale ? $locale->getCode()  : null
                            ])
                        );
                    }

                } elseif ($success && !$publish) {
                    $page->setUpdatedAt(new \DateTime());
                    $success = $this->get("designer.manager.page")->save($page);
                }

                if ($success) {
                    $status = Response::HTTP_OK;
                }
            }
        } catch (\Throwable $e) {
            if ($this->container->getParameter("kernel.environment") !== "prod") {
                return new Response($e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR);
            }
        }

        return new Response('', $status);
    }

    /**
     * @param $templateId
     * @param Request $request
     *
     * @return Response
     * @throws \Exception
     */
    public function showTemplateAction(Request $request, $templateId)
    {
        $template = null;
        $breakpoints = null;
        $widgets = [];

        try {
            $locale = $request->get("locale");
            $manager = $this->get('designer.manager.designer');
            if (!empty($manager) && $templateId > 0) {
                $template = $manager->getTemplate($templateId);
                $template->setWidth(
                    $template->getWidth(true)
                );
                $template->setColumns(
                    $template->getColumns(true)
                );
                $template->setGutterWidth(
                    $template->getGutterWidth(true)
                );
                $template->setCss(
                    $template->getCss(true, true)
                );
                $template->setLibraries(
                    $template->getLibraries(true, true)
                );
                $breakpoints = $manager->getBreakpoints($template);
            }
        } catch (\Throwable $e) {
            throw $e;
        }

        return $this->render(
            'Comitium5DesignerBundle:Designer:designer-template.html.twig',
            [
                "template"    => $template,
                "breakpoints" => $breakpoints,
                "jsonUrl"     => $this->generateUrl(
                    'designer_generate_json_template',
                    ['templateId' => $templateId, "locale" => $locale]
                ),
                "widgetsUrl"  => $this->generateUrl(
                    'designer_template_widgets',
                    ['templateId' => $templateId]
                ),
                'widgets'     => $widgets,
            ]
        );
    }

    /**
     * @param  Request $request
     * @param  int $templateId
     *
     * @return JsonResponse
     */
    public function generateJsonTemplateAction(Request $request, $templateId)
    {
        $status = Response::HTTP_NOT_FOUND;
        $data = '';

        try {
            $locale = $request->get('qa', '');
            $manager = $this->get('designer.manager.designer');
            $subsiteProvider = $this->get('designer.provider.subsite');

            if (!empty($manager) && $templateId > 0) {
                $template = $manager->getTemplate($templateId);
                $template->setBreakpoints(
                    $template->getBreakpoints(true)
                );
                $template->setWidth(
                    $template->getWidth(true)
                );
                $template->setColumns(
                    $template->getColumns(true)
                );
                $template->setGutterWidth(
                    $template->getGutterWidth(true)
                );

                $data = $template->toArray();
                $data['locales'] = [];

                $subsite = $subsiteProvider->getSubSite();

                foreach ($subsite->getLocaleDomains() as $localeDomain) {
                    $localeData = [
                        "default"        => $localeDomain->isDefault(),
                        "id"             => $localeDomain->getId(),
                        "locale"         => $localeDomain->getCode(),
                        "subsite"        => $subsite->getId(),
                        "subsiteDefault" => $subsite->isDefault(),
                    ];

                    if (
                        ($localeDomain->isDefault() && empty($locale))
                        || ($locale == $localeDomain->getCode())
                    ) {
                        $data['defaultLocale'] = $localeData;
                    } else {
                        $data['locales'][] = $localeData;
                    }
                }

                $status = Response::HTTP_OK;
            }
        } catch (\Throwable $e) {
            $data = $e->getMessage();
        }

        return new JsonResponse($data, $status);
    }

    /**
     * @param  int $layoutId
     *
     * @return JsonResponse
     */
    public function generateJsonLayoutAction($layoutId)
    {
        $status = Response::HTTP_NOT_FOUND;
        $data = '';

        try {
            $manager = $this->get('designer.manager.designer');
            if (!empty($manager) && $layoutId > 0) {
                $layout = $manager->getLayout($layoutId);
                $data = $layout->toArray();
                $data['code'] = $manager->generateHtmlLayouts(
                    $layout->getHtml(),
                    $layout->getChildren()
                );
                $status = Response::HTTP_OK;
            }
        } catch (\Throwable $e) {
        }

        return new JsonResponse($data, $status);
    }

    /**
     * @param  int $templateId
     *
     * @return Response
     */
    public function generateTemplateAction($templateId)
    {
        $status = Response::HTTP_NOT_FOUND;
        $code = '';

        try {
            $manager = $this->get('designer.manager.designer');
            if (!empty($manager) && $templateId > 0) {
                $template = $manager->getTemplate($templateId);
                $code = $manager->generateHtmlTemplate($template);
                $status = Response::HTTP_OK;
            }
        } catch (\Throwable $e) {
            if ($this->container->getParameter("kernel.environment") !== "prod") {
                return new Response($e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR);
            }
        }

        return new Response($code, $status);
    }

    /**
     * @param  Request $request
     * @param  int $templateId
     *
     * @return Response
     */
    public function saveTemplateAction(Request $request, $templateId)
    {
        $status = Response::HTTP_NOT_FOUND;

        try {
            $structure = $request->get('structure', '');
            $content = $request->get('content', '');
            $manager = $this->get('designer.manager.designer');
            if (!empty($manager) && $templateId > 0) {
                $success = $manager->saveTemplate(
                    $templateId, json_decode($structure, true)
                );
                if ($success && !empty($content)) {
                    $success = $manager->saveContent(
                        json_decode($content, true), $templateId
                    );
                }
                $publish = (intval($request->get('publish', 0)) == 1);
                if ($success && $publish) {
                    $response = $this->forward('Comitium5DesignerBundle:Designer/Designer:publishTemplate', [
                        'templateId' => $templateId,
                    ]);
                    $status = $response->getStatusCode();
                } elseif ($success) {
                    $status = Response::HTTP_OK;
                }
            }
        } catch (\Throwable $e) {
        }

        return new Response('', $status);
    }

    /**
     * @param  int $templateId
     *
     * @return Response
     */
    public function publishTemplateAction($templateId)
    {
        $status = Response::HTTP_NOT_FOUND;

        try {
            $manager = $this->get('designer.manager.designer');
            if (!empty($manager) && $templateId > 0) {
                $success = $manager->publishTemplate(
                    $templateId
                );
                if ($success) {
                    $status = Response::HTTP_OK;
                }
            }
        } catch (\Throwable $e) {
        }

        return new Response('', $status);
    }

    /**
     * @param  Request $request
     *
     * @return JsonResponse
     */
    public function searchLayoutsAction(Request $request)
    {
        $layouts = new ArrayCollection();

        try {
            $search = $request->get('search', '');
            $manager = $this->get('designer.manager.designer');
            if (!empty($manager) && $search != '') {
                $layouts = $manager->getLayouts($search);
            }
        } catch (\Throwable $e) {
        }

        return new JsonResponse($layouts);
    }

    /**
     * @param Request $request
     *
     * @return JsonResponse
     */
    public function searchWidgetsAction(Request $request)
    {
        $widgets = new ArrayCollection();

        $search = $request->get('search', '');
        $manager = $this->get('designer.manager.designer');
        if (!empty($manager) && $search != '') {
            $widgets = $manager->getWidgets($search, $this->getUser());
        }

        return new JsonResponse($widgets);
    }

    /**
     * @param Request $request
     * @param $templateId
     *
     * @return JsonResponse
     */
    public function getWidgetsOfTemplateAction(Request $request, $templateId)
    {
        $status = JsonResponse::HTTP_BAD_REQUEST;
        $data = [];

        if ($templateId > 0) {
            try {
                $widgetsVisualEditorManager = $this->get('designer.manager.widget_visual_editor');
                $manager = $this->get('designer.manager.designer');
                $template = $manager->getTemplate($templateId);
                $data['layoutPageWidgets'] = $widgetsVisualEditorManager->generateVisualEditorDataFromTemplate($template);
                $status = JsonResponse::HTTP_OK;
            } catch (\Throwable $e) {
                $data = [
                    "message" => $e->getMessage(),
                ];
                $status = JsonResponse::HTTP_INTERNAL_SERVER_ERROR;
            }
        }

        return new JsonResponse($data, $status);
    }

    /**
     * @param Request $request
     * @param int $pageId
     *
     * @return JsonResponse
     */
    public function getWidgetsOfPageAction(Request $request, $pageId)
    {
        $status = JsonResponse::HTTP_BAD_REQUEST;
        $data = [];

        if ($pageId > 0) {
            try {
                $widgetsVisualEditorManager = $this->get('designer.manager.widget_visual_editor');
                $manager = $this->get('designer.manager.designer');
                $page = $manager->getPage($pageId);
                $this->hydratePage($page);
                $data['layoutPageWidgets'] = $widgetsVisualEditorManager->generateVisualEditorDataFromPage($page);
                $status = JsonResponse::HTTP_OK;
            } catch (\Throwable $e) {
                $data = [
                    "message" => $e->getMessage(),
                ];
                $status = JsonResponse::HTTP_INTERNAL_SERVER_ERROR;
            }
        }

        return new JsonResponse($data, $status);
    }

    /**
     * @param PageInterface $page
     *
     * @throws \Exception
     */
    private function hydratePage(PageInterface $page)
    {
        $request = $this->get('request_stack')->getMasterRequest();
        $locale  = $request->query->get('locale') ?? $request->getLocale();

        $api = $this->get('designer.api')->buildClient(
            $this->getParameter('comitium5_api_client.site'),
            $this->getParameter('comitium5_api_client.subsite'),
            $locale
        );

        $pageApiService = new PagesApiService($api);

        $serializedPage = $pageApiService->find(new IdentifiedValue($page->getOriginId()));

        $page->setName($serializedPage["title"] ?? '');

        if (!empty($serializedPage["modificationDate"])) {
            $page->setUpdatedAt(\DateTime::createFromFormat("Y-m-d H:i:s", $serializedPage["modificationDate"]));
        }
    }

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

        return $pageValidator($page);
    }

    /**
     * @return BlockContentService
     */
    private function blockContentService(): BlockContentService
    {
        return new BlockContentService(
            new BlockContentApiService($this->get('designer.api')),
            $this->get('comitium5_common_widgets.cache.redis')
        );
    }
}
