<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Libs\Database\Eloquent\ContentTypeModel;
use App\Libs\Backoffice\Generic\ContentType;
use App\Facades\ContentTypes;
use Illuminate\Support\Collection;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use App\Models\GenericBackoffice\WebsiteObjectSearch;
use Illuminate\Support\Arr;

class ProcessSearch implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $contentType;
    protected $model;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct(ContentType $contentType, ContentTypeModel $model)
    {
        $this->contentType = $contentType;
        $this->model = $model;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        $parentStructure = $this->getContentTypeSearchParentStructure($this->contentType, get_class($this->model));
        if (count($parentStructure['parent']) > 0) {
            $formattedStructure = $this->structureUnsetIndexElements($parentStructure, 'model');
            $relationsDot = $this->dotalize($formattedStructure, '', 'parent', 'model');
            $undotalizedRelations = $this->undotalize($relationsDot);
            $relationsLoad = $this->getParentRelationsToLoad($undotalizedRelations);
            $this->model->load($relationsLoad);
            $relationIds = $this->getRelationIds($undotalizedRelations, $this->model, []);
        } else {
            $relationIds[get_class($this->model)] = [$this->model->id];
        }
        $saveData = $this->getChildrenSaveData($relationIds);
        foreach ($saveData as $line) {
            foreach ($line as $language => $data) {
                $websiteObjectSearch = WebsiteObjectSearch::where('website_object_id', $data['website_object_id'])->where('language_id', $language)->first();
                if ($websiteObjectSearch) {
                    if (empty($data['search_content'])) {
                        $websiteObjectSearch->delete();
                    } else {
                        $websiteObjectSearch->title = $data['title'];
                        $websiteObjectSearch->search_content = $data['search_content'];
                        $websiteObjectSearch->save();
                    }
                } else {
                    if (!empty($data['search_content']) && !is_null($data['website_object_id'])) {
                        $websiteObjectSearch = new WebsiteObjectSearch;
                        $websiteObjectSearch->language_id = $language;
                        $websiteObjectSearch->website_object_id = $data['website_object_id'];
                        $websiteObjectSearch->title = $data['title'];
                        $websiteObjectSearch->search_content = $data['search_content'];
                        $websiteObjectSearch->save();
                    }
                }
            }
        }
    }

    protected function getChildrenSaveData($relationIds)
    {
        $languages = getAvailableLanguages();
        $globalSearchData = [];
        foreach ($relationIds as $key => $ids) {
            foreach ($ids as $id) {
                $modelContentType = ContentTypes::findByModel($key);
                if ($modelContentType) {
                    $searchTitle = $modelContentType->getSearchTitle();
                    $searchFields = $modelContentType->getSearchFields() ?? [];
                    $searchIncludes = $modelContentType->getSearchIncludes() ?? [];
                    $searchRelations = $modelContentType->getSearchRelations() ?? [];
                    $modelQuery = $key::where('id', $id);
                    foreach ($searchIncludes as $includeKey => $values) {
                        $modelQuery->whereIn($includeKey, $values);
                    }
                    $model = $modelQuery->with(['websites' => function ($query) {
                        $query->where('status', 'published');
                    }])->first();
                    if ($model) {
                        $saveData = [];
                        if ($searchTitle) {
                            if (gettype($model->$searchTitle) === 'array') {
                                $modelSearchTitle = $model->$searchTitle['label'];
                            } else {
                                $modelSearchTitle = $model->$searchTitle;
                            }
                        } else {
                            $modelSearchTitle = '';
                        }
                        foreach ($languages as $lang => $language) {
                            $saveData[$lang] = [
                                'title' => $modelSearchTitle,
                                'search_content' => '',
                                'website_object_id' => $model->website_object_id
                            ];
                        }
                        foreach ($searchFields as $field) {
                            $contentTypeField = $modelContentType->getFields()->find($field);
                            if ($contentTypeField) {
                                if ($contentTypeField->isMultiLanguage()) {
                                    foreach ($model->languages as $modelLanguage) {
                                        if (gettype($modelLanguage->$field) === "array") {
                                            $field = $this->filterFieldEndTag($modelLanguage->$field['label'], '</pre>', ', ');
                                            if (!empty($field)) {
                                                $saveData[$modelLanguage->language_id]['search_content'] .= '<||>' . strip_tags($field) . '<||>';
                                            }
                                        } else {
                                            $field = $this->filterFieldEndTag($modelLanguage->$field, '</p>', ', ');
                                            if (!empty($field)) {
                                                $saveData[$modelLanguage->language_id]['search_content'] .= '<||>' . strip_tags($field) . '<||>';
                                            }
                                        }
                                    }
                                } else {
                                    foreach ($saveData as $language => $value) {
                                        if (gettype($model->$field) === "array") {
                                            $field = $this->filterFieldEndTag($model->$field['label'], '</p>', ', ');
                                            if (!empty($field)) {
                                                $saveData[$language]['search_content'] .= '<||>' . strip_tags($field) . '<||>';
                                            }
                                        } else {
                                            $field = $this->filterFieldEndTag($model->$field, '</p>', ', ');
                                            if (!empty($field)) {
                                                $saveData[$language]['search_content'] .= '<||>' . strip_tags($field) . '<||>';
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        foreach ($searchRelations as $relation) {
                            $model->load($relation);

                            if (empty($model->$relation)) {
                                continue;
                            }

                            if ($model->$relation instanceof Collection) {
                                $collection = $model->$relation;
                            } else if (!empty($model->$relation)) {
                                $collection = $model->$relation->newCollection([$model->$relation]);
                            }

                            $this->getChildrenRelationsSaveData($collection, $saveData);
                        }
                        $globalSearchData[] = $saveData;
                    }
                }
            }
        }
        return $globalSearchData;
    }

    protected function getChildrenRelationsSaveData($collection, &$saveData)
    {
        $searchRelations = [];
        foreach ($collection as $model) {
            $className = get_class($model);
            $modelContentType = ContentTypes::findByModel($className);
            if ($modelContentType) {
                $searchFields = $modelContentType->getSearchFields() ?? [];
                $searchIncludes = $modelContentType->getSearchIncludes() ?? [];
                $searchRelations = $modelContentType->getSearchRelations() ?? [];
                $modelQuery = $className::where('id', $model->id);
                foreach ($searchIncludes as $includeKey => $values) {
                    $modelQuery->whereIn($includeKey, $values);
                }
                $model = $modelQuery->first();
                if ($model) {
                    foreach ($searchFields as $field) {
                        $contentTypeField = $modelContentType->getFields()->find($field);
                        if ($contentTypeField) {
                            if ($contentTypeField->isMultiLanguage()) {
                                foreach ($model->languages as $modelLanguage) {
                                    if (gettype($modelLanguage->$field) === "array") {
                                        $field = $this->filterFieldEndTag($modelLanguage->$field['label'], '</p>', ', ');
                                        if (!empty($field)) {
                                            $saveData[$modelLanguage->language_id]['search_content'] .= '<||>' . strip_tags($field) . '<||>';
                                        }
                                    } else {
                                        $field = $this->filterFieldEndTag($modelLanguage->$field, '</p>', ', ');
                                        if (!empty($field)) {
                                            $saveData[$modelLanguage->language_id]['search_content'] .= '<||>' . strip_tags($field) . '<||>';
                                        }
                                    }
                                }
                            } else {
                                foreach ($saveData as $language => $value) {
                                    if (gettype($model->$field) === "array") {
                                        $field = $this->filterFieldEndTag($model->$field['label'], '</p>', ', ');
                                        if (!empty($field)) {
                                            $saveData[$language]['search_content'] .= '<||>' . strip_tags($field) . '<||>';
                                        }
                                    } else {
                                        $field = $this->filterFieldEndTag($model->$field, '</p>', ', ');
                                        if (!empty($field)) {
                                            $saveData[$language]['search_content'] .= '<||>' . strip_tags($field) . '<||>';
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        foreach ($searchRelations as $relation) {
            $collection->load($relation);
            foreach ($collection as $collectionModel) {
                if ($collectionModel->$relation instanceof Collection) {
                    $relationCollection = $collectionModel->$relation;
                } else {
                    $relationCollection = $collectionModel->newCollection($collectionModel->$relation);
                }
                $this->getChildrenRelationsSaveData($relationCollection, $saveData);
            }
        }
    }

    protected function filterFieldEndTag($field, $endTag, $replaceString)
    {
        if (strpos($field, $endTag) !== false) {
            $explodedContent = array_filter(explode($endTag, $field));
            foreach ($explodedContent as $key => $line) {
                $explodedContent[$key] = strip_tags($line);
            }
            return implode($replaceString, $explodedContent);
        } else {
            return $field;
        }
    }

    protected function mapContentTypeModels($className)
    {
        $modelNameExploded = explode('\\', $className);
        switch ($modelNameExploded[count($modelNameExploded) - 1]) {
            case 'SimpleLink':
                $modelNameExploded[count($modelNameExploded) - 1] = 'WebLink';
            break;
        }
        return implode('\\', $modelNameExploded);
    }

    protected function reverseDotalized($dotalizedArray)
    {
        $reversedDotalizedArray = [];
        foreach ($dotalizedArray as $key => $dotalized) {
            $array = explode('.', $key);
            array_pop($array);
            $dotalized = '';
            $arrayLength = count($array) - 1;
            for ($i = $arrayLength; $i >= 0; $i--) {
                $dotalized = $i == $arrayLength ? $array[$i] : $dotalized . '.' . $array[$i];
            }
            $reversedDotalizedArray[$dotalized] = [];
        }
        return $reversedDotalizedArray;
    }

    protected function getRelationIds($relations, $model, $ids = [])
    {
        $relationIds = $ids;
        if (empty($relations)) {
            if (is_a($model, EloquentCollection::class)) {
                foreach ($model as $item) {
                    $relationIds[get_class($item)][] = $item->id;
                }
            } else {
                $relationIds[get_class($model)][] = $model->id;
            }
        } else {
            foreach ($relations as $key => $relation) {
                if (is_array($relation) && !empty($relation)) {
                    if (is_a($model->$key, EloquentCollection::class)) {
                        foreach ($model->$key as $modelRelation) {
                            $relationIds = $this->getRelationIds($relation, $modelRelation, $relationIds);
                        }
                    } else {
                        $relationIds = $this->getRelationIds($relation, $model->$key, $relationIds);
                    }
                } else {
                    $relationIds = $this->getRelationIds([], $model->$key, $relationIds);
                }
            }
        }
        return $relationIds;
    }

    protected function getParentRelationsToLoad($array)
    {
        $results = [];
        $self = $this;
        foreach ($array as $key => $value) {
            $results[$key] = function ($query) use ($self, $value) {
                $query->noWebsiteNoLang();
                $withValues = $self->getParentRelationsToLoad($value);
                if (!empty($withValues)) {
                    $query->with($withValues);
                }
            };
        }
        return $results;
    }

    protected function structureUnsetIndexElements($array, $index)
    {
        foreach ($array as $key => $value) {
            if (!is_array($value)) {
                unset($array[$index]);
            } else {
                $array[$key] = $this->structureUnsetIndexElements($value, 'model');
            }
        }
        return $array;
    }

    protected function undotalize($dotNotationArray)
    {
        $array = [];
        foreach ($dotNotationArray as $key => $value) {
            Arr::set($array, $key, $value);
        }
        return $array;
    }

    protected function dotalize($array, $prepend = '')
    {
        $results = [];
        foreach ($array as $key => $value) {
            if (is_array($value) && !empty($value)) {
                if ($key == 'parent') {
                    $results = array_merge($results, $this->dotalize($value, $prepend));
                } else {
                    $results = array_merge($results, $this->dotalize($value, $prepend.$key.'.'));
                }
            } else {
                if (empty($value)) {
                    $prepend = rtrim($prepend, '.');
                    $results[$prepend] = $value;
                } else {
                    $results[$prepend.$key] = $value;
                }
            }
        }

        return $results;
    }

    protected function getContentTypeSearchParentStructure(ContentType $contentType, $modelPath)
    {
        $parentStructure = [
            'model' => $modelPath,
            'parent' => [],
        ];
        $parentRelationships = $contentType->getSearchParentRelationships();
        if (!is_null($parentRelationships)) {
            $parentStructure['parent'] = $this->getParentRelationshipHierarchy($parentStructure['parent'], $parentRelationships, $contentType);
        }
        return $parentStructure;
    }

    protected function getParentRelationshipHierarchy($structure, $parentRelations, $contentType)
    {
        foreach ($parentRelations as $parentRelation) {
            $contentTypeModelPath = $contentType->getModelType();
            $model = new $contentTypeModelPath();
            $relatedModel = $model->$parentRelation()->getRelated();
            $relationContentType = ContentTypes::findByModel(get_class($relatedModel));
            $relationParentRelationships = $relationContentType->getSearchParentRelationships();
            $structure[$parentRelation] = [
                'model'  => get_class($relatedModel),
                'parent' => [],
            ];
            if ($relationParentRelationships) {
                $structure[$parentRelation]['parent'] = $this->getParentRelationshipHierarchy($structure[$parentRelation]['parent'], $relationParentRelationships, $relationContentType);
            }
        }
        return $structure;
    }
}
