<?php

namespace Hilco\Models;
use Carbon\Carbon;
use Eloquent;
use HubSpot\Client\Crm\Contacts\ApiException as HubSpotContactsApiException;
use HubSpot\Client\Crm\Contacts\Model\Filter;
use HubSpot\Client\Crm\Contacts\Model\FilterGroup;
use HubSpot\Client\Crm\Contacts\Model\PublicObjectSearchRequest;
use HubSpot\Factory;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Klaviyo\ApiException as KlaviyoApiException;
use KlaviyoAPI\KlaviyoAPI;
use Log;


/**
 * Hilco\Models\Customer
 *
 * @property integer $id
 * @property string $cust_no
 * @property string $cust_name
 * @property string $aff_cust_no
 * @property integer $aff_customer_id
 * @property string $aff_cust_name
 * @property string $addr_1
 * @property string $addr_2
 * @property string $addr_3
 * @property string $addr_4
 * @property string $addr_5
 * @property string $city
 * @property string $state
 * @property string $zip_postal
 * @property string $country
 * @property string $bill_addr_1
 * @property string $bill_addr_2
 * @property string $bill_addr_3
 * @property string $bill_addr_4
 * @property string $bill_addr_5
 * @property string $bill_city
 * @property string $bill_state
 * @property string $bill_zip_postal
 * @property string $bill_country
 * @property string $category
 * @property integer $customercategory_id
 * @property string $incept_dt
 * @property string $currency
 * @property integer $websilo_id
 * @property boolean $is_de_domestic
 * @property string $service_charge_code
 * @property string $date_created
 * @property string $date_modified
 * @property string $date_uploaded
 * @property string $deleted_at
 * @property-read CustomerSegment $customerSegment
 * @property-read Collection|Customer[] $affiliatedCustomers
 * @property-read LegacyRewardsCustomer $legacyRewardsCustomer
 * @property-read Collection|Division[] $divisions
 * @property-read Collection|CustomerPhone[] $phones
 * @property-read Collection|CustomerEmail[] $emails
 * @property-read CustomerCategory $customer_category
 * @property-read Collection|Contact[] $contacts
 * @property-read Collection|CustomerDiscount[] $discounts
 * @property-read Collection|Order[] $orders
 * @property-read Collection|CustomerPharmacyInfo[] $pharmacyInfos
 * @property-read Collection|Customer_PharmacyLicense[] $customerPharmacyLicenses
 * @property-read Collection|CustomerCategoryGroupSpending6M[] $customerCategoryGroupSpending6M
 * @property-read Collection|CustomerCategoryGroupSpending12M[] $customerCategoryGroupSpending12M
 * @property-read Collection|CustomerCategoryGroupSpending18M[] $customerCategoryGroupSpending18M
 * @property-read BusinessChain_Customer $businessChainCustomer
 * @property-read BusinessChain $businessChain
 * @property-read BusinessChain $validBusinessChain
 * @property-read WebSilo $webSilo
 * @property-read mixed $id_string
 * @method static Builder|Customer whereId($value)
 * @method static Builder|Customer whereCustNo($value)
 * @method static Builder|Customer whereCustName($value)
 * @method static Builder|Customer whereAffCustNo($value)
 * @method static Builder|Customer whereAffCustomerId($value)
 * @method static Builder|Customer whereAffCustName($value)
 * @method static Builder|Customer whereAddr1($value)
 * @method static Builder|Customer whereAddr2($value)
 * @method static Builder|Customer whereAddr3($value)
 * @method static Builder|Customer whereAddr4($value)
 * @method static Builder|Customer whereAddr5($value)
 * @method static Builder|Customer whereCity($value)
 * @method static Builder|Customer whereState($value)
 * @method static Builder|Customer whereZipPostal($value)
 * @method static Builder|Customer whereCountry($value)
 * @method static Builder|Customer whereCategory($value)
 * @method static Builder|Customer whereCustomercategoryId($value)
 * @method static Builder|Customer whereInceptDt($value)
 * @method static Builder|Customer whereCurrency($value)
 * @method static Builder|Customer whereDateCreated($value)
 * @method static Builder|Customer whereDateModified($value)
 * @method static Builder|Customer whereDateUploaded($value)
 * @method static Builder|Customer whereDeletedAt($value)
 * @mixin Eloquent
 */
