<?php

namespace App\Libs\Backoffice\Generic\Fields;

use App\Facades\ContentTypes;
use App\Libs\Contracts\Backoffice\ListableField;
use App\Libs\Exception\InvalidConfigurationException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;

class SelectBoxField extends BaseField implements ListableField
{
    protected $contentType;
    protected $model;
    protected $list;
    protected $defaultValue;
    protected $allowInsert = false;
    protected $boxSearch = false;
    protected $labelField = null;
    protected $mandatory = 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 (isset($config["defaultValue"])) {
            $this->defaultValue = $config["defaultValue"];
        }

        if (isset($config["mandatory"])) {
            $this->mandatory = $config["mandatory"];
        }

        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"];
        }
    }

    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 isMandatory()
    {
        return $this->mandatory;
    }

    public function setMandatory($mandatory = false)
    {
        $this->mandatory = $mandatory;
    }


    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", "=", LaravelLocalization::getCurrentLocale());
            });

        $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 ($key, $value) 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;
    }

    /**
     * Gets the content type model instance
     *
     * @return Model
     */
    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 $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 $model, $enabled = true, $hidden = false)
    {
        if (!$this->isMandatory()) {
            $list = ['' => ''] + $this->_fetchList();
        } else {
            $list = $this->_fetchList();
        }

        $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;
        }

        return view(
            "admin.generic.components.forms.selectbox",
            array(
                "id" => $this->getName(),
                "name" => $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(),
                "info" => $this->getInfo()
            ));
    }

    private function _fetchList()
    {
        if (!empty($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';
        }

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

    public function getValidationData($copyAllLang = false)
    {
        $validation = parent::getValidationData();
        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 = LaravelLocalization::getCurrentLocale();

        return array(
            "selectBoxField" => path_asset('path.backoffice.js_path').'/view/generic/backend-generic-selectbox.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  Model  $model
     * @param  boolean  $view
     *
     * @return array
     */
    public function getSearchStructure(Model $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;
    }
}
  

