<?php

namespace Comitium5\DesignerBundle\Form\Resolver;

use Comitium5\ApiClientBundle\ApiClient\ResourcesTypes;
use Comitium5\ApiClientBundle\Client\Client;
use Comitium5\DesignerBundle\Form\Model\WidgetParameters\WidgetParametersValue;
use Comitium5\DesignerBundle\Form\Type\AssetType;
use Comitium5\DesignerBundle\Form\Type\CalendarType;
use Comitium5\DesignerBundle\Form\Type\CKEditor\CKEditorType;
use Comitium5\DesignerBundle\Form\Type\DoubleType;
use Comitium5\DesignerBundle\Form\Type\SpinnerType;
use Comitium5\DesignerBundle\Form\Type\Suggest\ExternalDataBaseSuggestType;
use Comitium5\DesignerBundle\Model\AssetTypes;
use Comitium5\DesignerBundle\Form\Type\Suggest\MultipleSubSiteSuggestType;
use Comitium5\DesignerBundle\Form\Type\Suggest\SuggestType;
use Comitium5\DesignerBundle\Model\Interfaces\IdentifiableInterface;
use Comitium5\DesignerBundle\Provider\SubsiteProvider;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;

/**
 * Class WidgetParametersResolver
 *
 * @author Carles Gómez <carles@bab-soft.com>
 * @package Comitium5\DesignerBundle\Form\Resolver
 */
class WidgetParametersResolver
{
    const PREVIEW_PATH = 'bundles/cs/Widgets/%s/previews/%s';

    //@TODO
    const DEFAULT_PREVIEW = '/bundles/comitium5designer/layout/img/logo.png';

    const TYPE_TEXT = "TEXT";
    const TYPE_TEXT_LARGE = "TEXT_LARGE";
    const TYPE_TEXT_HTML = "HTML";
    const TYPE_INTEGER = "INTEGER";
    const TYPE_DOUBLE = "DOUBLE";
    const TYPE_BOOL = "BOOL";
    const TYPE_CALENDAR = "CALENDAR";
    const TYPE_ASSET_SOUND = "ASSET_SOUND";
    const TYPE_ASSET_DOCUMENT = "ASSET_DOCUMENT";
    const TYPE_ASSET_IMAGE = "ASSET_IMAGE";
    const TYPE_ASSET_VIDEO = "ASSET_VIDEO";
    const TYPE_CATEGORY = "CATEGORY";
    const TYPE_ARTICLE = "ARTICLE";
    const TYPE_TAG = "TAG";
    const TYPE_AUTHOR = "AUTHOR";
    const TYPE_DIRECTORY = "DIRECTORY";
    const TYPE_DIRECTORY_ITEM = "DIRECTORY_ITEM";
    const TYPE_DIARY = "DIARY";
    const TYPE_ACTIVITY = "ACTIVITY";
    const TYPE_CLASSIFIED = "CLASSIFIED";
    const TYPE_POLL = "POLL";
    const TYPE_GALLERY = "GALLERY";
    const TYPE_ASSET = "ASSET";
    const TYPE_CONTACTS = "CONTACTS";
    const TYPE_FORMAT = "FORMAT";
    const TYPE_LIST = "LIST";
    const TYPE_PAGE = "PAGE";
    const TYPE_NEWSLETTER_TEMPLATE = "NEWSLETTER_TEMPLATE";
    const TYPE_LIVE_EVENT = "LIVE_EVENT";
    const TYPE_CROPS = "CROPS";
    const TYPE_CROPS_GROUPS = "CROPS_GROUPS";
    const EXTERNAL_DB = "EXTERNAL_DB";

    const MULTI_SUB_SITE_PARAM = "multisubsite";

    /**
     * @var Client
     */
    private $apiClient;

    /**
     * @var SubsiteProvider
     */
    private $subSiteProvider;

    /**
     * WidgetParametersResolver constructor.
     *
     * @param Client $apiClient
     * @param SubsiteProvider $subSiteProvider
     */
    public function __construct(Client $apiClient, SubsiteProvider $subSiteProvider)
    {
        $this->apiClient       = $apiClient;
        $this->subSiteProvider = $subSiteProvider;
    }