class Customer extends Model {
	protected $table = 'Customers';
    protected $connection = "hilco-live";
	protected $fillable = ['name', 'country', 'state', 'city', 'zip_postal', 'addr_1', 'addr_2', 'addr_3','currency','cust_name'];

	public static $currencies = [
		'USD'=>'USD',
		'NZD'=>'NZD',
		'JPY'=>'JPY',
		'GBP'=>'GBP',
		'CNY'=>'CNY',
		'CAD'=>'CAD',
		'AUD'=>'AUD'
	];
	const UPDATED_AT = 'date_modified';
	
	const HIDDEN = 'HIDDEN';
	const GERMAN_DOMESTIC_WEB_SERVICE_CHARGE_CODE = '33';
    const GERMAN_DOMESTIC_STANDARD_SERVICE_CHARGE_CODE = '01';

    use \Hilco\SoftDeletes;

    public static $perEnvironment = true;
    public static $objectIdKey = 'cust_no';

    public function getSolrRecord() {
        $record = [
            'id' => $this->id,
            'cust_name' => $this->cust_name,
            'cust_no' => $this->cust_no,
            'legacy_cust_no' => $this->legacy_cust_no
        ];
        return $record;
    }
    
    public function solrTable()
    {
        return $this->hasMany(Customer_Solr::class, 'customer_id', 'id');
    }

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

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

	public function webUser()
	{
		return $this->hasOne(WebUser::class, 'customer_id');
	}

	public function webUsers() {
        return $this->hasMany(WebUser::class, 'customer_id');
    }

	public function shippingAddresses() {
		return $this->hasMany(CustomerShippingAddress::class, 'cust_no', 'cust_no');
	}

	public function availableShippingAddresses() {
        return $this->hasMany(CustomerShippingAddress::class, 'cust_no', 'cust_no')->hasShipToProfile();
    }

	public function contacts() {
	    return $this->hasMany(Contact::class, 'customer_id', 'id');
    }

    public function customerEmails() {
        return $this->hasMany(CustomerEmail::class, 'customer_id', 'id');
    }

    public function jobsonDatas() {
        return $this->belongsToMany(JobsonData::class, 'Customer_JobsonData', 'customer_id', 'jobsondata_id');
    }

    public function getLatestCall() {
        return Call::where('customer_id', $this->id)->orderBy('start_time', 'desc')->first();
    }

	public static $priority = [
		'A'	=> [
			'from' => 2000,
			'to'   => 9999999
		],
		'B'	=> [
			'from' => 1000,
			'to'   => 1999
		],
		'C'	=> [
			'from' => 400,
			'to'   => 999
		],
		'D'	=> [
			'from' => 0,
			'to'   => 399
		]
	];

    public function getActiveSegmentAttribute() {
        return $this->segments->where('active', 1)->first();
    }

    public function getDefaultShippingAddressAttribute() {
        $segment = $this->active_segment;
        if (is_null($segment)) {
            return null;
        } else {
            if (is_null($segment->address) || is_null($segment->address()->isActive()->first())) {
                return $segment->shipping_addresses()->isActive()->first();
            } else {
                return $segment->address;
            }
        }
    }

	public function activeSegments()
	{
		return $this->segments()->where('CustomerSegments.active', '=', 1);
	}

	public function pharmacyInfos(): HasManyThrough {
		return $this->hasManyThrough(CustomerPharmacyInfo::class, Customer_PharmacyLicense::class, 'customer_id', 'id', 'id', 'customerpharmacyinfo_id')->where("Customer_PharmacyLicense.deleted_at", "=", DB::raw("0"));
	}

    public function customerPharmacyLicenses(): HasMany {
        return $this->hasMany(Customer_PharmacyLicense::class, 'customer_id', 'id');
    }

	public function segments() {
		return $this->hasMany(CustomerSegment::class, 'customer_id', 'id');
	}

	public function activeDivisions()
	{
		return $this
            ->belongsToMany(Division::class, 'CustomerSegments', 'customer_id', 'division_id')
            ->where('CustomerSegments.active', '=', 1)
            ->where('CustomerSegments.deleted_at', '=', DB::raw(0));
	}

