<?php

namespace App\Libs\Backoffice\Generic\Fields;

use App\Facades\ContentTypes;
use App\Libs\Contracts\Backoffice\SavableField;
use App\Libs\Exception\InvalidConfigurationException;
use App\Models\GenericBackoffice\WebsiteObject;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;

class MultiplePickerField extends BaseField implements SavableField
{
    protected $order = false;
    protected $objectTypes = false;
    protected $maxSize = false;
    protected $longList = false;
    protected $minSize = 0;
    protected $websiteFiltered = false;
    protected $allowInsert = false;

    public function __construct($config)
    {
        unset($config["multiLanguage"]);
        unset($config["inList"]);
        unset($config["search"]);
        unset($config["sort"]);

        parent::__construct($config);

        $this->relationField = true;

        if (empty($config["objectTypes"])) {
            throw new InvalidConfigurationException("The {$this->name} configuration is missing the object types");
        }

        $this->objectTypes = $config["objectTypes"];

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

        if ($this->websiteFiltered === false && count($this->objectTypes) > 1) {
            throw new InvalidConfigurationException("The {$this->name} multi picker field cannot have more than one object type if it is not multi website");
        }

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

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

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

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


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

    }

    public function isOrderable()
    {
        return $this->order;
    }

    public function setOrderable($order)
    {
        $this->order = $order;
    }

    public function getObjectTypes()
    {
        return $this->objectTypes;
    }

    public function getObjectTypesModelsType()
    {
        $modelsType = array();
        foreach ($this->objectTypes as $objectType) {
            $contentType = ContentTypes::find($objectType);
            $modelsType[] = $contentType->getModelType();

        }
        return $modelsType;
    }

    public function getContentTypes()
    {
        $contentTypes = array();
        foreach ($this->objectTypes as $objectType) {
            $contentType = ContentTypes::find($objectType);
            $contentTypes[] = $contentType;
        }
        return $contentTypes;
    }

    public function setObjectTypes($objectTypes = array())
    {
        $this->objectTypes = $objectTypes;
    }

    public function isMultipleTypes()
    {
        return !empty($this->objectTypes) && count($this->objectTypes) > 1;
    }

    public function setWebsiteFiltered($websiteFiltered)
    {
        $this->websiteFiltered = $websiteFiltered;
    }

    public function isWebsiteFiltered()
    {
        return $this->websiteFiltered;
    }

    public function getMaxSize()
    {
        return $this->maxSize;
    }

    public function setMaxSize($maxSize)
    {
        $this->maxSize = $maxSize;
    }

    public function getMinSize()
    {
        return $this->minSize;
    }

    public function setMinSize($minSize = 0)
    {
        $this->minSize = $minSize;
    }

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

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

    public function isLongList()
    {
        return $this->longList;
    }

    public function setLongList($longList)
    {
        $this->longList = $longList;
    }

    public function getValidation()
    {
        $rules = "array";

        if ($this->minSize > 0) {
            if ($this->parent) {
                $rules .= "|required_if:{$this->parent["name"]},".implode(",", $this->parent["values"]);
                $parentValue = request()->input($this->parent["name"]);
                if (!is_null($parentValue) && in_array($parentValue, $this->parent["values"])) {
                    $rules .= "|min:".$this->minSize;
                }
            } else {
                $rules .= "|required|min:".$this->minSize;
            }
        }

        if ($this->maxSize > 0) {
            $rules .= "|max:".$this->maxSize;
        }

        return $rules;
    }

    public function toForm(Model $model, $enabled = true, $hidden = false)
    {
        if (!$this->isLongList()) {
            if ($this->isWebsiteFiltered()) {
                return $this->_toFormWebsite($model, $enabled, $hidden);
            } else {
                return $this->_toFormNoWebsite($model, $enabled, $hidden);
            }
        } else {
            if ($this->isWebsiteFiltered() || $this->isMultipleTypes()) {
                throw new InvalidConfigurationException("A long list multipickerfield cannot be website or multiple types");
            }
            return $this->_toFormLongListNoWebsite($model, $enabled, $hidden);
        }
    }