    /**
     * @param WidgetParametersValue $parameter
     *
     * @return array|void
     * @throws \Doctrine\ORM\ORMException
     */
    public function resolve(WidgetParametersValue $parameter)
    {
        if (!$parameter->getType()) {
            return;
        }

        $data = $this->defaultFormFields($parameter);

        if (!$data) {
            return;
        }
        if ($parameter->getOptions()) {
            $data['options'] = $data['options'] + $parameter->getOptions();

            $this->resolveOptions($data['options']);
        }

        if ($parameter->getValue()) {
            $dataValue = $this->parseDefaultValues($parameter);

            if ($dataValue !== null || ($dataValue === null && $parameter->getType() !== self::TYPE_FORMAT)) {
                $data['options']['data'] = $dataValue;
            }
        }

        $data = $this->resolveLabel($parameter, $data);

        $data = $this->resolveHelp($parameter, $data);

        return $data;
    }

    /**
     * @param WidgetParametersValue $parameter
     * @param $data
     *
     * @return array
     */
    private function resolveLabel(WidgetParametersValue $parameter, &$data): array
    {
        if (!isset($data['options']['label'])) {
            $data['options']['label'] = $parameter->getName();
        }

        $locales = $parameter
            ->getWidgetParameter()
            ->getLocales();

        if (isset($locales[0])) {
            if (isset($data['options']['label'][$locales[0]])) {
                $data['options']['label'] = $data['options']['label'][$locales[0]];
            }
        }

        return $data;
    }

    /**
     * @param WidgetParametersValue $parameter
     * @param $data
     *
     * @return array
     */
    private function resolveHelp(WidgetParametersValue $parameter, &$data): array
    {
        if (!isset($data['options']['help'])) {
            return $data;
        }

        $locales = $parameter
            ->getWidgetParameter()
            ->getLocales();

        if (isset($locales[0])) {
            if (isset($data['options']['help'][$locales[0]])) {
                $data['options']['help'] = $data['options']['help'][$locales[0]];
            }
        }

        return $data;
    }

    /**
     * @param WidgetParametersValue $parameter
     *
     * @throws \Doctrine\ORM\ORMException
     */
    public function transformFromValue(WidgetParametersValue $parameter): void
    {
        $data = $this->defaultFormFields($parameter);

        if ($this->requiredTransFormTypes($data['type'])) {
            $entity = $parameter->getValue();

            if ($entity instanceof IdentifiableInterface) {
                $parameter->setValue($entity->getId());
            }

            if ($entity instanceof ArrayCollection || is_array($entity)) {
                $ids = [];

                foreach ($entity as $entityData) {
                    if ($entityData instanceof IdentifiableInterface) {
                        $ids[] = $entityData->getId();
                    }
                }

                $parameter->setValue(implode(',', $ids));
            }
        }
    }

    /**
     * @param WidgetParametersValue $parameter
     */
    public function transformCropsValue(WidgetParametersValue $parameter): void
    {
        $value = $parameter->getValue();

        if (is_string($value)) {
            $value = explode(",", $value);
        }

        $parameter->setValue($value);
    }

