<?php

namespace App\Libs\Backoffice\Generic\Fields;

use App\Libs\Contracts\Backoffice\DuplicatableField;
use App\Libs\Contracts\Backoffice\ListableField;
use App\Libs\Contracts\Backoffice\SearchableField;
use App\Libs\Contracts\Backoffice\VersionableField;
use App\Libs\Exception\InvalidConfigurationException;
use App\Libs\Facades\ContentTypes;
use App\Libs\Facades\Localization;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use App\Models\WebsiteObject;

class SelectBoxField extends BaseField implements ListableField, SearchableField, VersionableField, DuplicatableField
{
    protected $contentType;
    protected $model;
    protected $list;
    protected $defaultValue = null;
    protected $defaultValueDefaultLanguage = null;
    protected $allowInsert = false;
    protected $boxSearch = false;
    protected $labelField = null;
    protected $dropdownSortField = null;
    protected $browser = null;
    protected $woMultilang = false;

    public function __construct($config)
    {
        // unset($config["multiLanguage"]);
        parent::__construct($config);

        if (empty($config["contentType"]) && empty($config["list"])) {
            throw new InvalidConfigurationException("The field {$this->getName()} is missing a content type or a list");
        }

        if (!empty($config["contentType"]) && !empty($config["list"])) {
            throw new InvalidConfigurationException("The field {$this->getName()} is can only have a content type or a list");
        }

        if (!empty($config["contentType"])) {
            $this->contentType = $config["contentType"];
        } else {
            $this->list = $config["list"];
        }

        if (!empty($config["defaultValue"])) {
            $this->defaultValue = $config["defaultValue"];
        }

        if (!empty($config["defaultValueDefaultLanguage"])) {
            $this->defaultValueDefaultLanguage = $config["defaultValueDefaultLanguage"];
        }

        if (!empty($config["dropdownSortField"])) {
            $this->dropdownSortField = $config["dropdownSortField"];
        }

        if (!empty($config["labelField"])) {
            $this->labelField = $config["labelField"];
        }

        if (!empty($config["allowInsert"]) && !empty($config["contentType"])) {
            $this->allowInsert = $config["allowInsert"];
        }

        if (!empty($config["boxSearch"])) {
            $this->boxSearch = $config["boxSearch"];
        }

        if (!empty($config["browser"])) {
            $this->browser = $config["browser"];
        }

        if (!empty($config["woMultilang"])) {
            $this->woMultilang = $config["woMultilang"];
        }
    }

    public function getContentType()
    {
        return $this->contentType;
    }

    public function setContentType($contentType)
    {
        $this->contentType = $contentType;
    }

    public function getList()
    {
        return $this->list;
    }

    public function setList($list)
    {
        $this->list = $list;
    }

    public function getAllowInsert()
    {
        return $this->allowInsert;
    }

    public function setAllowInsert($allowInsert)
    {
        $this->allowInsert = $allowInsert;
    }

    public function isBoxSearchable()
    {
        return $this->boxSearch;
    }

    public function setBoxSearchable($boxSearch)
    {
        $this->boxSearch = $boxSearch;
    }

    public function buildSelect(Builder $query)
    {

        if (!empty($this->contentType)) {
            return $this->_buildModelSelect($query);
        } else {
            return $this->_buildListSelect($query);
        }
    }

    private function _buildModelSelect(Builder $query)
    {
        $tablePrefix = $query->getModel()->getTable();
        $model = $this->_getModel();
        $modelTable = $model->getTable();
        $query->leftJoin($modelTable, "{$tablePrefix}.{$this->getName()}", "=", "{$modelTable}.id");

        $languageRelationship = $model->languages();
        $languageModel = $languageRelationship->getRelated();
        $languageFK = $languageRelationship->getQualifiedForeignKeyName();
        $query->leftJoin($languageModel->getTable(),
            function ($join) use ($model, $languageFK, $languageModel) {
                $join->on($model->getQualifiedKeyName(), "=", $languageFK)
                    ->where("{$languageModel->getTable()}.language_id", "=", Localization::getActiveLocale());
            });

        $query->addSelect("{$languageModel->getTable()}.title as relation.".$this->getName());
        return $query;
    }

    private function _buildListSelect(Builder $query)
    {
        if ($this->isMultiLanguage()) {
            $tablePrefix = $query->getModel()->languages()->getRelated()->getTable();
        } else {
            $tablePrefix = $query->getModel()->getTable();
        }

        $query->addSelect("{$tablePrefix}.{$this->name}");
        return $query;
    }

    public function buildWhere(Builder $query, $searchPhrase)
    {
        if (empty($searchPhrase) || !$this->isSearchable()) {
            return $query;
        }

        if (!empty($this->contentType)) {
            return $this->_buildModelWhere($query, $searchPhrase);
        } else {
            return $this->_buildListWhere($query, $searchPhrase);
        }
    }