	public function divisions()
	{
		return $this->belongsToMany(Division::class, 'CustomerSegments', 'customer_id', 'division_id');
	}

	public function phones()
	{
		return $this->hasMany(CustomerPhone::class);
	}

	public function emails()
	{
		return $this->hasMany(CustomerEmail::class);
	}

//	public function customer_category() {
//	    return $this->customerCategory;
//    }

	public function customerCategory()
	{
		return $this->belongsTo(CustomerCategory::class, 'customercategory_id');
	}


	public function discounts()
	{
		return $this->hasMany(CustomerDiscount::class);
	}

	public function customerStateTax(){
		return $this->belongsTo(StateTax::class,'state','state')
			->where(function($query){
				$query->where(function($query){
					return $query->where('effective_date','<=', date('Y-m-d',time()))
						->where('expiration_date','>=', date('Y-m-d',time()));
				})
					->orWhere('expiration_date', '000-00-00');
			});
	}

	public function customerVATTax(){

	}

	public function orders()
	{
		return $this->hasMany(Order::class, 'soldto_customer_id');
	}

	public function salesStats()
	{
		return $this->hasMany(CustomerSalesStats::class, 'customer_id');
	}

	public function stateTax() {
		return StateTax::where("state", $this->state)->inEffect()->first();
	}

	public function scopeWithEmails($query) {
		return $query->addSelect('email_raw', 'good_email', 'email_note', 'email_news', 'email_sales', 'email_ship')
			->join('CustomerEmails', 'Customers.id', '=', 'CustomerEmails.customer_id')
			;
	}

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
	public function webCustomerSalesSummaries() {
	    return $this->hasMany(WebCustomerSalesSummary::class, 'customer_id', 'id');
    }

    public function webCustomerLimits() {
        return $this->hasMany(WebCustomerLimit::class, 'customer_id', 'id');
    }

    public function recentOrders($count = 5) {
        return $this
            ->orders()
            ->submitted()
            ->with('orderLineSchedules.shipment')
            ->orderBy('order_date', 'desc')
            ->take($count)
            ->get();
    }

    public function topPurchasedItems($count = 5) {
        $ids = WebFamily::webVisible()
            ->join('WebPart_WebFamily', 'WebPart_WebFamily.webfamily_id', '=', 'WebFamilies.id')
            ->join('WebParts', 'WebParts.id', '=', 'WebPart_WebFamily.webpart_id')
            ->join('SalesOrderLines', function($join) {
                $join
                    ->on('SalesOrderLines.part_id', '=', 'WebParts.part_id');
            })
            ->join('SalesOrders', function($join) {
                $join
                    ->on('SalesOrders.id', '=', 'SalesOrderLines.salesorder_id');
            })
            ->leftJoin('SalesOrderLineSchedules', function($join) {
                $join
                    ->on('SalesOrderLineSchedules.salesorder_id', '=', 'SalesOrders.id')
                    ->on('SalesOrderLineSchedules.salesorderline_id', '=', 'SalesOrderLines.id');
            })
            ->where('SalesOrders.deleted_at', '=', DB::raw('0'))
            ->where('SalesOrderLines.deleted_at', '=', DB::raw('0'))
            ->where(function ($where) {
                $where
                    ->whereNull('SalesOrderLineSchedules.deleted_at')
                    ->orWhere('SalesOrderLineSchedules.deleted_at', '=', DB::raw('0'))
                ;
            })
            ->where('SalesOrders.soldto_customer_id', '=', $this->id)
            ->whereNotIn('SalesOrders.order_status', ['CANCELLED', 'DISCARDED'])
            ->groupBy('WebFamilies.id')
            ->orderBy(DB::raw('COUNT(DISTINCT SalesOrders.id)'), 'DESC')
            ->take($count)
            ->pluck('WebFamilies.id')
        ;

        return WebFamily::with(['assets', 'translations'])->with('visibleWebParts.part.rewardsFamilyExclusion')->with('visibleWebParts.part.rewardsPartExclusion')->with('visibleWebParts.part.pharmacyPartExclusion')->with('visibleWebParts.part.pharmacyFamilyExclusion')->findMany($ids);
    }