    /**
     * @param WidgetParametersValue $parameter
     *
     * @return array
     * @throws \Exception
     */
    private function defaultFormFields(WidgetParametersValue $parameter): array
    {
        $choices = [];
        $data    = [];
        $parameterData = $parameter->getData();

        if ($parameter->getType() === self::TYPE_FORMAT) {
            $options = $parameter->getOptions();
            if (isset($options['choices']) && is_array($options['choices'])) {
                foreach ($options['choices'] as $values) {
                    $url = self::DEFAULT_PREVIEW;

                    if (isset($values['preview'])) {
                        $preview = sprintf(
                            self::PREVIEW_PATH,
                            $parameter
                                ->getWidgetParameter()
                                ->getWidget()
                                ->formatName(),
                            $values['preview']
                        );

                        $url = $preview;
                    }

                    $choices[$values['value']] = $url;
                }

                if (!$parameter->getValue()) {
                    $parameter->setValue(current(array_keys($choices)));
                }
            }
        }

        switch ($parameter->getType()) {
            case self::TYPE_TEXT:
                $data = [
                    'type' => TextType::class,
                    'options' => []
                ];
                break;
            case self::TYPE_TEXT_LARGE:
                $data = [
                    'type' => TextareaType::class, 'options' => [
                        "attr" => [
                            "rows" => "15",
                            "style" => "width:100%;",
                        ],
                    ]];
                break;
            case self::TYPE_TEXT_HTML:
                $data = [
                    'type' => CKEditorType::class,
                    'options' => [],
                ];
                break;
            case self::TYPE_INTEGER:
                $data = [
                    'type' => SpinnerType::class,
                    'options' => []
                ];
                break;
            case self::TYPE_DOUBLE:
                $data = [
                    'type' => DoubleType::class,
                    'options' => []
                ];
                break;
            case self::TYPE_BOOL:
                $data = [
                    'type' => CheckboxType::class,
                    'options' => [
                        'view_type' => 'tick',
                    ]
                ];
                break;
            case self::TYPE_CALENDAR:
                $data = [
                    'type' => CalendarType::class,
                    'options' => [
                        "label_attr" => [
                            "class" => ""
                        ],
                        "container_attr" => [
                            "class" => ""
                        ]
                    ]
                ];
                break;
            case self::TYPE_ASSET_SOUND:
                $data = [
                    'type' => AssetType::class,
                    'options' => [
                        'asset_type' => AssetTypes::SOUND, 'data_class' => null,
                    ]
                ];
                break;
            case self::TYPE_ASSET_DOCUMENT:
                $data = [
                    'type' => AssetType::class,
                    'options' => [
                        'asset_type' => AssetTypes::DOCUMENT, 'data_class' => null,
                    ]
                ];
                break;
            case self::TYPE_ASSET_IMAGE:
                $data = [
                    'type' => AssetType::class,
                    'options' => [
                        'asset_type' => AssetTypes::IMAGE,
                        'data_class' => null,
                    ]
                ];
                break;
            case self::TYPE_ASSET_VIDEO:
                $data = [
                    'type' => AssetType::class,
                    'options' => [
                        'asset_type' => AssetTypes::VIDEO, 'data_class' => null,
                    ]
                ];
                break;
            case self::TYPE_CATEGORY:
                $data = [
                    'type' => SuggestType::class,
                    'options' => [
                        'content_type'  => ResourcesTypes::CATEGORY,
                        'onlyPublished' => true,
                    ]
                ];
                break;
            case self::TYPE_ARTICLE:
                $data = [
                    'type' => !empty($parameterData[self::MULTI_SUB_SITE_PARAM]) ?
                        MultipleSubSiteSuggestType::class :
                        SuggestType::class,
                    'options' => [
                        'content_type'    => ResourcesTypes::ARTICLE,
                        'labelProperties' => '%id%: %title%',
                        'onlyPublished'   => true,
                    ],
                ];
                break;
            case self::TYPE_TAG:
                $data = [
                    'type' => SuggestType::class,
                    'options' => [
                        'content_type'  => ResourcesTypes::TAG,
                        'onlyPublished' => true,
                    ],
                ];
                break;
            case self::TYPE_AUTHOR:
                $data = [
                    'type' => SuggestType::class,
                    'options' => [
                        'content_type'    => ResourcesTypes::AUTHOR,
                        'labelProperties' => '%id%: %fullName%',
                    ],
                ];
                break;
            case self::TYPE_DIRECTORY:
                $data = [
                    'type' => SuggestType::class,
                    'options' => [
                        'content_type'  => ResourcesTypes::DIRECTORY,
                        'onlyPublished' => true,
                    ]
                ];
                break;
            case self::TYPE_DIRECTORY_ITEM:
                $data = [
                    'type' => SuggestType::class,
                    'options' => [
                        'content_type'  => ResourcesTypes::DIRECTORY_ITEM,
                        'onlyPublished' => true,
                    ]
                ];
                break;
            case self::TYPE_DIARY:
                $data = [
                    'type' => SuggestType::class,
                    'options' => [
                        'content_type'  => ResourcesTypes::DIARY,
                        'onlyPublished' => true,
                    ]
                ];
                break;
            case self::TYPE_ACTIVITY:
                $data = [
                    'type' => SuggestType::class,
                    'options' => [
                        'content_type'  => ResourcesTypes::ACTIVITY,
                        'onlyPublished' => true,
                    ]
                ];
                break;
            case self::TYPE_CLASSIFIED:
                $data = [
                    'type' => SuggestType::class,
                    'options' => [
                        'content_type'  => ResourcesTypes::CLASSIFIED,
                        'onlyPublished' => true,
                    ]
                ];
                break;
            case self::TYPE_POLL:
                $data = [
                    'type' => SuggestType::class,
                    'options' => [
                        'content_type'  => ResourcesTypes::POLL,
                        'onlyPublished' => true,
                    ]
                ];
                break;
            case self::TYPE_GALLERY:
                $data = [
                    'type' => SuggestType::class,
                    'options' => [
                        'content_type'  => ResourcesTypes::GALLERY,
                        'onlyPublished' => true,
                    ]
                ];
                break;
            case self::TYPE_FORMAT:
                $data = [
                    'type' => ChoiceType::class,
                    'options' => [
                        'choices'  => $choices,
                        'choices_as_values' => false,
                        'expanded' => true,
                        'multiple' => false,
                        'placeholder' => false,
                        'view_type' => "format",
                    ]
                ];
                break;
            case self::TYPE_LIST:
                $data = [
                    'type' => ChoiceType::class,
                    'options' => [
                        'placeholder' => false,
                        'choices_as_values' => false,
                        'view_type' => "list",
                    ]
                ];
                break;
            case self::TYPE_PAGE:
                $data = [
                    'type' => SuggestType::class,
                    'options' => [
                        'content_type'  => ResourcesTypes::PAGE,
                        'onlyPublished' => true,
                    ]
                ];
                break;
            case self::TYPE_NEWSLETTER_TEMPLATE:
                $data = [
                    'type' => SuggestType::class,
                    'options' => [
                        'content_type'  => ResourcesTypes::NEWSLETTER_TEMPLATE,
                        'onlyPublished' => true,
                    ]
                ];
                break;
            case self::TYPE_LIVE_EVENT:
                $data = [
                    'type' => SuggestType::class,
                    'options' => [
                        'content_type'    => ResourcesTypes::LIVE_EVENT,
                        'labelProperties' => '%id%: %title%',
                        'onlyPublished'   => true,
                    ]
                ];
                break;
            case self::TYPE_CROPS:
                $data = [
                    'type' => ChoiceType::class,
                    'options' => [
                        "choices"           => $this->getImageResizeChoices(),
                        'choices_as_values' => false,
                    ]
                ];
                break;
            case self::TYPE_CROPS_GROUPS:
                $data = [
                    'type' => ChoiceType::class,
                    'options' => [
                        "choices"           => $this->getImageResizeGroupsChoices(),
                        'choices_as_values' => false,
                    ]
                ];
                break;
            case self::EXTERNAL_DB:
                $data = [
                    'type'    => ExternalDataBaseSuggestType::class,
                    'options' => $parameter->getOptions(),
                ];
                break;
        }

        if ($data['type'] == SuggestType::class) {
            $data['options']['separator'] = ',';
        }

        return $data;
    }

