<?php

namespace Comitium5\DesignerBundle\Controller\Widget;

use Comitium5\ApiClientBundle\ApiClient\ResourcesTypes;
use Comitium5\DesignerBundle\Controller\BaseController;
use Comitium5\DesignerBundle\Entity\Asset;
use Comitium5\DesignerBundle\Entity\Widget;
use Comitium5\DesignerBundle\Form\Model\WidgetParameters\WidgetParameters;
use Comitium5\DesignerBundle\Form\Resolver\WidgetParametersResolver;
use Comitium5\DesignerBundle\Form\Type\WidgetParameters\WidgetParametersType;
use Comitium5\DesignerBundle\Helper\Utils;
use Comitium5\DesignerBundle\Model\WidgetType;
use GuzzleHttp\Exception\GuzzleException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * Class VisualEditorWidgetController
 *
 * @author Carles Gómez <carles@bab-soft.com>
 * @package Comitium5\DesignerBundle\Controller\Widget
 */
class VisualEditorWidgetController extends BaseController
{
    /**
     * This method is called by visual editor to display the parameters popup or the widget code.
     *
     * @param Request $request
     * @param Widget  $widget
     *
     * @Route("/init/{id}", options={"expose" = true}, name = "visual_editor_widget_init")
     *
     * @return Response|static
     *
     * @throws \Exception|GuzzleException
     */
    public function visualEditorInitAction(Request $request, Widget $widget)
    {
        $isDummy = $request->get('dummy', false);
        $display = $request->get('display', "json");
        $locales = $request->get('locales', []);
        $requestParameters = $request->get('parameters');
        $edit = $request->get('edit');
        $subSite = $this->get("designer.provider.subsite")->getSubSite();

        try {
            if ($widget->getType() === WidgetType::ABSTRACTS) {
                throw new \Exception("Abstracts widgets should not be initialised");
            }

            if ($locales === null) {
                throw new \Exception("Locales must be passed as parameter.");
            }

            $controller = $this
                ->get('designer.builder.widget')
                ->buildWidgetController($widget);

            $params = isset($requestParameters['_parameters']) ? $this->resolveRequestParameters($controller, $requestParameters['_parameters']) : $controller->getConfigParameters();
            $uuid   = isset($requestParameters['_uuid']) ? $requestParameters['_uuid'] : null;

            $entity = new WidgetParameters(
                $params,
                $widget,
                $locales,
                $uuid,
                $subSite
            );

            if ($params && $isDummy === false && ($requestParameters === null || $edit !== null)) {
                $form = $this->createForm(WidgetParametersType::class, $entity, [
                    'action' => $this->generateUrl('render_widget', ['id' => $widget->getId(), 'display' => $display]),
                    'data_class' => get_class($entity),
                    'method' => Request::METHOD_POST,
                ]);

                return $this->render('Comitium5DesignerBundle:Widget:form.html.twig', [
                    'form' => $form->createView(),
                    'entity' => $entity
                ]);
            }

            $action = $isDummy ? 'renderDummyAction' : 'renderAction';

            return $display === "json" ?
                $this->buildJsonResponse($controller, $entity, $action) :
                $controller->$action($entity->toArray())
                ;
        } catch (\Throwable $e) {
            return JsonResponse::create([
                "message" => $e->getMessage(),
            ], JsonResponse::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * This action is called by visual editor to set the widget parameters and display the widget code
     *
     * @param Request $request
     * @param Widget  $widget
     *
     * @Route("/render/{id}", options={"expose" = true}, name="render_widget")
     * @return \Symfony\Component\HttpFoundation\Response|static
     * @throws \Exception
     */
    public function renderAction(Request $request, Widget $widget)
    {
        //try {
        $subSite = $this->get("designer.provider.subsite")->getSubSite();

        $display = $request->query->get('display', "json");

        $controller = $this
            ->get('designer.builder.widget')
            ->buildWidgetController($widget);

        $params = $controller->getConfigParameters();

        $entity = new WidgetParameters(
            $params,
            $widget,
            array(),
            null,
            $subSite
        );

        $form = $this->createForm(WidgetParametersType::class, $entity, [
            'action' => $this->generateUrl('render_widget', ['id' => $widget->getId()]),
            'data_class' => get_class($entity),
            'method' => Request::METHOD_POST,
        ]);

        $form->handleRequest($request);

        if (!$form->isValid()) {
            return JsonResponse::create([], JsonResponse::HTTP_BAD_REQUEST);
        }

        return $display === "json" ?
            $this->buildJsonResponse($controller, $entity, "renderAction") :
            $controller->renderAction($entity->toArray())
            ;
        //        } catch (\Throwable $e) {
        //            return JsonResponse::create([
        //                "message" => $e->getMessage(),
        //            ], JsonResponse::HTTP_INTERNAL_SERVER_ERROR);
        //        }
    }

    /**
     * @param AbstractDesignerWidgetController $controller
     * @param WidgetParameters         $widgetParameters
     * @param string                   $action
     *
     * @return JsonResponse
     */
    private function buildJsonResponse(
        AbstractDesignerWidgetController $controller,
        WidgetParameters $widgetParameters,
        $action = "renderAction"
    ) {

        $parameters = $widgetParameters->toArray();

        $data = [
            'parameters' => $parameters,
            'uuid' => $widgetParameters->getUuid(),
            'icons' => $controller->getIconPaths(),
            'css'  => $controller->getCssResourcesPaths(),
            'js'   => $controller->getJSResourcesPaths(),
            'html' => [],
        ];

        $locales = $widgetParameters->getLocales();

        foreach ($locales as $locale) {
            $parameters['_locale'] = $locale;

            $response = $controller->$action($parameters);

            if (!$response instanceof Response) {
                continue;
            }

            $data['html'][$locale] = trim($response->getContent());
        }

        // Remove this parameter before send to editor to avoid save this parameter on database
        if (isset($data['parameters']['_editor'])) {
            unset($data['parameters']['_editor']);
        }

        return new JsonResponse($data);
    }

    /**
     * This action is called by visual editor display asset form
     *
     * @param Request $request
     * @param Asset   $asset
     * @Route("/render-asset-form/{assetId}", defaults={"assetId": 0}, options={"expose" = true}, name="render_asset_form")
     *
     * @return Response|static
     */
    public function displayAssetForm(Request $request, $assetId = null)
    {
        try {
            $subSite = $this->get("designer.provider.subsite")->getSubSite();
            $asset = $this->get("cs.repository.asset")->find($assetId);

            $type = $request->get("type",AssetTypes::IMAGE);

            if ($asset !== null) {
                $type = $asset->getType();
            }

            switch ($type) {
                case AssetTypes::IMAGE:
                    $type = "ASSET_IMAGE";
                    break;
                case AssetTypes::SOUND:
                    $type = "ASSET_SOUND";
                    break;
                case AssetTypes::VIDEO:
                    $type = "ASSET_VIDEO";
                    break;
                default:
                    $type = "ASSET";
            }

            $locales = $request->get('locales', []);

            $params["asset"] = [
                "type"  => $type,
                "value" => $asset ? $asset->getId(): null,
                "options" => [
                    "label" => $this->get("translator")->trans("common.label.asset_select"),
                ],
            ];

            $entity = new WidgetParameters($params, null, $locales, null, $subSite);

            $form = $this->createForm(WidgetParametersType::class, $entity, [
                'action' => $this->generateUrl('render_asset_form', ['asset' => $assetId]),
                'data_class' => get_class($entity),
                'method' => Request::METHOD_POST,
            ]);

            $form->handleRequest($request);

            if ($form->isValid()) {
                $parameters = $entity->toArray();

                if (!isset($parameters['_parameters']['asset']['value'])) {
                    return JsonResponse::create(["Asset value is mandatory"], JsonResponse::HTTP_INTERNAL_SERVER_ERROR);
                }

                $assetId = $parameters['_parameters']['asset']['value'];

                if ($assetId === null) {
                    throw new WidgetControllerErrorException($this, "Asset id must not be null");
                }

                $entity = $this->fetchAssetFromApi($assetId, $parameters);

                $response = [
                    "parameters" => $parameters,
                    "entity"     => $entity,
                ];

                return JsonResponse::create($response);
            }

            return $this->render('Comitium5DesignerBundle:Widget:form.html.twig', [
                'form' => $form->createView(),
                'entity' => $entity
            ]);
        } catch (\Throwable $e) {
            return JsonResponse::create([
                "message" => $e->getMessage(),
            ], JsonResponse::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * @param AbstractDesignerWidgetController $controller
     * @param $requestParameters
     * @return array
     * @throws \Exception
     */
    private function resolveRequestParameters(AbstractDesignerWidgetController $controller, array $requestParameters)
    {
        $results = [];

        foreach ($controller->getConfigParameters() as $key => $configParameter) {
            if (array_key_exists($key, $requestParameters)) {
                if ($configParameter["type"] === $requestParameters[$key]["type"]) {
                    $configParameter["name"] = isset($requestParameters[$key]["name"]) ? $requestParameters[$key]["name"] : "";
                    $configParameter["value"] = isset($requestParameters[$key]["value"]) ? $requestParameters[$key]["value"] : $this->resolveDefaultValueByType($configParameter["type"]);
                }
            }

            $results[$key] = $configParameter;
        }

        return $results;
    }

    /**
     * @param $type
     * @return array|bool|string
     */
    private function resolveDefaultValueByType($type)
    {
        switch ($type) {
            case  WidgetParametersResolver::TYPE_BOOL:
                $defaultValue = false;
                break;
            case  WidgetParametersResolver::TYPE_LIST:
                $defaultValue = [];
                break;
            default:
                $defaultValue = "";
        }

        return $defaultValue;
    }

    /**
     * @param $assetId
     * @param array $parameters
     *
     * @return array|mixed
     * @throws \Doctrine\ORM\EntityNotFoundException
     * @throws \Doctrine\ORM\ORMException
     */
    private function fetchAssetFromApi($assetId, array $parameters)
    {
        $localeCode = empty($parameters['_locale']) ?
            $parameters['_locale']:
            null;

        $apiClient = $this->handleApiClient($localeCode);

        $response = $apiClient->find(
            ResourcesTypes::ASSET,
            $assetId
        );

        if (isset($response["statusCode"])) {
            if ($response["statusCode"] === Response::HTTP_OK) {
                return $response["data"];
            }
        } else {
            return $response;
        }
    }
}