    public function affiliateCustomer() {
	    return $this->hasOne(Customer::class, 'id', 'aff_customer_id');
    }

    public function affiliateChildren() {
        return $this->hasMany(Customer::class, 'aff_customer_id', 'id');
    }

    public function affiliatedCustomers() {
	    return $this->hasMany(Customer::class, 'aff_customer_id', 'aff_customer_id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function webSilo() {
        return $this->belongsTo(WebSilo::class, 'websilo_id', 'id');
    }

    public function customerWebSilo() {
        return $this->hasOne(CustomerWebSilo::class, 'customer_id', 'id');
    }

    public function getHasAffiliateAttribute() {
	    return !is_null($this->aff_customer_id);
    }

    public function croakiesAdventuresStatus() {
        return $this->hasMany(CroakiesAdventuresStatus::class, 'customer_id', 'aff_customer_id');
    }

    public function croakiesAdventuresTier() {
        return $this->hasOne(CroakiesAdventuresTiers::class, 'customer_id', 'id')
                    ->orderBy('first_qualified_date', 'desc')
                    ->orderBy('tier', 'desc')
                    ->take(1);
    }

    public function getCroakiesOrdered() {
        $statuses = $this->croakiesAdventuresStatus;
        $croakiesOrdered = 0;

        $lastDateModified = Carbon::now()->toDateString();
        foreach($statuses as $status) {
            $croakiesOrdered = $croakiesOrdered + $status->quantities;
            $lastDateModified = $status->date_modified;
        }

        $affCustomers = $this->affiliatedCustomers;
        $affCustomerIds = [];
        $index = 0;
        foreach ($affCustomers as $affCustomer) {
            $affCustomerIds[$index] = $affCustomer->id;
            $index = $index + 1;
        }

        $croakiesOrders = Order::whereIn('billto_customer_id', $affCustomerIds)
                                ->where('date_created', '>=', $lastDateModified)
                                ->whereIn('order_status', ['PENDING', 'ADDRESS_PENDING', 'PENDING_APPROVAL', 'CLOSED', 'EXPORTING', 'SENT_TO_POINTMAN'])
                                ->where('deleted_at', '=', '0000-00-00 00:00:00')
                                ->get();

        foreach ($croakiesOrders as $order) {
            $partLines = $order->partLines;
            foreach ($partLines as $partLine) {
                $quantity = $partLine->quantity;
                $partValue = 1;

                $prepackPart = CroakiesAdventuresPrepackPart::where('part_no', '=', $partLine->part->part_no)->first();
                if ($prepackPart) {
                    $partValue = $prepackPart->no_units;
                }

                $croakiesOrdered = $croakiesOrdered + ($quantity * $partValue);
            }
        }

        return $croakiesOrdered;
    }

    public function rewardsStatus() {
	    return $this->hasMany(CustomerRewardsStatus::class, 'customer_id', 'aff_customer_id');
    }

    public function rewardsTier() {
	    return $this->hasOne(CustomerRewardsTier::class, 'customer_id', 'aff_customer_id')->orderBy('first_qualified_date', 'DESC')->orderBy('tier', 'DESC')->take(1);
    }

    public function rewardsRegistration() {
	    return $this->hasOne(CustomerRewardsRegistration::class, 'customer_id', 'aff_customer_id');
    }

    public function customerCategoryGroupSpending6M(): HasMany {
        return $this->hasMany(CustomerCategoryGroupSpending6M::class, 'customer_id', 'id');
    }

    public function customerCategoryGroupSpending12M(): HasMany {
        return $this->hasMany(CustomerCategoryGroupSpending12M::class, 'customer_id', 'id');
    }

    public function customerCategoryGroupSpending18M(): HasMany {
        return $this->hasMany(CustomerCategoryGroupSpending18M::class, 'customer_id', 'id');
    }

    public function getBillToCustomerAttribute() {
	    $activeSegment = $this->activeSegment;
	    if (is_null($activeSegment)) return null;
	    return $activeSegment->billToCustomer;
    }

    public function getBillsDirectAttribute() {
	    $activeSegment = $this->activeSegment;

	    return is_null($activeSegment) ? false : $this->activeSegment->billsDirect;
    }

    public function getRewardsTierLevelAttribute() {
        $tier = $this->rewardsTier;
        if (!is_null($tier)) {
            return $tier->tier;
        }
        return 0;
    }

    public function customer4P() {
        return $this->hasOne(Customer4P::class, 'customer_id', 'id');

    }

    public function customerOverride() {
	    return $this->hasOne(CustomerOverride::class, 'customer_id', 'aff_customer_id');
    }

    public function getOverrideCustomerCategoryAttribute() {
        $relation = null;
	    $override = $this->customerOverride;
	    if (is_null($override)) {
            $relation = $this->customerCategory;
        } else {
            $relation = $override->customerCategory;
        }
        if(is_null($relation)){
	        return $this->customerCategory;
        }
        return $relation;
    }

    public function getProfileAttribute() {
        $profile = null;
        $override = $this->customerOverride;
        if (!is_null($override)) {
            $profile = $this->customerOverride->profile;
        }
        return $profile;
    }

    public function getDefaultPriceListAttribute() {
	    $activeSegment = $this->activeSegment;
	    if ($activeSegment) {
	        return $activeSegment->def_price_list;
        }

        return null;
    }

    /**
     * @return Customer|object|null
     */
    public function getDummyCustomer() {
        // Check if there is a specific customer for this currency and facility
        $dummyCustomer = DummyCustomer::where('currency', $this->currency)->where('facility', $this->activeSegment->facility_id)->first();

        // If null, Check if there is a default for the currency
        if ($dummyCustomer == null) {

            $dummyCustomer = DummyCustomer::where('currency', $this->currency)->whereNull('facility')->first();
        }

        // If still null, Default USD
        if ($dummyCustomer == null) {
            $dummyCustomer = DummyCustomer::where('currency', 'USD')->whereNull('facility')->first();
        }
        
        return Customer::where('cust_no', $dummyCustomer->cust_no)->first();
    }

    public function getDivisionByFacility() {
        $customerSegment = $this->customerSegment;
        if (isset($customerSegment)) {
            $facility = $customerSegment->facilityDivision;
            return $facility->division()->first();
        }
        return null;
    }

    /**
     * @return string
     */
    public function getServiceChargeCodeForWeb(): string {
        if ($this->service_charge_code == self::GERMAN_DOMESTIC_STANDARD_SERVICE_CHARGE_CODE) {
            return Customer::GERMAN_DOMESTIC_WEB_SERVICE_CHARGE_CODE;
        } else {
            return $this->service_charge_code;
        }
    }

    /**
     * @return HasOne
     */
    public function customerSegment(): HasOne {
        return $this->hasOne(CustomerSegment::class, "customer_id", "id");
    }

    public function currentCustomerLifecycle() {
        return $this->hasOne(CustomerLifecycle::class)->whereRaw('NOT EXISTS (SELECT 1 FROM CustomerLifecycles b WHERE b.customer_id = CustomerLifecycles.customer_id AND b.transfer_date > CustomerLifecycles.transfer_date)');
    }

    public function getCurrency() {
        return $this->currency;
    }

    public function getCustNo() {
        return $this->cust_no;
    }

    public function getSpendingArray(): \Illuminate\Support\Collection {
        $spending6month = $this->customerCategoryGroupSpending6M;
        $spending12month = $this->customerCategoryGroupSpending12M;
        $spending18month = $this->customerCategoryGroupSpending18M;
        $groups = ProductCategoryGroup::where('product_category_group', '!=', ProductCategoryGroup::TOTAL_ADJUST)
            ->orderByRaw('CAST(product_category_group AS UNSIGNED) ASC')
            ->get();

        $data = collect();

        foreach ($groups as $group) {
            $spending6M = $spending6month->where('productcategorygroup_id', $group->id)->first();
            if (is_null($spending6M)) {
                $spending6M = new CustomerCategoryGroupSpending6M();
                $spending6M->productcategorygroup_id = $group->id;
                $spending6M->spending = 0;
            }

            $spending12M = $spending12month->where('productcategorygroup_id', $group->id)->first();
            if (is_null($spending12M)) {
                $spending12M = new CustomerCategoryGroupSpending12M();
                $spending12M->productcategorygroup_id = $group->id;
                $spending12M->spending = 0;
            }

            $spending18M = $spending18month->where('productcategorygroup_id', $group->id)->first();
            if (is_null($spending18M)) {
                $spending18M = new CustomerCategoryGroupSpending18M();
                $spending18M->productcategorygroup_id = $group->id;
                $spending18M->spending = 0;
            }

            $data->put($group->getDescription(), ['6month' => $spending6M, '12month' => $spending12M, '18month' => $spending18M]);
        }

        return $data;
    }

    public function legacyRewardsCustomer(): HasOne {
        return $this->hasOne(LegacyRewardsCustomer::class, "customer_id", "id");
    }

    public function totalSpending6m() {
        return $this->customerCategoryGroupSpending6M->sum('spending');
    }

    public function totalSpending12m() {
        return $this->customerCategoryGroupSpending12M->sum('spending');
    }

    public function totalSpending18m() {
        return $this->customerCategoryGroupSpending18M->sum('spending');
    }

    /**
     * NOTE: this deliberately skips evaluating the Klaviyo List and Current Order Triggers on the Rewards promotion
     * @param $rewardsPromotion
     * @param $webUser
     * @param $pricelistConnection
     * @return bool
     */
    public function isRewardsQualifying ($rewardsPromotion, $webUser, $pricelistConnection = null): bool {
        $triggered = false;
        if (!($rewardsPromotion instanceof Promotion)) {
            $rewardsPromotion = Promotion::find(config('hilco.rewardsPromotionId'));
        }
        if (!($webUser instanceof WebUser)) {
            $webUser = auth()->user();
        }
        if (isset($rewardsPromotion) && isset($webUser)) {
//            $webCartItems = WebCart::getWebCartItems($webUser, false);
//            $distinctPartInfo = WebCart::getDistinctPartInfo($pricelistConnection, $webCartItems, $this, $webUser);
            foreach ($rewardsPromotion->triggers as $promoTrigger) {
                if ($promoTrigger->trigger_type !== "klaviyoListTrigger" && $promoTrigger->trigger_type !== 'currentOrderTrigger') {
                    $triggered = $promoTrigger->details->isTriggered($this, null, null);
                    if (!$triggered) break;
                }
            }
        }

        return $triggered;
    }

    /**
     * @param $klaviyoListId
     * @return bool
     */
    public function isRewardsEnrolled ($klaviyoListId): bool {
        if (isset($this->legacyRewardsCustomer)) {
            return true;
        }
//        if (empty($hubspotSubId)) {
//            $rewardsPromotion = Promotion::find(config('hilco.rewardsPromotionId'));
//            if (isset($rewardsPromotion)) {
//                foreach ($rewardsPromotion->triggers as $trigger) {
//                    if ($trigger->trigger_type == 'hubspotSubTrigger') {
//                        $hubspotSubId = $trigger->details->subscriptionid;
//                        break;
//                    }
//                }
//                if (empty($hubspotSubId)) {
//                    return false;
//                }
//            }
//        }
        return $this->hasHubSpotMarketingContacts();
    }

    /**
     * @param $hubspotSubId
     * @return bool
     */
    public function isSubscribedToHubspotSub ($hubspotSubId) {
        if (empty($hubspotSubId)) return false;

        foreach ($this->webUsers as $webUser) {
            if ($webUser->isSubscribedToHubspotSub($hubspotSubId)) {
                return true;
            }
        }

        return false;
    }

    public function hasHubSpotMarketingContacts() {
        $hubspot = Factory::createWithAccessToken(config('hubspot.access_token'));
        $filter = new Filter();
        $filter->setOperator('EQ')->setPropertyName('marketing_contact')->setValue("Yes");
        $filterGroup = new FilterGroup();
        $filterGroup->setFilters([$filter]);
        $posr = new PublicObjectSearchRequest();
        $posr->setFilterGroups([$filterGroup]);
        $posr->setLimit(1);
        try {
            $crwtspofp = $hubspot->crm()->contacts()->searchApi()->doSearch($posr);
            return $crwtspofp->getTotal() > 0;
        } catch (HubSpotContactsApiException $ae) {
            Log::error("Error searching for HS marketing contacts for customer $this->id: {msg} - {trace}", [
                'msg' => $ae->getMessage(), 'trace' => $ae->getTraceAsString()
            ]);
        }
        return false;
    }

    public function isSubscribedToKlaviyoList ($klaviyo_list): bool {
        if (empty($klaviyo_list)) return false;

        $cachedListSub = KlaviyoListSubscriptionResult::byKlaviyoListAndCustomer($klaviyo_list, $this->id)->first();
        if (isset($cachedListSub) && !$cachedListSub->isExpired()) {
            return $cachedListSub->isSubscribed();
        }

        $webSilo = $this->webSilo;
        $klaviyoClient = new KlaviyoAPI($webSilo->klaviyo_api_key);

        // filter_var() with FILTER_VALIDATE_EMAIL will at least strip out any webuser emails that get returned that
        // aren't actually valid formatted email addresses.
        // array_values() is used because the toArray() on the result of filter() creates an array with explicit keys,
        // e.g., [ 0 => "email", 1 => "email2", ...],
        // rather than what we want, which is implicit keys,
        // e.g., ["email", "email2", ...]
        $userEmailsArray = array_values($this->webUsers()->pluck('email')->filter(function ($email) {
            return filter_var($email, FILTER_VALIDATE_EMAIL);
        })->toArray());
        $membershipsArray = [];
        try {
            foreach (array_chunk($userEmailsArray, 20) as $userEmailsArrayChunk) { // max # objs in filter is 20
                $filterStr = 'any(email,["'.implode('","', $userEmailsArrayChunk).'"])';
                $getListProfilesResponse = $klaviyoClient->Lists->getListProfiles($klaviyo_list, [], ["email"], $filterStr);
                if (!empty($getListProfilesResponse) && isset($getListProfilesResponse['data'])) {
                    $membershipsArray = $getListProfilesResponse['data'];
                }
                if (count($membershipsArray)) {
                    break;
                }
            }
        } catch (ApiException $klaviyoException) { // ApiException is thrown on a non-2xx response
            Log::warning($klaviyoException->getMessage(), $klaviyoException->getTrace());
        }

        $isSubscribed = count($membershipsArray) > 0;

        $webUser = null;
        if (auth()->check()) {
            $webUser = WebUser::find(auth()->user()->id);
        }
        KlaviyoListSubscriptionResult::createAndSave($klaviyo_list, $this, $isSubscribed, json_encode($membershipsArray), $webUser);

        return $isSubscribed;
    }

    /**
     * @return HasOne
     */
    public function businessChainCustomer(): HasOne {
        return $this->hasOne(BusinessChain_Customer::class, "customer_id", "id");
    }

    /**
     * @return HasOneThrough
     */
    public function businessChain(): HasOneThrough {
        return $this->hasOneThrough(BusinessChain::class, BusinessChain_Customer::class, "customer_id", "id", "id", "businesschain_id")
            ->where("BusinessChain_Customer.deleted_at", "=", DB::raw("0"));
    }

    /**
     * Same as businessChain() relation except the valid_x_date columns are checked in BusinessChain_Customer
     * @return HasOneThrough
     */
    public function validBusinessChain(): HasOneThrough {
        return $this->hasOneThrough(BusinessChain::class, BusinessChain_Customer::class, "customer_id", "id", "id", "businesschain_id")
            ->where(function ($query) {
                return $query->where(function ($where1) {
                    return $where1->whereNull("BusinessChain_Customer.valid_from_date")
                        ->orWhere("BusinessChain_Customer.valid_from_date", "=", DB::raw("0"))
                        ->orWhere("BusinessChain_Customer.valid_from_date", "<", DB::raw("DATE(NOW())"));
                })->where(function ($where2) {
                    return $where2->whereNull("BusinessChain_Customer.valid_to_date")
                        ->orWhere("BusinessChain_Customer.valid_to_date", "=", DB::raw("0"))
                        ->orWhere("BusinessChain_Customer.valid_to_date", ">", DB::raw("DATE(NOW())"));
                });
            })->where("BusinessChain_Customer.deleted_at", "=", DB::raw("0"));
    }
}