    /**
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    private function getImageResizeChoices(): array
    {
        $choices = [];

        $resizes = $this
            ->apiClient
            ->findBy(ResourcesTypes::IMAGE_RESIZE, [
                'limit' => 99
            ]);

        if (!isset($resizes['data']['results'])) {
            return [];
        }

        foreach ($resizes['data']['results'] as $resize) {
            $choices[$resize['width']."|".$resize['height']] = $resize['title'];
        }

        return $choices;
    }

    /**
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    private function getImageResizeGroupsChoices(): array
    {
        $choices = [];

        $crops = $this
            ->apiClient
            ->findBy(ResourcesTypes::IMAGE_CROP, [
                'limit' => 99
            ]);

        if (!isset($crops['data']['results'])) {
            return [];
        }

        foreach ($crops['data']['results'] as $crop) {
            $key = [];
            $resizes = $this
                ->apiClient
                ->findBy(ResourcesTypes::IMAGE_RESIZE, [
                    'limit' => 99,
                    'imageCrops' => $crop['id']
                ]);

            if (isset($resizes['data']['results'])) {
                foreach ($resizes['data']['results'] as $resize) {
                    $key[] = $resize['width']."|".$resize['height'];
                }
            }

            $choices[implode(",", $key)] = $crop['title'];
        }

        return $choices;
    }

    /**
     * @param $type
     *
     * @return bool
     */
    private function requiredTransFormTypes($type): bool
    {
        $required = [
            EntityType::class,
            SuggestType::class,
            CheckboxType::class,
        ];

        return in_array($type, $required);
    }

