<?php

namespace Hilco\Models;

use Arr;
use DB;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Collection;
use Request;

/**
 * Class WebFamily
 * @package Hilco\Models
 * @property string $name
 * @property string $slug
 * @property string $detail
 * @property boolean $is_visible
 * @property boolean $search_only
 * @property integer $sort_priority
 * @property boolean $best_seller
 * @property string $label
 * @property string $family_desc
 * @property string $product_tagline
 * @property boolean $is_prescription
 * @method static WebFamily firstOrNew(array $attributes)
 */
class WebFamily extends WebModel implements HasLandingPageInterface, HasWebSiloOverridesInterface
{
    protected $table = "WebFamilies";
    protected $fillable = ['name', 'slug', 'detail', 'webCollectionIds', 'webAttributeIds', 'is_visible', 'search_only', 'sort_priority', 'best_seller', 'label', 'metadata_title', 'metadata_description', 'family_desc', 'product_tagline', 'is_prescription'];
    protected $perPage = 24;
    protected $casts = [
        ['is_visible' => 'boolean', 'is_prescription' => 'boolean'],
    ];
    protected $with = ['webSiloOverride'];
    protected $hierarchyParent = WebCollection::class;

    public static $perEnvironment = true;

    public function getSolrRecord($webSilos = false) {
        if ($webSilos === false) {
            $webSilos = [WebSilo::find(1)];
        }

        $record['id']       = $this->id;
        $record['name']     = $this->name;
        $record['slug']     = $this->slug;

        // Add language localized versions of translatable columns
        foreach ($this->translations as $webModelTranslation) {
            $columnName = $webModelTranslation->column_name;
            $language = $webModelTranslation->language;
            $value = $webModelTranslation->translation;
            if ($columnName != 'detail') {
                if ($columnName == 'name') {
                    $record['name_txt_' . $language] = $this->deduplicateValue($this->sanitizeValue($value));
                } else {
                    $transKey = $columnName . '_txt_' . $language; // e.g., 'name_txt_en', 'name_txt_fr'
                    $record[$transKey] = $value;
                }
            } else {
                $record['detail_txt_' . $language] = $this->deduplicateValue($this->sanitizeValue($value));
            }
        }

        // Add "searchable" fields with non-alphanumeric characters stripped out of the values
        // /[^A-Za-z0-9\x{00C0}-\x{00FF}\x{1E9E}]/u matches all non-alphanumerics, the 00C0-00FF range of Unicode Latin-1 Supplement characters, and 1E9E (ß) from Unicode Latin Extended Additional
        foreach ($record as $key => $value) {
            $record[$key."_searchable"] = preg_replace("/[^A-Za-z0-9\x{00C0}-\x{00FF}\x{1E9E}]/u", '', $value);
        }

        // Populate list of microsites for which this item should be valid and visible
        $record['webSiloIds'] = [];
        if ($webSilos === null) {
            $webSilos = WebSilo::visible()->get();
        }
        foreach ($webSilos as $webSilo) {
            if ($this->visibleInSilo($webSilo)) {
                $record['webSiloIds'][] = $webSilo->id;
            }
        }


        $webCollections = $this->webCollections;

        foreach ($webCollections as $webCollection) {
            $webCategories = $webCollection->webCategories;
            foreach ($webCategories as $webCategory) {
                $webGroups = $webCategory->webGroups;
                foreach ($webGroups as $webGroup) {
                    if ($webGroup->is_menubar == 1) {
                        $webHierarchies = $webGroup->webHierarchies;
                        foreach ($webHierarchies as $webHierarchy) {
                            $webHierarchyId = $webHierarchy->id;

                            if (!isset($record["webGroupIds$webHierarchyId"])) {
                                $record["webGroupIds$webHierarchyId"] = [];
                            }
                            if (!isset($record["webCategoryIds$webHierarchyId"])) {
                                $record["webCategoryIds$webHierarchyId"] = [];
                            }
                            if (!isset($record["webCollectionIds$webHierarchyId"])) {
                                $record["webCollectionIds$webHierarchyId"] = [];
                            }

                            if (!in_array($webGroup->id, $record["webGroupIds$webHierarchyId"])) {
                                $record["webGroupIds$webHierarchyId"][] = $webGroup->id;
                                foreach ($webGroup->translations as $groupTranslation) {
                                    $columnName = $groupTranslation->column_name;
                                    if ($columnName == 'name') {
                                        $language = $groupTranslation->language;
                                        $groupNameTranslation = $groupTranslation->translation;
                                        $groupNameKey = "web_group_${webHierarchyId}_text_$language";
                                        if (!isset($record[$groupNameKey])) {
                                            $record[$groupNameKey] = [];
                                        }
                                        $record[$groupNameKey][] = $this->sanitizeValue($groupNameTranslation);
                                    }
                                }
                            }
                            if (!in_array($webCategory->id, $record["webCategoryIds$webHierarchyId"])) {
                                $record["webCategoryIds$webHierarchyId"][] = $webCategory->id;
                                foreach ($webCategory->translations as $categoryTranslation) {
                                    $columnName = $categoryTranslation->column_name;
                                    if ($columnName == 'name') {
                                        $language = $categoryTranslation->language;
                                        $categoryNameTranslation = $categoryTranslation->translation;
                                        $categoryNameKey = "web_category_${webHierarchyId}_text_$language";
                                        if (!isset($record[$categoryNameKey])) {
                                            $record[$categoryNameKey] = [];
                                        }
                                        $record[$categoryNameKey][] = $this->sanitizeValue($categoryNameTranslation);
                                    }
                                }
                            }
                            if (!in_array($webCollection->id, $record["webCollectionIds$webHierarchyId"])) {
                                $record["webCollectionIds$webHierarchyId"][] = $webCollection->id;
                                foreach ($webCollection->translations as $collectionTranslation) {
                                    $columnName = $collectionTranslation->column_name;
                                    if ($columnName == 'name') {
                                        $language = $collectionTranslation->language;
                                        $collectionNameTranslation = $collectionTranslation->translation;
                                        $collectionNameKey = "web_collection_${webHierarchyId}_text_$language";
                                        if (!isset($record[$collectionNameKey])) {
                                            $record[$collectionNameKey] = [];
                                        }
                                        $record[$collectionNameKey][] = $this->sanitizeValue($collectionNameTranslation);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        $webParts = $this->webParts;

        $webAttributeValues = [];
        $partNumbers = [];
        $legacyPartNumbers = [];
        $hasAutoship = false;
        foreach ($webParts as $webPart) {
            foreach ($webPart->webAttributeWebParts as $webAttributeWebPart) {
                $webAttribute = $webAttributeWebPart->webAttribute;
                if (isset($webAttribute)) {
                    foreach ($webAttributeWebPart->translations as $webAttributeValueTranslation) {
                        $attrNameKey = 'webAttribute' . $webAttribute->id . '_' . $webAttributeValueTranslation->language. '_str';
                        $attributeValue = $webAttributeValueTranslation->translation;
                        if (!isset($record[$attrNameKey]) || !in_array($attributeValue, $record[$attrNameKey])) {
                            $record[$attrNameKey][] = $attributeValue;

                            if (!isset($webAttributeValues[$webAttributeValueTranslation->language])) {
                                $webAttributeValues[$webAttributeValueTranslation->language] = [];
                            }
                            $webAttributeValues[$webAttributeValueTranslation->language][] = $this->sanitizeValue($attributeValue);
                        }
                    }
                }
            }
            $partNumbers[] = $webPart->part_number;
            $legacyPartNumber = Arr::get($webPart, 'part.legacy_part_no', '');
            if (strlen($legacyPartNumber)) $legacyPartNumbers[] = $legacyPartNumber;
            if ($webPart->has_subscribe_and_save) $hasAutoship = true;
        }

        $hasNonRX = false;
        foreach ($webParts as $webPart) {
            if ($webPart->part && !$webPart->part->is_rx) {
                $hasNonRX = true;
                break;
            }
        }

        foreach ($webAttributeValues as $langCode => $attrValues) {
            $record['attribute_values_txt_' . $langCode] = implode(' ', array_unique($attrValues));
        }
        $record['is_rx'] = !$hasNonRX;
        $record['b2b_web_visible'] = $this->is_solr_visible;
        $record['search_only'] = (bool)$this->search_only;

        // Populate keywords strings by language (separated by space)
        $internalKeywords = $this->webFamilyKeywords->where('keyword_type', 'internal'); // ->get() not necessary here
        foreach ($internalKeywords as $internalKeyword) {
            $keywordKey = 'keywords_txt_' . $internalKeyword->language_code;
            if (!isset($record[$keywordKey])) {
                $record[$keywordKey] = $internalKeyword->keyword;
            } else {
                $record[$keywordKey] = $record[$keywordKey] . " " . $internalKeyword->keyword;
            }
        }

        $record['part_numbers_txt'] = $this->deduplicateValue(implode(' ', $partNumbers));
        $record['legacy_part_numbers_txt'] = $this->deduplicateValue(implode(' ', $legacyPartNumbers));
        $record['sort_priority'] = (int)$this->sort_priority;
        foreach (config('webfamilylabels.languages') as $langCode) {
            $record["label_$langCode"] = (string) config("webfamilylabels.$langCode.{$this->label}");
        }

        $record['autoship'] = (bool)$hasAutoship;
        return $record;
    }

    private function deduplicateValue($value) {
        return implode(' ', array_unique(explode(' ', $value)));
    }

    private function sanitizeValue($value) {
        return trim(preg_replace('/\s+/', ' ', preg_replace("/[^0-9a-zA-Z&\-\/'\x{00C0}-\x{00FF}\x{1E9E} ]/u", '', preg_replace('/\s+/', ' ', strip_tags(html_entity_decode($value))))));
    }

    public function solrTable()
    {
        return $this->hasMany(WebFamily_Solr::class, 'webFamily_id', 'id');
    }

    public function getSolrClassAttribute()
    {
        return WebFamily_Solr::class;
    }

    public function getSolrIDFieldAttribute()
    {
        return 'webFamily_id';
    }

    public function getIsSolrVisibleAttribute()
    {
        return static::solrVisible()->where('id', $this->id)->count() > 0;
    }

    public function scopeSolrVisible(Builder $query)
    {
        return
            $query
                ->isVisible()
                ->hasSolrVisibleParents()
                ->hasSolrVisibleChildren();
    }

    public function scopeHasSolrVisibleChildren(Builder $query)
    {
        return $query
            ->whereHas('webParts', function ($query) {
                return $query->isVisible()->hasSolrVisibleChildren();
            });
    }

    public function scopeHasSolrVisibleParents(Builder $query)
    {
        return $query
            ->whereHas('webCollections', function ($query) {
                return $query->isVisible()->hasSolrVisibleParents();
            });
    }

    public function webFamilyProductFeatures() {
        return $this->hasMany(WebFamilyProductFeature::class, 'webfamily_id', 'id');
    }

    public function webFamilyKeywords()
    {
        return $this->hasMany(WebFamilyKeyword::class, 'webfamily_id', 'id');
    }

    public function webFamilyKeywordsInternal()
    {
        return $this->hasMany(WebFamilyKeyword::class, 'webfamily_id', 'id')->where('keyword_type', 'internal');
    }

    public function getPerPage()
    {
        $tmpPerPage = Request::input('perPage');
        return (isset($tmpPerPage) && $tmpPerPage > 0)?$tmpPerPage:$this->perPage;
    }

    public function webParts() {
        return $this->belongsToMany(WebPart::class, 'WebPart_WebFamily','webfamily_id', 'webpart_id')
            ->withPivot(['is_family_image'])
            ->wherePivot('deleted_at', '=', '0000-00-00 00:00:00');
    }

    public function webDetailTabs() {
        return $this->belongsToMany(WebDetailTab::class, 'WebDetailTab_WebFamily', 'webfamily_id', 'webdetailtab_id')->wherePivot('deleted_at', '=', '0000-00-00 00:00:00')->withPivot('is_visible');
    }

    public function visibleWebDetailTabs()
    {
        return $this->webDetailTabs()->wherePivot('is_visible', 1);
    }

    public function relatedParent() {
        return $this->morphToMany(WebFamily::class, 'related', 'WebFamily_Related', 'related_id', 'webfamily_id')->wherePivot('deleted_at', '=', '0000-00-00 00:00:00')->withPivot(['related_category']);
    }

    public function relatedFamilies() {
        return $this->morphedByMany(WebFamily::class, 'related', 'WebFamily_Related', 'webfamily_id', 'related_id')->wherePivot('deleted_at', '=', '0000-00-00 00:00:00')->webVisible()->withPivot(['related_category']);
    }

    public function relatedParts() {
        return $this->morphedByMany(WebPart::class, 'related', 'WebFamily_Related', 'webfamily_id', 'related_id')->wherePivot('deleted_at', '=', '0000-00-00 00:00:00')->webVisible()->withPivot(['related_category']);
    }

    public function  webFamilyRelateds() {
        return $this->hasMany(WebFamily_Related::class, 'webfamily_id', 'id');
    }

    /**
     * @return BelongsToMany
     */
    public function favoritedWebUsers(): BelongsToMany {
        return $this
            ->belongsToMany(WebUser::class, WebFamily_WebUser::class, 'webfamily_id', 'webuser_id')
            ->wherePivot('deleted_at', '=', '0000-00-00 00:00:00')
            ->without('customer.segments');
    }

    public function isFavorite()
    {
        return $this->favoritedWebUsers->where('id', auth()->id())->count() ? true : false;
    }

    public function getRelatedAttribute()
    {
        return $this->relatedFamilies->where('pivot.related_category', 'related');
    }

    public function getAccessoriesAttribute()
    {
        $result = $this->relatedFamilies->where('pivot.related_category', 'accessory');
        $result->merge($this->relatedParts->where('pivot.related_category', 'accessory'));

        $filteredResult = collect();
        foreach ($result as $family) {
            if ($family->visibleWebParts->count()) {
                $filteredResult->push($family);
            }
        }

        return $filteredResult->sortBy('name');
    }

    public function getAlsoLikeAttribute()
    {
        $alsoLikeFamilies =
            $this->relatedFamilies
                ->where('pivot.related_category', 'also_like')
                ->filter(function ($value, $key) {
                    $visibleWebParts = $value->visibleWebParts;
                    return isset($visibleWebParts) && count($visibleWebParts);
                })
        ;
        return $alsoLikeFamilies->sortBy('name');
    }

    public function alsoLikeFamilies($limit = 4)
    {
        $currentWebCollection = $this->webCollections->first();
        $currentWebCategory = ($currentWebCollection) ? $currentWebCollection->webCategories->first() : false;
        $currentWebGroup = ($currentWebCategory) ? $currentWebCategory->webGroups->first() : false;

        $notIn = [$this->id];

        $alsoLike = $this->also_like;
        if (is_null($alsoLike)) $alsoLike = new Collection;
        $alCount = $alsoLike->count();
        if ($alCount > 4) $alsoLike = $alsoLike->random($limit);

        if ($alCount < 4) {
            $newItems = ($currentWebGroup)
                ? WebFamily::whereNotIn('id', $notIn)->inGroup($currentWebGroup)->webVisible()->inRandomOrder()->limit($limit - $alCount)->get()
                : WebFamily::whereNotIn('id', $notIn)->webVisible()->inRandomOrder()->limit($limit - $alCount)->get();
            $alsoLike = $alsoLike->merge($newItems);
        }

        return $alsoLike->shuffle();
    }

    public function getAccessoryProductsAttribute() {
        $relateds = collect();
        foreach (['relatedFamilies','relatedParts'] as $type) {
            $relatedsOfType = $this->$type()->wherePivot('related_category', '=', 'accessory')->get();
            foreach ($relatedsOfType as $related) {
                $relateds->push($related);
            }
        }
        return $relateds->shuffle();
    }

    public function getRelatedProductsAttribute() {
        $relateds = collect();
        foreach (['relatedFamilies','relatedParts'] as $type) {
            $relatedsOfType = $this->$type()->wherePivot('related_category', '=', 'related')->get();
            foreach ($relatedsOfType as $related) {
                $relateds->push($related);
            }
        }
        return $relateds->shuffle();
    }

    public function getSubstituteProductsAttribute() {
        $relateds = collect();
        foreach (['relatedFamilies','relatedParts'] as $type) {
            $relatedsOfType = $this->$type()->wherePivot('related_category', '=', 'substitute')->get();
            foreach ($relatedsOfType as $related) {
                $relateds->push($related);
            }
        }
        return $relateds->shuffle();
    }

    public function getSuggestedProductsAttribute() {
        $relateds = collect();
        foreach (['relatedFamilies','relatedParts'] as $type) {
            $relatedsOfType = $this->$type()->wherePivot('related_category', '=', 'also_like')->get();
            foreach ($relatedsOfType as $related) {
                $relateds->push($related);
            }
        }
        return $relateds->shuffle();
    }

    public function visibleWebParts() {
        return $this->webParts()->isVisible()->hasVisibleChildren()
            ->with(['part' => function ($query) {
                $query->with('productFamily.productCategory.productCategoryGroup');
                $query->with('webSilos');
                $query->with(['inventoryItems' => function ($query) {
                    $query->inSilo();
                }]);
            }])
            ->with(['assets' => function ($query) {
                return $query->wherePivot('assetable_join_type', 'primary');
            }])
            ->with(['webAttributes'])
            ->with('webAttributeWebParts.webAttribute')
            ->with('webAttributeWebParts.translations')
            ->with(['partAsset'])
            ->orderBy('name');
    }

    public function visibleWebPartsCondensed(){
        return $this->webParts()->webVisible()->orderBy('name');
    }

    public function scopeDisplayOrder(Builder $query)
    {
        return $query->orderBy('pivot_display_order', 'asc')->orderBy('name', 'asc');
    }

    public function scopeAlphabetical(Builder $query)
    {
        return $query->orderBy('name', 'asc');
    }

    /**
     * @return BelongsToMany
     */
    public function webCollections() {
        return $this->belongsToMany(WebCollection::class, 'WebFamily_WebCollection', 'webfamily_id', 'webcollection_id')
            ->wherePivot('deleted_at', '=', '0000-00-00 00:00:00')
            ->withPivot(['is_featured', 'display_order']);
    }

    public function webCollectionsListWithPaths()
    {
        $output = [];
        $webCollections = $this->webCollections()->with('webCategories.webGroups.webHierarchies')->without('webSiloOverride')->get()->keyBy('id');
        foreach ($webCollections as $id => $webCollection) {
            foreach ($webCollection->hierarchyPaths as $path) {
                $string = $webCollection->name;
                $string .= ' (' . $path['webCategory']->name;
                $string .= ' -> ' . $path['webGroup']->name;
                $string .= ' -> ' . $path['webHierarchy']->name . ')';
                $output[$id] = $string;
                break;
            }
        }
        return $output;
    }

    /**
     * @return BelongsToMany
     */
    public function webAttributes()
    {
        return $this
            ->belongsToMany(WebAttribute::class, 'WebAttribute_WebFamily', 'webfamily_id', 'webattribute_id')
            ->wherePivot('deleted_at', '=', '0000-00-00 00:00:00')
            ->withPivot(['is_visible', 'display_order', 'is_thumbnail', 'is_filterable', 'is_select'])
            ->orderBy('pivot_display_order');
    }

    public function getWebAttributeIdsAttribute()
    {
        return $this->webAttributes->pluck('id')->toArray();
    }

    public function getWebCollectionIdsAttribute()
    {
        return $this->webCollections->pluck('id')->toArray();
    }

    public function setWebAttributeIdsAttribute($ids) {
        $this->webAttributes()->sync($ids);
    }

    public function setWebCollectionIdsAttribute($ids) {
        $this->webCollections()->syncWithoutDetaching($ids);
    }

    public static function deduplicatedList()
    {
        $webFamilies = WebFamily::with('webCollections.webCategories.webGroups.webHierarchies')->without('webSiloOverride')->get()->sortBy('name')->keyBy('id');
        $output = [];
        foreach ($webFamilies as $id => $webFamily) {
            foreach ($webFamily->hierarchyPaths as $path) {
                $string = $webFamily->name;
                $string .= ' (' . $path['webCollection']->name;
                $string .= ' ->' . $path['webCategory']->name;
                $string .= ' -> ' . $path['webGroup']->name;
                $string .= ' -> ' . $path['webHierarchy']->name . ')';
                $output[$id] = $string;
                break;
            }
        }
        return collect($output);
    }

    public function getPriceRangeAttribute()
    {
        if (is_null($this->visibleWebParts)) return null;
        $min = PHP_INT_MAX;
        $max = 0;
        foreach ($this->visibleWebParts as $webPart) {
            $min = min($min, $webPart->customer_price);
            $max = max($max, $webPart->customer_price);
        }
        return [$min, $max];
    }

    public function getPriceAttribute()
    {
        try {
            if (is_null($this->visibleWebParts)) return null;
            return $this->visibleWebParts->first()->part->prices->first()->price;
        } catch (\Exception $e) {
            return null;
        }
    }

    public function getDisplayNameForUACAttribute()
    {
        return $this->name;
    }

    public function getNameAttribute()
    {
        if (config('hilco.ignoreActiveWebSilo')) {
            return $this->getTranslation('name', AvailableLanguage::DEFAULT_LANG_CODE, $this->attributes['name']);
        } else {
            if (isset($this->webSiloOverride) &&
                $this->webSiloOverride->is_visible &&
                !is_null($this->webSiloOverride->name) &&
                trim($this->webSiloOverride->name) != '') {

                return $this->webSiloOverride->name;
            } else {
                return $this->getTranslation('name');
            }
        }
    }

    public function getNameForEditAttribute()
    {
        return $this->getTranslation('name', AvailableLanguage::DEFAULT_LANG_CODE, $this->attributes['name']);
    }

    public function getDetailAttribute()
    {
        if (config('hilco.ignoreActiveWebSilo')) {
            return $this->getTranslation('detail', AvailableLanguage::DEFAULT_LANG_CODE, $this->attributes['detail']);
        } else {
            if (isset($this->webSiloOverride) &&
                $this->webSiloOverride->is_visible &&
                !is_null($this->webSiloOverride->detail)) {

                return $this->webSiloOverride->detail;
            } else {
                return $this->getTranslation('detail');
            }
        }
    }

    public function getFamilyDescAttribute() {
        if (config('hilco.ignoreActiveWebSilo')) {
            return $this->getTranslation('family_desc', AvailableLanguage::DEFAULT_LANG_CODE, $this->attributes['family_desc']);
        } else {
            return $this->getTranslation('family_desc');
        }
    }

    public function visibleInSilo($webSilo, $processDownChain = true, $processUpChain = true)
    {
        $isPartVisible = !$processDownChain;
        $isCollectionVisible = !$processUpChain;

        if (!$webSilo instanceof WebSilo) $webSilo = WebSilo::find($webSilo);

        if (count($this->webSiloOverrides)) {
            foreach ($this->webSiloOverrides as $webSiloOverride) {
                if ($webSiloOverride->websilo_id == $webSilo->id && $webSiloOverride->is_visible == 0) {
                    return false;
                }
            }
        }

        if ($processDownChain) {
            foreach ($this->webParts as $webPart) {
                if ($webPart->visibleInSilo($webSilo, true, false)) {
                    $isPartVisible = true;
                    break;
                }
            }
        }

        if ($processUpChain) {
            foreach ($this->webCollections as $webCollection) {
                if ($webCollection->visibleInSilo($webSilo, false, true)) {
                    $isCollectionVisible = true;
                    break;
                }
            }
        }

        return ($isPartVisible && $isCollectionVisible);
    }

    public function getCustomerPriceAttribute() {
        $part = $this->visibleWebParts->first();
        if (is_null($part)) return null;
        return $part->customer_price;
    }

    public function getHierarchyPathsAttribute()
    {
        $paths = [];
        foreach ($this->webCollections as $webCollection) {
            foreach ($webCollection->webCategories as $webCategory) {
                foreach ($webCategory->webGroups as $webGroup) {
                    foreach ($webGroup->webHierarchies as $webHierarchy) {
                        $paths[] = [
                            'webHierarchy' => $webHierarchy,
                            'webGroup' => $webGroup,
                            'webCategory' => $webCategory,
                            'webCollection' => $webCollection,
                        ];
                    }
                }
            }
        }
        return $paths;
    }

    public function scopeHasAttributes($query, $filters)
    {
        if (!isset($filters) || is_null($filters)) return $query;
        $attrs = [];
        foreach ($filters as $filter) {
            if (!isset($attrs[$filter->webattribute_id])) {
                $attrs[$filter->webattribute_id] = [];
            }
            $attrs[$filter->webattribute_id] = array_merge($attrs[$filter->webattribute_id], $filter->webAttributeFilterValues->pluck('filter_value')->toArray());
        }
        return $query->where(function ($query) use ($attrs) {
            foreach ($attrs as $id => $values) {
                $query->whereHas('webAttributes', function ($query) use ($id, $values) {
                    return $query->where('webattribute_id', $id);
                });
            }
            return $query->whereHas('visibleWebParts', function ($query) use ($attrs) {
                foreach ($attrs as $id => $values) {
                    $query = $query->whereHas('webAttributes', function ($query) use ($id, $values) {
                        return $query->where('webattribute_id', $id)->whereIn('attribute_value', $values);
                    });
                }
                return $query;
            });
        });
    }

    public function scopeManagedBy($query, $product_manager)
    {
        return $query->whereHas('webParts', function ($query) use ($product_manager) {
            return $query->managedBy($product_manager);
        });
    }

    public function scopeInCollection($query, $webCollection)
    {
        $webCollectionId = ($webCollection instanceof WebCollection) ? $webCollection->id : $webCollection;
        return $query->whereHas('webCollections', function ($query) use ($webCollectionId) {
            return $query->where('webcollection_id', $webCollectionId);
        });
    }

    public function scopeInCategory($query, $webCategory)
    {
        $webCategoryId = ($webCategory instanceof WebCategory) ? $webCategory->id : $webCategory;
        return $query->whereHas('webCollections', function ($query) use ($webCategoryId) {
            return $query->inCategory($webCategoryId);
        });
    }

    public function scopeInGroup($query, $webGroup)
    {
        $webGroupId = ($webGroup instanceof WebGroup) ? $webGroup->id : $webGroup;
        return $query->whereHas('webCollections', function ($query) use ($webGroupId) {
            return $query->inGroup($webGroupId);
        });
    }

    public function scopeInSilo($query, $webSilo)
    {
        $webSiloId = ($webSilo instanceof WebSilo) ? $webSilo->id : $webSilo;
        return $query->whereHas('webCollections', function ($query) use ($webSiloId) {
            return $query->inSilo($webSiloId);
        });
    }

    public function scopeFilterCorrectly($query, $model)
    {
        if ($model instanceof WebGroup) {
            $webGroupId = $model->id;
            return $query->where('WebGroups.id', '=', $webGroupId);
        } else if ($model instanceof WebCategory) {
            $webCategoryId = $model->id;
            $defaultWebGroup = $model->webGroups->first();

            if (empty(session()->get('breadcrumb')) || empty(session()->get('breadcrumb.groupId'))) {
                $webGroupId = $defaultWebGroup->id;
            } else {
                $webGroupId = session()->get('breadcrumb.groupId');
            }

            return
                $query
                    ->where('WebCategories.id', '=', $webCategoryId)
                    ->where('WebGroups.id', '=', $webGroupId);
        } else if ($model instanceof WebCollection) {
            $webCollectionId = $model->id;
            $defaultWebCategory = $model->webCategories->first();
            $defaultWebGroup = $defaultWebCategory->webGroups->first();

            if (empty(session()->get('breadcrumb')) || empty(session()->get('breadcrumb.categoryId'))) {
                $webCategoryId = $defaultWebCategory->id;
            } else {
                $webCategoryId = session()->get('breadcrumb.categoryId');
            }

            if (empty(session()->get('breadcrumb')) || empty(session()->get('breadcrumb.groupId'))) {
                $webGroupId = $defaultWebGroup->id;
            } else {
                $webGroupId = session()->get('breadcrumb.groupId');
            }

            return
                $query
                    ->where('WebCollections.id', '=', $webCollectionId)
                    ->where('WebCategories.id', '=', $webCategoryId)
                    ->where('WebGroups.id', '=', $webGroupId);
        } else if ($model instanceof WebSilo) {
            return $query;
        }
    }

    public function scopeIn($query, $model)
    {
        if ($model instanceof WebGroup) return $query->inGroup($model);
        else if ($model instanceof WebCategory) return $query->inCategory($model);
        else if ($model instanceof WebCollection) return $query->inCollection($model);
        else if ($model instanceof WebSilo) $query->webSiloApproved();
    }

    public static function in($model)
    {
        if ($model instanceof WebGroup) {
            return self::inGroup($model);
        } else if ($model instanceof WebCategory) return self::inCategory($model);
        else if ($model instanceof WebCollection) return self::inCollection($model);
        else if ($model instanceof WebSilo) return self::inSilo($model);
        else return self::newQuery();
    }

    public function scopeVisible(Builder $query)
    {
        return $query
            ->where('WebFamilies.is_visible', '1')
            ->whereHas('webParts', function ($query) {
                return $query->visible();
            })
//            ->whereHas('webCollections', function ($query) {
//                return $query->visible();
//            })
            ;
    }

    public function scopeHasVisibleChildren(Builder $query, $activeWebSilo = false, $activeCountry = false, $activePlant = false)
    {
        return $query
            ->whereHas('webParts', function ($query) use ($activeWebSilo, $activeCountry, $activePlant) {
                return $query->isVisible()->hasVisibleChildren($activeWebSilo, $activeCountry, $activePlant);
            });
    }

    public function scopeHasVisibleParents(Builder $query, $activeWebSilo = false)
    {
        return $query
            ->whereHas('webCollections', function ($query) use ($activeWebSilo) {
                return $query->isVisible()->hasVisibleParents($activeWebSilo);
            });
    }

    public function scopeIsVisible(Builder $query)
    {
        return $query->where('WebFamilies.is_visible', '1');
    }

    public function scopeWebVisible(Builder $query)
    {
        return $query
            ->where('WebFamilies.is_visible', 1)
            ->where(function ($where) {
                return $where
                    ->whereDoesntHave('webSiloOverride')
                    ->orWhereHas('webSiloOverride', function ($query) {
                        return $query->where('WebSiloOverrides.is_visible', 1);
//                            ->where('WebSiloOverrides.websilo_id', '=', b2b()->activeWebSilo()->id)
//                            ->where('WebSiloOverrides.language_code', '=', b2b()->activeLanguage());
                    });
            })
            ->hasVisibleParents()
            ->hasVisibleChildren();
    }

    public function scopeWebSiloApproved(Builder $query)
    {
        return $query->whereHas('webParts', function ($webPartsQuery) {
            return $webPartsQuery->webSiloApproved();
        });
    }

    public function scopeForDisplay(Builder $query, $slug)
    {
        return $query
            ->webVisible()
//alternative eager-er loading option, but I'm gonna leave it out for now
//            ->with(['visibleWebParts' => function ($query) {
//                $query->with(['assets', 'part' => function ($query) {
//                    $query
//                        ->with('customerPrices', 'inventoryItems', 'webSilos')
//                        ->with('productFamily.productCategory.productCategoryGroup');
//                }]);
//            }])
            ->with(['visibleWebParts.assets','visibleWebParts.part.activeSiloParts', 'visibleWebParts.translations','visibleWebParts.webAttributes.translations',  'assets', 'webAttributes.translations'])
            ->with(['favoritedWebUsers' => function ($query) {
                $query->where('WebUsers.id', auth()->id());
            }])
            ->with(['visibleWebParts.webPartProductFeatures' => function ($query) {
                $query->where('language_code', b2b()->activeLanguage())
                    ->orderBy('display_order', 'ASC');
            }])
            ->with(['webFamilyProductFeatures' => function ($query) {
                $query->where('language_code', b2b()->activeLanguage())
                    ->orderBy('display_order', 'ASC');
            }])
            ->slug($slug);
    }

    public function scopeForBreadcrumbs($query)
    {
        $query->with([
            'webCollections' => function ($lQuery) {
                if (session('breadcrumb.collectionId')) {
                    $lQuery->where('WebCollections.id', '=', session('breadcrumb.collectionId'));
                }
                $lQuery->with(['webCategories' => function ($cQuery) {
                    if (session('breadcrumb.categoryId')) {
                        $cQuery->where('WebCategories.id', '=', session('breadcrumb.categoryId'));
                    }
                    $cQuery->with(['webGroups' => function ($gQuery) {
                        if (session('breadcrumb.groupId')) {
                            $gQuery->where('WebGroups.id', '=', session('breadcrumb.groupId'));
                        }
                    }]);
                }]);
            }
        ]);
        return $query;
    }

    public function scopeForLandingPage($query, HasLandingPageInterface $lpModel)
    {
        $query
            ->isVisible()
            ->hasVisibleParents()
            ->hasVisibleChildren()
            ->notForSearchOnly()
            ->in($lpModel)
            ->filterCorrectly($lpModel)
            ->join('WebFamily_WebCollection as WFWC', 'WFWC.webfamily_id', '=', 'WebFamilies.id')
            ->join('WebCollection_WebCategory as WLWC', 'WLWC.webcollection_id', '=', 'WFWC.webcollection_id')
            ->join('WebCategory_WebGroup as WCWG', 'WCWG.webcategory_id', '=', 'WLWC.webcategory_id')
            ->join('WebGroups', 'WebGroups.id', '=', 'WCWG.webgroup_id')
            ->join('WebCategories', 'WebCategories.id', '=', 'WCWG.webcategory_id')
            ->join('WebCollections', 'WebCollections.id', '=', 'WLWC.webcollection_id')
            ->where(function ($where) {
                return $where
                    ->whereNull('WebGroups.deleted_at')
                    ->orWhere('WebGroups.deleted_at', '=', DB::raw(0));
            })
            ->where(function ($where) {
                return $where
                    ->whereNull('WebCategories.deleted_at')
                    ->orWhere('WebCategories.deleted_at', '=', DB::raw(0));
            })
            ->where(function ($where) {
                return $where
                    ->whereNull('WebCollections.deleted_at')
                    ->orWhere('WebCollections.deleted_at', '=', DB::raw(0));
            });

        $query
            ->orderBy('WebFamilies.sort_priority', 'desc')
            ->orderBy('WebGroups.name', 'asc')
            ->orderBy('WCWG.display_order', 'asc')
            ->orderBy('WebCategories.name', 'asc')
            ->orderBy('WLWC.display_order', 'asc')
            ->orderBy('WebCollections.name', 'asc')
            ->orderBy('WFWC.display_order', 'asc')
            ->orderBy('WebFamilies.name', 'asc')
            ->select(DB::raw('DISTINCT WebFamilies.*'))
            ->with(['visibleWebParts', 'assets'])
            ->with(['favoritedWebUsers' => function ($query) {
                $query->where('WebUsers.id', auth()->id());
            }]);
        return $query;
    }

    public static function getImageUrl($partNumber, $width = null, $height = null)
    {
        $url = config('hilco.images.url');
        if (!isset($url)) $url = config('hilco-b2b.images.url');
        $url .= '/' . $partNumber;
        if ($width) $url .= "/$width";
        if ($height) $url .= "/$height";
        return $url;
    }

    public function scopeJoinWebPart(Builder $query)
    {
        return $query->leftJoin('WebParts', 'WebParts.webfamily_id', '=', 'WebFamilies.id');
    }

    public function slugUrl()
    {
        return route('family.slug', [$this->slug]);
    }

    public function scopeForSearchOnly(Builder $query)
    {
        return $query->where('search_only', 1);
    }

    public function scopeNotForSearchOnly(Builder $query)
    {
        return $query->where('search_only', 0);
    }

    public function scopeBestSellers(Builder $query)
    {
        return $query->where('best_seller', 1)->with('translations')->with('assets')->with('visibleWebParts.part.rewardsFamilyExclusion')->with('visibleWebParts.part.rewardsPartExclusion')->with('visibleWebParts.part.pharmacyPartExclusion')->with('visibleWebParts.part.pharmacyFamilyExclusion');
    }

    public function getPrimaryImage() {
        $asset = Arr::get($this, 'webSiloOverride.primaryImage', null);
        if (!is_null($asset)) return $asset;

        $asset = $this->assetsByType('primary')->first();
        if (!is_null($asset)) return $asset;

        $part = $this->visibleWebParts->where('pivot.is_family_image', 1)->first();
        if (!is_null($part)) {
            $asset = $part->getPrimaryImage();
            if (!is_null($asset)) return $asset;
        }

        $part = $this->visibleWebParts->first();
        if (!is_null($part)) {
            $asset = $part->getPrimaryImage();
            if (!is_null($asset)) return $asset;
        }

        return null;
    }

    public function getIsSiloPartAttribute()
    {
        foreach ($this->visibleWebParts as $webPart) {
            if ($webPart->isSiloPart) return true;
        }
        return false;
    }

    public function getRequireApprovalAttribute()
    {
        $requireApproval = true;
        if ($this->visibleWebParts()->count() == 0) {
            return false;
        }
        foreach ($this->visibleWebParts as $webPart) {
            if (!$webPart->require_approval) $requireApproval = false;
        }
        return $requireApproval;
    }

    public function getMetadataDescriptionAttribute() {
        $metadataDescription = $this->getMetadata('description', b2b()->activeLanguage());
        if (!empty($metadataDescription)) {
            return $metadataDescription;
        } else if (!empty($this->product_tagline)) {
            return $this->product_tagline;
        } else {
            return trim(preg_replace('/(\s*<.*?>\s*)+/', ' ', $this->detail));
        }
    }

    public function getMetadataTitleAttribute() {
        $value = $this->getMetadata('title', b2b()->activeLanguage());
        if (strlen($value)) return $value;

        return $this->name;
    }

    use HasAssets, HasSlug, HasLandingPage, HasWebSiloOverrides, HasCommitSequence, HasModelTranslations, HasMetadata;

    public function getParentRelationName()
    {
        return 'webCollections';
    }

    public function webFamilyLabel() {
        return $this->hasOne(WebFamilyLabel::class, 'label_name', 'label');
    }

//
    public function getLabelStyleAttribute()
    {
        $webFamilyLabel = $this->webFamilyLabel;
        if ($webFamilyLabel != null) {
            return $webFamilyLabel->bootstrap_class;
        }
        return null;

    }
//
    public function getLabelNameAttribute()
    {
        $webFamilyLabel = $this->webFamilyLabel;

        if ($webFamilyLabel != null) {
            return $webFamilyLabel->label_name;
        }
        return null;
    }

    public function getIsWebVisibleAttribute()
    {
        return static::webVisible()->where('id', $this->id)->count() > 0;
    }

    public function keywordsAsText() {
        $delim = '';
        $result = '';
        foreach($this->webFamilyKeywords as $webFamilyKeyword) {
            $result .= $delim.$webFamilyKeyword->keyword;
            $delim = ',';
        }
        return $result;
    }

    public function getProductTaglineAttribute() {
        if (config('hilco.ignoreActiveWebSilo')) {
            return $this->getTranslation('product_tagline', AvailableLanguage::DEFAULT_LANG_CODE, $this->attributes['product_tagline']);
        } else {
            return $this->getTranslation('product_tagline');
        }
    }

    public function webCollectionsInHierarchy ($webHierarchyId) {
        return $this->webCollections()->inHierarchy($webHierarchyId);
    }

    /**
     * @param array $webFamilyIds
     * @return array
     */
    public static function getWebHierarchyIdsForFamilies(array $webFamilyIds): array {
        $webHierarchiesRaw = DB::select(
            "SELECT WebHierarchies.* 
                FROM WebFamilies
                JOIN WebFamily_WebCollection ON WebFamilies.id = WebFamily_WebCollection.webfamily_id AND WebFamily_WebCollection.deleted_at = 0
                JOIN WebCollections ON WebCollections.id = WebFamily_WebCollection.webcollection_id AND WebCollections.deleted_at = 0
                JOIN WebCollection_WebCategory ON WebCollection_WebCategory.webcollection_id = WebCollections.id AND WebCollection_WebCategory.deleted_at = 0
                JOIN WebCategories ON WebCategories.id = WebCollection_WebCategory.webcategory_id AND WebCategories.deleted_at = 0
                JOIN WebCategory_WebGroup ON WebCategory_WebGroup.webcategory_id = WebCategories.id AND WebCategory_WebGroup.deleted_at = 0
                JOIN WebGroups ON WebGroups.id = WebCategory_WebGroup.webgroup_id AND WebGroups.deleted_at = 0
                JOIN WebGroup_WebHierarchy ON WebGroup_WebHierarchy.webgroup_id = WebGroups.id AND WebGroup_WebHierarchy.deleted_at = 0
                JOIN WebHierarchies ON WebHierarchies.id = WebGroup_WebHierarchy.webhierarchy_id AND WebHierarchies.deleted_at = 0
                WHERE WebFamilies.id IN (?)",
            [implode(',', $webFamilyIds)]
        );

        $webHierarchyIds = WebHierarchy::hydrate($webHierarchiesRaw)->pluck('id')->toArray();
        return array_unique($webHierarchyIds);
    }

    public function getBrand($brandAttributeId){
        if (is_null($this->webAttributes->find($brandAttributeId))) {
            return '';
        }
        $brandValue = "";
        $hasDifferentBrands = false;
        foreach ($this->visibleWebParts as $visibleWebPart) {
            $brandWAWP = $visibleWebPart->webAttributeWebParts->filter(function ($wawp) use ($brandAttributeId) {
                return $wawp->webattribute_id == $brandAttributeId;
            })->first();
            if (isset($brandWAWP)) {
                $brandWAWPValue = $brandWAWP->getAttributeValueAttribute();
                if (empty($brandWAWPValue)) {
                    continue;
                }
                if (empty($brandValue)) {
                    $brandValue = $brandWAWPValue;
                } else if ($brandValue != $brandWAWPValue) {
                    $hasDifferentBrands = true;
                    break;
                }
            }
        }

        if ($hasDifferentBrands) {
            return trans("app.various_brands");
        } else {
            return $brandValue;
        }
    }

    /**
     * This retrieves the name of an arbitrary webgroup in the provided microsite that the provided webfamily is in the
     * hierarchical chain for, or empty string is none is found, all so that Google Analytics can receive some BS value
     * for the 'brand' field in the 'dataLayer'.
     * @param $webFamilyId
     * @param $webSiloId
     * @return string
     */
    public static function getBrandNameForGoogleAnalytics ($webFamilyId, $webSiloId): string {
        $webGroup = WebGroup::whereExists(function ($exists) use ($webSiloId) {
            return $exists->select(DB::raw(1))->from("WebGroup_WebHierarchy")
                ->whereColumn(
                    "WebGroup_WebHierarchy.webgroup_id", "=", "WebGroups.id"
                )->whereRaw(
                    "WebGroup_WebHierarchy.webhierarchy_id = (SELECT webhierarchy_id FROM WebSilos WHERE id= ?)",
                    [$webSiloId]
                )->where("WebGroup_WebHierarchy.deleted_at", "=", "0000-00-00 00:00:00");
        })->whereExists(function ($exists) use ($webFamilyId) {
            return $exists->select(DB::raw(1))->from("WebCategory_WebGroup")->whereColumn(
                "WebCategory_WebGroup.webgroup_id", "=", "WebGroups.id"
            )->where(
                "WebCategory_WebGroup.deleted_at", "=", "0000-00-00 00:00:00"
            )->whereExists(function ($exists2) use ($webFamilyId) {
                return $exists2->select(DB::raw(1))->from("WebCategories")->whereColumn(
                    "WebCategory_WebGroup.webcategory_id", "=", "WebCategories.id"
                )->where(
                    "WebCategories.deleted_at", "=", "0000-00-00 00:00:00"
                )->whereExists(function ($exists3) use ($webFamilyId) {
                    return $exists3->select(DB::raw(1))->from("WebCollection_WebCategory")->whereColumn(
                        "WebCollection_WebCategory.webcategory_id", "=", "WebCategories.id"
                    )->where(
                        "WebCollection_WebCategory.deleted_at", "=", "0000-00-00 00:00:00"
                    )->whereExists(function ($exists4) use ($webFamilyId) {
                        return $exists4->select(DB::raw(1))->from("WebCollections")->whereColumn(
                            "WebCollection_WebCategory.webcollection_id", "=", "WebCollections.id"
                        )->where(
                            "WebCollections.deleted_at", "=", "0000-00-00 00:00:00"
                        )->whereExists(function ($exists5) use ($webFamilyId) {
                            return $exists5->select(DB::raw(1))->from("WebFamily_WebCollection")->whereColumn(
                                "WebFamily_WebCollection.webcollection_id", "=", "WebCollections.id"
                            )->where(
                                "WebFamily_WebCollection.deleted_at", "=", "0000-00-00 00:00:00"
                            )->where("WebFamily_WebCollection.webfamily_id", "=", $webFamilyId);
                        });
                    });
                });
            });
        })->first();
        return isset($webGroup) ? $webGroup->name : "";
    }

    public function getHubSpotEventProperties() {
        return [
            "product_id"    => $this->id,
            "product_name"  => $this->name,
            'product_url'   => route('family.slug', [$this->slug]),
        ];
    }
}