<?php

namespace Application\Admin\Form\LinkField;

use Application\Core\Model\LinkType;

/**
 * @deprecated
 */
abstract class LinkField
{
    /** @var \Application\Admin\Form\LinkField\BaseHandler[] */
    protected $handlers = [];

    /** @var \ActiveRecord\Base */
    protected $model;

    /** @var \Pongho\Template\Html */
    protected $html;

    /** @var \Application\Core\Model\Site */
    protected $site;

    /** @var \Application\Core\Localization */
    protected $lang;

    protected $field;

    protected $languages;

    abstract public function setHandlers($field);

    /**
     * Costruttore.
     *
     * Compatibile con l'aggiunta del campo alla vista da parte del CrudController.
     *
     * Il modello è quello che contiene la relazione con il LinkType, se la relazione è in un sotto-modello (es: SlideItem e MenuItem) è possibile
     * passare il valore null ed utilizzare il metodo render() ma in questo caso non è possibile inserire il campo dal CrudController aggiungendolo
     * alla lista dei campi
     *
     * È necessario specificare alcune opzioni aggiuntive per il corretto funzionamento:
     * - site: istanza di Application\\Core\\Model\\Site
     * - lang: istanza di Application\\Core\\Localization
     * - template_html: istanza di Application\\Core\\Utilities\\Html
     *
     * @param \ActiveRecord\Base $model   Il modello che contiene il campo link_type
     * @param string             $name    Il nome del campo link_type (senza _id) usato nella relazione del modello
     * @param array              $options Le opzioni per il funzionamento del LinkField
     * @throws \BadMethodCallException
     */
    public function __construct($model, $name, array $options)
    {
        $this->model = $model;
        $this->field = $name;

        if (!array_key_exists('site', $options)) {
            throw new \BadMethodCallException('The LinkType field requires an option `site` with an instance of Application\\Core\\Model\\Site');
        }

        if (!array_key_exists('lang', $options)) {
            throw new \BadMethodCallException('The LinkType field requires an option `lang` with an instance of Application\\Core\\Localization');
        }

        if (!array_key_exists('template_html', $options)) {
            throw new \BadMethodCallException('The LinkType field requires an option `template_html` with an instance of Application\\Core\\Utilities\\Html');
        }

        $this->site = $options['site'];
        $this->lang = $options['lang'];
        $this->html = $options['template_html'];

        foreach ($this->site->getLanguages() as $language) {
            $this->languages[$language->language_id] = $language;
        }

        $this->setHandlers($name);
    }

    /**
     * Metodo per aggiungere un handler.
     * Accetta sia classe che handler già istanziato
     *
     * @param $name
     * @param $handler
     */
    public function addHandler($name, $handler)
    {
        if ($handler instanceof BaseHandler) {
            $this->handlers[$name] = $handler;
        } else {
            $this->handlers[$name] = new $handler($this->site->id, $this->field);
        }
    }

    /**
     * Elimina un handler
     *
     * @param $name
     */
    public function delHandler($name)
    {
        if (array_key_exists($name, $this->handlers)) {
            unset($this->handlers[$name]);
        }
    }

    /**
     * @return BaseHandler[]
     */
    public function getHandlersByName()
    {
        return $this->handlers;
    }

    /**
     * Restituisce l'elenco delle opzioni per la select iniziale.
     *
     * @param $language_id
     * @return array
     */
    public function getTypesOptions($language_id)
    {
        $options = [];
        /** @var \Application\Admin\Form\LinkField\BaseHandler $handler */
        foreach ($this->handlers as $handler) {
            $options = $handler->addTypesOptions($language_id, $options);
        }

        foreach ($options as $key => &$label) {
            $value = explode('::', (string) $key);

            $label = [
                $this->lang->get('link_type_' . str_replace('-', '_', $label)),
                'data-value' => $value[1] ?? 'malformed-key',
            ];
        }

        return $options;
    }

    /**
     * @param $language_id
     * @param $link_type_handler
     * @return string
     */
    public function select($language_id, $link_type_handler)
    {
        /**
         * Se il link type handler è nullo, vuol dire che non ho un link associato (nolink)
         *
         * controllo se negli handler del link_field ho a disposizione un "nolink" e quindi mi faccio restituire la sua classe per selezionarlo
         * altrimenti seleziono il primo valore della select.
         */
        if ($link_type_handler === null && array_key_exists('nolink', $this->handlers)) {
            $link_type_handler = $this->handlers['nolink']->getClass();
        }

        return $this->html->select($this->getTypesOptions($language_id), $link_type_handler);
    }

    /**
     * Restituisce il nome del campo (senza l'_id) a cui è abbinato il LinkField corrente
     *
     * @return string
     */
    public function getField()
    {
        return $this->field;
    }

    /**
     * Metodo chiamato dal CrudController per aggiungere il campo alla vista
     *
     * @throws \BadMethodCallException
     * @return string
     */
    public function getViewCode()
    {
        if ($this->model === null) {
            throw new \BadMethodCallException('You can’t call this method because no `model` was set for the LinkType field, call the render() method instead.');
        }

        return $this->render($this->field, $this->lang->getLanguageId(), $this->model->{$this->field});
    }

    /**
     * Restituisce il codice HTML del campo.
     *
     * @param          $name
     * @param          $language_id
     * @param LinkType $link_type
     * @return string
     */
    public function render($name, $language_id, ?LinkType $link_type = null)
    {
        $link_type_handler = null;

        if ($link_type instanceof LinkType) {
            $link_type_handler = $link_type->handler . '::' . $link_type->link_type;
        }

        $select = $this->select($language_id, $link_type_handler);

        $blocks = [
            '<div><select name="' . $name . '[link_type]" class="link-type-select">' . $select . '</select></div>',
        ];

        foreach ($this->handlers as $handler) {
            foreach ($handler->renderSpan($name, $language_id, $link_type) as $block_name => $block_code) {
                $display = ($link_type instanceof LinkType && $link_type->link_type == $block_name) ? ' visible' : '';

                $blocks[] = '<div class="link-type link-type-' . $block_name . $display . '">' . $block_code . '</div>';
            }
        }

        return implode('', $blocks);
    }

    /**
     * Ricava le opzioni delle select degli handler
     *
     * @param       $language_id
     * @param array $query_options Opzioni (query) aggiuntive da passare all'handler per gestire le opzioni della select
     * @return array
     */
    public function getSelectOptions($language_id, array $query_options = [])
    {
        $select_options = [];
        foreach ($this->handlers as $handler) {
            foreach ($handler->getSelectOptions($language_id, $query_options) as $block_name => $options) {
                $select_options[$block_name] = $options;
            }
        }

        return $select_options;
    }

    /**
     * Aggiorna il modello con il valore del campo passato
     */
    public function setAttributeModel()
    {
        $link_type_attributes = LinkType::prepareLinkTypeAttributes(
            $_POST[$this->field],
            $this->lang->getLanguageId(),
            $this->site->id
        );

        /** @var \Application\Admin\Form\LinkField\BaseHandler $handler */
        $handler = $link_type_attributes['handler'];
        $this->model->{$this->field . '_id'} = $handler::save($link_type_attributes);
    }
}