    /**
     * @param WidgetParametersValue $parameter
     *
     * @return array|mixed|string|null
     */
    private function parseDefaultListValues(WidgetParametersValue $parameter)
    {
        $options = $parameter->getOptions();
        $values = $parameter->getValue();
        $multiple = isset($options["multiple"]) ? $options["multiple"] : false;
        $matches = [];

        if (is_null($values) && $multiple) {
            $values = [];
        }

        foreach ($options as $option) {
            if (is_array($values)) {
                foreach ( $values as $value) {
                    if (isset($option[$value])) {
                        $matches[] = $value;
                    }
                }
            } else {
                if (!isset($option[$values])) {
                    continue;
                }
                return $values;
            }
        }

        return $matches;
    }

    /**
     * @param WidgetParametersValue $parameter
     *
     * @return array|mixed
     */
    private function parseDefaultCropsValues(WidgetParametersValue $parameter)
    {
        $options = $parameter->getOptions() == null ? [] : $parameter->getOptions();

        $this->resolveOptions($options);

        $values = $parameter->getValue();
        $matches = [];
        $multiple = isset($options["multiple"]) ? $options["multiple"] : false;

        foreach ($values as $value) {
            if ($multiple) {
                $matches[] = $value;
            } else {
                return $value;
            }
        }

        return $matches;
    }

    /**
     * @param WidgetParametersValue $parameter
     *
     * @return mixed|string
     */
    private function parseDefaultCropsGroupsValues(WidgetParametersValue $parameter)
    {
        $options = $parameter->getOptions();

        $multiple = isset($options["multiple"]) ? $options["multiple"] : false;

        $this->resolveOptions($options);

        $values = $parameter->getValue();

        if ($multiple === false) {
            return implode(',', $values);
        }

        return $values;
    }

    /**
     * @param WidgetParametersValue $parameter
     *
     * @return array|bool|ArrayCollection|mixed|object|string|void|null
     * @throws \Doctrine\ORM\ORMException
     */
    private function parseDefaultValues(WidgetParametersValue $parameter)
    {
        if ($parameter->getType() === self::TYPE_FORMAT) {
            return;
        }

        if ($parameter->getType() === self::TYPE_LIST) {
            return $this->parseDefaultListValues($parameter);
        }

        if ($parameter->getType() === self::TYPE_CROPS) {
            return $this->parseDefaultCropsValues($parameter);
        }

        if ($parameter->getType() === self::TYPE_CROPS_GROUPS) {
            return $this->parseDefaultCropsGroupsValues($parameter);
        }

        $options = $parameter->getOptions() === null ? [] : $parameter->getOptions();

        $this->resolveOptions($options);

        $data = $this->defaultFormFields($parameter);

        if (!$data) {
            return;
        }

        if (!$this->requiredTransFormTypes($data['type'])) {
            return $parameter->getValue();
        }

        switch ($data['type']) {
            case CheckboxType::class:
                return $parameter->getValue() === "true" ? true : false;
            default:
                break;

        }

        $value = $parameter->getValue();

        if ($data['type'] === SuggestType::class || $data['type']) {
            $multiple = isset($options["multiple"]) ? $options["multiple"] : false;

            $value = $this->parseToArray($value, $multiple, $data['options']['separator']);
        }

        if (!is_array($value)) {
            return ($value);
        } else {
            $data = new ArrayCollection();

            foreach ($value as $id) {
                $data->add($id);
            }

            return $data;
        }
    }

    /**
     * @param string $value
     * @param bool $multiple
     * @param string $separator
     *
     * @return false|mixed|string|string[]
     */
    private function parseToArray(string $value, bool $multiple, string $separator)
    {
        $newValue = explode($separator, $value);

        if ($multiple === false) {
            $newValue = array_shift($newValue);
        }

        return $newValue;
    }

    /**
     * @param array $options
     */
    private function resolveOptions(array &$options): void
    {
        if (!empty($options["multiple"])) {
            $options["multiple"] = $options["multiple"] === "false" ? false : true;
        }

        if (!empty($options["required"])) {
            $options["required"] = $options["required"] === "false" ? false : true;
        }
    }
}