    public function _toFormWebsite($model, $enabled, $hidden)
    {
        if (empty($this->objectTypes)) {
            return "";
        }

        $websiteModel = new WebsiteObject();
        $relation = $websiteModel->related();

        $modelsTypes = $this->getObjectTypesModelsType();


        if (empty($modelsTypes)) {
            return "";
        }

        $query = WebsiteObject::with("objectable")
            ->whereIn("object_type", $modelsTypes);


        $objects = $query->get();

        $fullList = array();

        foreach ($this->getContentTypes() as $contentType) {

            $fullList[$contentType->getModelType()] = array(
                "label" => ucfirst($contentType->getLabel()),
                "list" => array()
            );
        }

        foreach ($objects as $object) {
            $fullList[$object->object_type]["list"][] = (object) array(
                "id" => $object->id,
                "title" => $object->objectable->title,
                "object_id" => $object->objectable->id
            );
        }

        foreach ($fullList as $listType) {
            usort($listType["list"], function ($firstItem, $secondItem) {
                if ($firstItem->title == $secondItem->title) {
                    return 0;
                }

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

        }

        $postData = request()->old($this->getName());
        $pickedList = array();
        if (!empty($postData)) {
            $pickedList = json_decode($postData);
        } elseif ($model->exists) {
            $websiteObjectModel = $model->getActiveWebsiteEntry();

            $query = DB::table($relation->getTable())
                ->where($relation->getQualifiedForeignPivotKeyName(), '=', $websiteObjectModel->id)
                ->select($relation->getRelatedPivotKeyName()." as id");

            if ($this->isOrderable()) {
                $query->orderBy($relation->getTable().".position", "asc");
            }

            $pickedList = $query->get();
        }

        return view(
            "admin.generic.components.forms.multiplepicker",
            array(
                "id" => $this->getName(),
                "name" => $this->getName(),
                "errorKey" => $this->getName(),
                "label" => $this->getLabel(),
                "order" => $this->isOrderable(),
                "maxSize" => $this->getMaxSize(),
                "fullList" => $fullList,
                "pickedList" => $pickedList,
                "multipleTypes" => $this->isMultipleTypes(),
                "hide" => $hidden,
                "enabled" => $enabled,
                "parentHidden" => ($hidden && !empty($this->parent)),
                "parent" => $this->getParent(),
                "allowInsert" => $this->getAllowInsert(),
                "websiteFiltered" => $this->isWebsiteFiltered(),
                "info" => $this->getInfo()
            ));
    }

    public function _toFormLongListNoWebsite($model, $enabled, $hidden)
    {
        $contentType = head($this->getContentTypes());
        $relationshipName = $this->name;
        $relationship = $model->$relationshipName();
        $relationTableName = $relationship->getTable();
        $relationModel = $relationship->getRelated();

        $query = $relationModel->full()
            ->select("id", "title", "id as object_id")
            ->join($relationTableName, $relationModel->getTable().'.'.$relationship->getRelatedKeyName(), "=",
                $relationship->getQualifiedRelatedPivotKeyName());

        if ($this->isOrderable()) {
            $query->orderBy("{$relationTableName}.position", "asc");
        } else {
            $query->orderBy("title", "asc");
        }

        $postData = request()->old($this->getName());

        if ($model->exists) {
            $query->where($relationship->getQualifiedForeignPivotKeyName(), '=', $model->id);
            $pickedList = $query->get();
        } elseif (!empty($postData) && is_array($postData)) {
            $query->whereIn($relationModel->getRelatedPivotKeyName(), $postData);
            $pickedList = $query->get();
        } else {
            $pickedList = $relationModel->newCollection();
        }


        $pickedList = array(
            $contentType->getType() =>
                array(
                    "label" => ucfirst($contentType->getSingularLabel()),
                    "list" => $pickedList
                )
        );

        return view(
            "admin.generic.components.forms.multiplepickersearch",
            array(
                "id" => $this->getName(),
                "name" => $this->getName(),
                "errorKey" => $this->getName(),
                "label" => $this->getLabel(),
                "order" => $this->isOrderable(),
                "maxSize" => $this->getMaxSize(),
                "pickedList" => $pickedList,
                "multipleTypes" => false,
                "hide" => $hidden,
                "enabled" => $enabled,
                "parentHidden" => ($hidden && !empty($this->parent)),
                "parent" => $this->getParent(),
                "allowInsert" => $this->getAllowInsert(),
                "websiteFiltered" => $this->isWebsiteFiltered(),
                "info" => $this->getInfo(),
                "searchUrl" => url("admin/content/auto-complete/".$contentType->getType())
            ));


    }

    public function _toFormNoWebsite($model, $enabled, $hidden)
    {
        $contentType = head($this->getContentTypes());

        $relationModelType = $contentType->getModelType();
        $relationModel = new $relationModelType;

        $query = $relationModel->full()
            ->select("id", "title", "id as object_id")->orderBy("title", "asc");
        $fullList = $query->get();

        $relationName = $this->getName();
        $relation = $model->$relationName();

        $fullList = array(
            $contentType->getType() =>
                array(
                    "label" => ucfirst($contentType->getSingularLabel()),
                    "list" => $fullList
                )
        );

        $postData = request()->old($this->getName());
        $pickedList = array();
        if (!empty($postData)) {
            $pickedList = json_decode($postData);
        } elseif ($model->exists) {
            $query = DB::table($relation->getTable())
                ->where($relation->getQualifiedForeignPivotKeyName(), '=', $model->id)
                ->select($relation->getQualifiedRelatedPivotKeyName()." as id");

            if ($this->isOrderable()) {
                $query->orderBy($relation->getTable().".position", "asc");
            }

            $pickedList = $query->get();
        }

        return view(
            "admin.generic.components.forms.multiplepicker",
            array(
                "id" => $this->getName(),
                "name" => $this->getName(),
                "errorKey" => $this->getName(),
                "label" => $this->getLabel(),
                "order" => $this->isOrderable(),
                "maxSize" => $this->getMaxSize(),
                "fullList" => $fullList,
                "pickedList" => $pickedList,
                "multipleTypes" => $this->isMultipleTypes(),
                "hide" => $hidden,
                "enabled" => $enabled,
                "parentHidden" => ($hidden && !empty($this->parent)),
                "parent" => $this->getParent(),
                "allowInsert" => $this->getAllowInsert(),
                "websiteFiltered" => $this->isWebsiteFiltered(),
                "info" => $this->getInfo()
            ));
    }

    public function getValidationData($copyAllLang = false)
    {
        $contentTypes = $this->getContentTypes();
        if ($this->isWebsiteFiltered() && empty($contentTypes)) {
            return null;
        }

        $validation = array(
            "rules" => array(),
            "values" => array(),
            "labels" => array()
        );

        $fieldName = $this->getName();
        $value = request()->input($fieldName);
        $value = json_decode($value, true);
        $rules = $this->getValidation();

        if (!empty($rules)) {
            $validation["rules"][$fieldName] = $this->getValidation();
            $validation["labels"][$fieldName] = $this->getLabel();
        }
        $validation["values"][$fieldName] = $value;
        return $validation;
    }

    public function save(Model $model, $values)
    {
        if ($this->isWebsiteFiltered()) {
            return $this->_saveWebsite($model, $values);
        } else {
            return $this->_saveNoWebsite($model, $values);
        }
    }

    protected function _saveNoWebsite($model, $values)
    {
        $relationName = $this->getName();

        $ids = Arr::flatten($values);

        if ($this->isOrderable()) {
            $syncData = array();
            $position = 1;
            foreach ($ids as $id) {
                $syncData[$id] = array("position" => $position);
                $position = $position + 1;
            }
            $model->$relationName()->sync($syncData);
        } else {
            $model->$relationName()->sync($ids);
        }
    }

    protected function _saveWebsite($model, $values)
    {
        if (empty($this->objectTypes)) {
            return;
        }

        $type = $this->getName();
        $websiteObject = $model->getActiveWebsiteEntry();
        $relation = $websiteObject->related();

        $ids = Arr::flatten($values);

        if ($this->isOrderable()) {
            $syncData = array();
            $position = 1;
            foreach ($ids as $id) {
                $syncData[$id] = array("position" => $position, "type" => $type);
                $position = $position + 1;
            }
            $relation->wherePivot("type", $type)->sync($syncData);
        } else {
            $syncData = array();
            foreach ($ids as $id) {
                $syncData[$id] = array("type" => $type);
            }
            $relation->wherePivot("type", $type)->sync($syncData);
        }
    }

    public function toArray()
    {
        $array = parent::toArray();
        $array["maxSize"] = $this->maxSize;
        $array["minSize"] = $this->minSize;
        return $array;
    }

    public static function getScript()
    {
        return array(
            "multiplePicker" =>
                path_asset('path.backoffice.js_path').'/view/generic/backend-multiple-picker.js',
            path_asset('path.backoffice.js_path').'/view/generic/backend-multiple-picker-search.js'
        );

    }
}