    private function _buildModelWhere(Builder $query, $searchPhrase)
    {
        $tablePrefix = $this->_getModel()->languages()->getRelated()->getTable();

        $query->orWhere("{$tablePrefix}.title", 'like', "%{$searchPhrase}%");
        return $query;
    }

    private function _buildListWhere(Builder $query, $searchPhrase)
    {
        $list = Arr::where($this->list, function ($value, $key) use ($searchPhrase) {
            return Str::contains(strtolower($value), strtolower($searchPhrase));
        });
        $tablePrefix = $query->getModel()->getTable();

        if (!empty($list)) {
            $query->orWhereIn("{$tablePrefix}.{$this->getName()}", array_keys($list));
        }
        return $query;
    }

    public function buildOrder(Builder $query, $sortOrder = "asc")
    {
        if (!empty($this->contentType)) {
            return $this->_buildModelOrder($query, $sortOrder);
        } else {
            return $this->_buildListOrder($query, $sortOrder);
        }
    }

    private function _buildModelOrder(Builder $query, $sortOrder = "asc")
    {
        $tablePrefix = $this->_getModel()->languages()->getRelated()->getTable();
        $query->orderBy("{$tablePrefix}.title", $sortOrder);
        return $query;
    }

    private function _buildListOrder(Builder $query, $sortOrder = "asc")
    {
        $list = $this->list;
        $sortFunction = "_reorderAscList";
        if (strtolower($sortOrder) != 'asc') {
            $sortFunction = "_reorderDescList";
        }
        $tablePrefix = $query->getModel()->getTable();

        uasort($list, array($this, $sortFunction));
        $sortValues = implode(" ','", array_keys($list));
        $query->orderByRaw("field({$tablePrefix}.{$this->getName()}, '{$sortValues}')");

        return $query;
    }

    protected function _reorderAscList($listItem1, $listItem2)
    {
        $firstItem = strtolower($listItem1);
        $secondItem = strtolower($listItem2);

        if ($firstItem == $secondItem) {
            return 0;
        }

        return ($firstItem > $secondItem) ? 1 : -1;
    }

    protected function _reorderDescList($listItem1, $listItem2)
    {
        $firstItem = strtolower($listItem1);
        $secondItem = strtolower($listItem2);

        if ($firstItem == $secondItem) {
            return 0;
        }

        return ($firstItem < $secondItem) ? 1 : -1;
    }

    public function toGridHeader()
    {
        $header = '<th data-column-id="relation.'.$this->getName().'"';

        if (!empty($this->list)) {
            $header .= ' data-formatter="staticList"';
        }
        $header .= '>'.ucfirst($this->getLabel()).'</th>';
        return $header;
    }

    private function _getModel()
    {
        if (empty($this->model)) {
            $contentType = ContentTypes::find($this->contentType);
            $modelType = $contentType->getModelType();
            $this->model = new $modelType;

        }

        return $this->model;
    }

    public function getCurrentValue($model)
    {
        $fieldName = $this->getName();

        if (request()->has($fieldName)) {
            return request()->input($fieldName);
        }

        if ($model->exists) {
            return $model->$fieldName;
        }

        if (!empty($this->defaultValue)) {
            return $this->defaultValue;
        }

        return null;
    }

    public function toForm($model, $enabled = true, $hidden = false)
    {
        $list = ['' => ''] + $this->_fetchList();
        $html = '';
        if ($this->isMultiLanguage()) {
            $languages = Localization::getSupportedLanguages();
            $defaultLang = Localization::getDefaultLocale();
            $fieldName = $this->getName();

            foreach ($languages as $language) {
                $name = $fieldName."[{$language->id}]";
                $id = $fieldName."_{$language->id}";
                $errorKey = $fieldName.".".$language->id;
                $value = null;

                if ($model->exists) {
                    if ($this->woMultilang) {
                        $wo = WebsiteObject::find($model->website_object_id);
                        $languageModel = $wo->languages->findByLocale($language->id);
                    } else {
                        $languageModel = $model->languages->findByLocale($language->id);
                    }
                    if (!empty($languageModel)) {
                        $value = $languageModel->$fieldName;
                    }
                } elseif ($this->defaultValueDefaultLanguage && $language->id == $defaultLang) {
                    $value = $this->defaultValueDefaultLanguage;
                } elseif ($this->defaultValue) {
                    $value = $this->defaultValue;
                }

                $contentType = false;

                if ($this->getAllowInsert()) {
                    $contentType = ContentTypes::find($this->getContentType());
                    $contentType = array(
                        "type" => $contentType->getType(),
                        "label" => ucfirst($contentType->getSingularLabel())
                    );
                }

                if ($this->isDisabled()) {
                    $enabled = false;
                }

                $html .= view(
                    "admin.generic.components.forms.selectbox",
                    array(
                        "id" => $id,
                        "name" => $name,
                        "fieldName" => $fieldName,
                        "errorKey" => $errorKey,
                        "label" => $this->getLabel(),
                        "value" => $value,
                        "list" => $list,
                        "hide" => ($hidden || $language->id != $defaultLang),
                        "enabled" => $enabled,
                        "language" => $language->id,
                        "languages" => $languages,
                        "defaultLang" => $defaultLang,
                        "parentHidden" => ($hidden && !empty($this->parent)),
                        "parent" => $this->getParent(),
                        "contentType" => $contentType,
                        "searchable" => $this->isBoxSearchable(),
                        "browser" => $this->browser,
                        "info" => $this->getInfo(),
                        "isVersionable" => !empty($this->list),
                        "blocksMode" => $this->blocksMode,
                    ));
            }
        } else {
            $name = $this->getName();
            $value = null;

            if ($model->exists) {
                $value = $model->$name;
            } elseif ($this->defaultValue) {
                $value = $this->defaultValue;
            }

            $contentType = false;

            if ($this->getAllowInsert()) {
                $contentType = ContentTypes::find($this->getContentType());
                $contentType = array(
                    "type" => $contentType->getType(),
                    "label" => ucfirst($contentType->getSingularLabel())
                );
            }

            if ($this->isDisabled()) {
                $enabled = false;
            }

            $html = view(
                "admin.generic.components.forms.selectbox",
                array(
                    "id" => $this->getName(),
                    "name" => $this->getName(),
                    "fieldName" => $this->getName(),
                    "errorKey" => $this->getName(),
                    "label" => $this->getLabel(),
                    "value" => $value,
                    "list" => $list,
                    "hide" => $hidden,
                    "enabled" => $enabled,
                    "parentHidden" => ($hidden && !empty($this->parent)),
                    "parent" => $this->getParent(),
                    "contentType" => $contentType,
                    "searchable" => $this->isBoxSearchable(),
                    "browser" => $this->browser,
                    "info" => $this->getInfo(),
                    "isVersionable" => !empty($this->list),
                    "blocksMode" => $this->blocksMode,
                ));
        }
        return $html;
    }

    private function _fetchList()
    {
        if (!empty($this->list)) {
            if ($this->dropdownSortField === 'list') {
                asort($this->list);
            }
            return $this->list;
        }

        $relationshipModel = $this->_getModel();
        $relationshipTable = $relationshipModel->getTable();

        $languageRelationship = $relationshipModel->languages();
        $languageModel = $languageRelationship->getRelated();
        $languageTable = $languageModel->getTable();

        if ($this->labelField == null) {
            $this->labelField = $languageTable.'.title';
        }

        $sortField = $this->dropdownSortField ? $this->dropdownSortField : "{$languageTable}.title";

        return $relationshipModel->full()
            ->select("{$relationshipTable}.id", $this->labelField)
            ->orderBy($sortField, "asc")->pluck($this->labelField, "id")->all();

    }

    public function getValidationData($copyAllLang = false)
    {
        $validation = parent::getValidationData();
        if ($this->isMultiLanguage()) {
            foreach ($validation["values"][$this->getName()] as $key => $value) {
                if ($value == "") {
                    $validation["values"][$this->getName()][$key] = null;
                }
            }
        } else {
            if ($validation["values"][$this->getName()] == "") {
                $validation["values"][$this->getName()] = null;
            }
        }
        return $validation;
    }

    public function toArray()
    {
        $array = parent::toArray();
        if (!empty($this->list)) {
            $array["list"] = $this->list;
        }
        return $array;
    }

    public static function getScript()
    {
        $locale = Localization::getActiveLocale();

        return array(
            "selectBoxField" => path_asset('path.backoffice.js_path').'/view/backend-generic-selectbox.js',
            "selectBoxBrowser" => path_asset('path.backoffice.js_path').'/view/backend-generic-browser-modal.js',
            "selectBoxSearch" => path_asset('path.backoffice.js_path').'/select/bootstrap-select.min.js',
            "selectBoxSearchLang" => path_asset('path.backoffice.js_path')."/select/i18n/defaults-{$locale}.js"
        );

    }

    /**
     * Simple level 2 search indexing. only works for multilang and for title field
     * Not to be used in any outher case!
     *
     * @param  mixed  $model
     * @param  mixed  $view
     */
    public function getSearchStructure($model, $view = false)
    {
        $searchData = array();

        $fieldName = $this->getName();

        if (!$this->contentType || !$model->$fieldName) {
            return null;
        }

        $childModel = $this->_getModel();
        $childModel = $childModel::find($model->$fieldName);

        foreach ($childModel->languages as $languageModel) {
            if (!empty($languageModel->title)) {
                if ($view) {
                    $value = cut_string($languageModel->title, 500);
                } else {
                    $value = $languageModel->title;
                }
                $searchData[$languageModel->language_id] = $value;
            } else {
                $searchData[$languageModel->language_id] = '';
            }
        }
        return $searchData;
    }

    public function getVersionStructure($model)
    {
        $fieldVersion = array();
        $fieldName = $this->getName();

        $fieldVersion['multilanguage'] = false;
        $fieldVersion['value'] = $model->$fieldName;

        return $fieldVersion;
    }

    public function isVersionable()
    {
        return !empty($this->list);
    }
}


