<?php

namespace Hilco\Models;

use Arr;
use Hilco\GuzzleWrappers\RequestBuilders\TaxRequestBuilder;
use HilcoB2B\Notifications\HilcoResetPasswordNotification;
use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Notifications\Notifiable;
use Klaviyo\Klaviyo as Klaviyo;
use Klaviyo\Model\ProfileModel as KlaviyoProfile;
use Klaviyo\Model\EventModel as KlaviyoEvent;
use Log;
use URL;

/**
 * Hilco\Models\WebUser
 *
 * @property integer $id
 * @property string $email
 * @property string $password
 * @property string $name
 * @property integer $customer_id
 * @property string $remember_token
 * @property string $authy_status
 * @property string $authy_id
 * @property string $date_created
 * @property string $date_modified
 * @property string $date_uploaded
 * @property string $deleted_at
 * @property string $last_login
 * @method static \Illuminate\Database\Query\Builder|\Hilco\Models\WebUser whereId($value)
 * @method static \Illuminate\Database\Query\Builder|\Hilco\Models\WebUser whereEmail($value)
 * @method static \Illuminate\Database\Query\Builder|\Hilco\Models\WebUser wherePassword($value)
 * @method static \Illuminate\Database\Query\Builder|\Hilco\Models\WebUser whereName($value)
 * @method static \Illuminate\Database\Query\Builder|\Hilco\Models\WebUser whereCustomerId($value)
 * @method static \Illuminate\Database\Query\Builder|\Hilco\Models\WebUser whereRememberToken($value)
 * @method static \Illuminate\Database\Query\Builder|\Hilco\Models\WebUser whereDateCreated($value)
 * @method static \Illuminate\Database\Query\Builder|\Hilco\Models\WebUser whereDateModified($value)
 * @method static \Illuminate\Database\Query\Builder|\Hilco\Models\WebUser whereDateUploaded($value)
 * @method static \Illuminate\Database\Query\Builder|\Hilco\Models\WebUser whereDeletedAt($value)
 * @method static \Illuminate\Database\Query\Builder|\Hilco\Models\WebUser whereLastLogin($value)
 * @method static \Illuminate\Database\Query\Builder|\Hilco\Models\WebUser whereAuthyStatus($value)
 * @method static \Illuminate\Database\Query\Builder|\Hilco\Models\WebUser whereAuthyId($value)
 * @method static WebUser firstOrNew($attributes)
 * @mixin \Eloquent
 * @property-read \Hilco\Models\Customer $customer
 */
class WebUser extends WebModel implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract {
	use Authenticatable, Authorizable, CanResetPassword, Notifiable;

	protected $table = "WebUsers";
	protected $fillable = ['email', 'password', 'username', 'name', 'customer_id', 'is_enabled', 'needs_password_reset', 'default_websilo_id', 'parent_webuser_id', 'webRoles', 'webRoleIds', 'webUserPersonas', 'webUserPersonaIds', 'webPermissions', 'webPermissionIds', 'commit_sequence'];
	protected $hidden = ['password', 'remember_token'];
    protected $with = ['customer.segments'];

    const HIDDEN = 'HIDDEN';

    public function scopeUsername($query, $username) {
        return $query->where('username', $username);
    }

	public function customer() {
		return $this->belongsTo(Customer::class, 'customer_id', 'id');
	}

	public function payment_profile()
	{
		return $this->hasOne(UserPaymentProfile::class, 'user_id', 'id');
	}

    public function webAuthEvents() {
	    return $this->hasMany(WebAuthEvent::class, 'email', 'email');
    }

	public function webRoles()
    {
        return $this->belongsToMany(WebRole::class, 'WebRole_WebUser', 'webuser_id', 'webrole_id');
    }

    public function webPermissions() {
        return $this->belongsToMany(WebPermission::class, 'WebPermission_WebUser', 'webuser_id', 'webpermission_id');
    }

    /**
     * @return mixed
     */
    public function divisionPermissions() {
        $uniqueM3DivisionCodes =
            CustomerSegment::whereRaw('m3_division_code IS NOT NULL AND m3_division_code != ""')
                ->distinct('m3_division_code')
                ->pluck('m3_division_code')
                ->toArray();

        return $this->effective_permissions->filter(function ($perm) use ($uniqueM3DivisionCodes) {
            return in_array($perm->slug, $uniqueM3DivisionCodes);
        });
    }

    /**
     * @return BelongsToMany
     */
    public function webSilo() {
        return $this
            ->belongsToMany(WebSilo::class, 'WebSilo_WebUser', 'webuser_id', 'websilo_id')
            ->withPivot(['spending_limit', 'spending_period', 'minimum_order'])
            ;
    }

    /**
     * @return BelongsToMany
     */
    public function webUserPersonas() {
        return $this
            ->belongsToMany(WebUserPersona::class, 'WebUser_WebUserPersona', 'webuser_id', 'webuserpersona_id');
    }

    public function webSilos() {
        return $this
            ->belongsToMany(WebSilo::class, 'WebSilo_WebUser', 'webuser_id', 'websilo_id')
            ->withPivot(['spending_limit', 'spending_period', 'minimum_order'])
            ;
    }

    /**
     * @return HasOne
     */
    public function webSiloWebUser(): HasOne {
        return $this->hasOne(WebSilo_WebUser::class, 'webuser_id', 'id');
    }

    public function getWebSiloAttribute() {
        $webSilo = $this->getRelationValue('webSilo')->first();
        if ($webSilo) return $webSilo;
        return WebSilo::default();
    }

    public function defaultWebSilo() {
        return $this->belongsTo(WebSilo::class, 'default_websilo_id', 'id');
    }

    public function parentWebUser() {
        return $this->belongsTo(WebUser::class, 'parent_webuser_id', 'id');
    }

    public function favoriteFamilies() {
        return $this
            ->belongsToMany(WebFamily::class, WebFamily_WebUser::class, 'webuser_id', 'webfamily_id')
            ->where('WebFamily_WebUser.deleted_at', '=', '0000-00-00 00:00:00')
            ->webVisible()
            ->with('visibleWebParts')
        ;
    }

    /**
     * because favoriteFamilies() has all those with's.....
     * @return BelongsToMany
     */
    public function favoriteFamiliesOnly(): BelongsToMany {
        return $this->belongsToMany(WebFamily::class, WebFamily_WebUser::class, 'webuser_id', 'webfamily_id')
            ->wherePivot('WebFamily_WebUser.deleted_at', '=', '0000-00-00 00:00:00');
    }

	public function can($permissionSlug, $arguments = []) {
        return $this->checkPermission($permissionSlug);
    }

    public function hasRole($roleSlug) {
        return $this->webRoles->where('slug', $roleSlug)->count() > 0;
    }

    public function hasDirectPermission($permissions) {
        if ($permissions instanceof WebPermission) $permissions = $permissions->slug;
        if (!is_array($permissions)) $permissions = [$permissions];
        return $this->webPermissions->whereIn('slug', $permissions)->count() > 0;
    }

    public function hasRolePermission($permissions) {
        if ($permissions instanceof WebPermission) $permissions = $permissions->slug;
        if (!is_array($permissions)) $permissions = [$permissions];
        return $this->webRoles->pluck('webPermissions')->flatten()->whereIn('slug', $permissions)->count() > 0;
    }

	protected function checkPermission($permissionSlug) {
        return $this->effective_permissions->where('slug', $permissionSlug)->count() > 0;
    }

	public function getEffectivePermissionsAttribute() {
	    $rolePemissions = $this->webRoles->pluck('webPermissions')->flatten()->keyBy('id');
        $userPermissions = $this->webPermissions->keyBy('id');
        return $rolePemissions->merge($userPermissions);
	}

	public function userPhone() {
		$this->hasOne(CustomerPhone::class, 'customer_id', 'customer_id');
	}


    public function hasActiveCustomer() {
        return !is_null($this->active_customer);
    }

    public function setWebRolesAttribute($webRoleIds) {
        $this->webRoles()->sync($webRoleIds);
    }

    public function setWebRoleIdsAttribute($ids) {
        return $this->setWebRolesAttribute($ids);
    }

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

    public function setWebUserPersonasAttribute($webUserPersonaIds) {
        $this->webUserPersonas()->sync($webUserPersonaIds);
    }

    public function setWebUserPersonaIdsAttribute($ids) {
        return $this->setWebUserPersonasAttribute($ids);
    }

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

    public function setWebPermissionIdsAttribute($ids) {
        $this->webPermissions()->sync($ids);
    }

    public function webFeedbackResponses() {
	    return $this->hasMany(WebFeedbackResponse::class, 'webuser_id', 'id');
    }

    // TODO: Probably consider migrating this somewhere other than the WebUser.

    /**
     * @param CustomerShippingAddress $shippingAddress
     * @return array|int
     */
    public function getTaxes($shippingAddress) {
        $taxes = [];
        $customer = b2b()->activeCustomer();
        if (is_null($customer)) return 0;

        $segment = $customer->activeSegment;
        if (is_null($segment)) return 0;

        $taxRequestBuilder = new TaxRequestBuilder([
            'shippingAddress1' => $shippingAddress->addr_1,
            'shippingAddress2' => $shippingAddress->addr_2,
            'shippingAddress3' => $shippingAddress->addr_3,
            'shippingAddress4' => $shippingAddress->addr_4,
            'shippingAddress5' => $shippingAddress->addr_5,
            'city' => $shippingAddress->city,
            'mainDivision' => $shippingAddress->state,
            'postalCode' => $shippingAddress->postal_cd,
            'country' => $shippingAddress->country,
            'customer_code' => $segment->cust_no
        ]);

        $taxRequest = $taxRequestBuilder->requestTaxes();

        $tax = 0;
        if($taxRequest) {
            $tax = $taxRequest * 100;
        }
//        $additionalTaxCustomerSegments = $segment->additionalTaxCustomerSegment;
//        if ($additionalTaxCustomerSegments) {
//            foreach ($additionalTaxCustomerSegments as $additionalTaxCustomerSegment) {
//                $additionalTaxes = $additionalTaxCustomerSegment->additionalTax;
//                if ($additionalTaxes) {
//                    foreach ($additionalTaxes as $additionalTax) {
//                        $taxes[] = $additionalTax->tax_percentage;
//                    }
//                }
//            }
//        }
//
//        $tax = StateTax::inEffect()->whereState($shippingAddress->state)->first();
//        if ($tax) {
//            $taxes[] = $tax->tax_percentage;
//        }
//
//        return $taxes;
        return $tax;
    }

    public function getHasActiveCustomerSegmentAttribute() {
	    $customer = $this->customer;
	    if (is_null($customer)) return false;

	    return !is_null($customer->activeSegment);
    }

    public function getPrimarySalesRep() {
        $activeCustomerSegment = $this->customer->getActiveSegmentAttribute();

        if ($activeCustomerSegment != null && $activeCustomerSegment->primarySalesRep != null) {
            return $activeCustomerSegment->primarySalesRep->pointman_name;
        } else {
            return '';
        }

    }

    public function getLastSalesCallDate() {
        $activeCustomerSegment = $this->customer->getActiveSegmentAttribute();
        if ($activeCustomerSegment != null) {
            $latestCall = $activeCustomerSegment->getLatestCallAmongAffiliates();
            if ($latestCall != null) {
                $date = $latestCall->start_time;
                return date('Y-m-d', strtotime($date));
            }
        }

        return '';
    }

    public function getLicenseExpiration() {
        if ($this->customer->pharmacyInfo != null) {
            if ($this->customer->pharmacyInfo->license_expiration_date != '0000-00-00') {
                return $this->customer->pharmacyInfo->license_expiration_date;
            }
        } else {
            return '';
        }
    }

    public function getRegion() {
        return $this->customer->country;
    }

    public function getBuyingGroupName() {
        $activeCustomerSegment = $this->customer->getActiveSegmentAttribute();

        if ($activeCustomerSegment != null) {
            return $activeCustomerSegment->bill_cust_name;
        }

        return '';
    }

    public function getMicrositeNameAttribute() {
        $micrositeName = false;

        $customerWebSilo = $this->customer->webSilo;
        if ($customerWebSilo && $customerWebSilo->is_visible) {
            $micrositeName = $customerWebSilo->name;
        }

        if ($micrositeName === false) {
            $userWebSilo = $this->defaultWebSilo;
            if ($userWebSilo) {
                $micrositeName = $userWebSilo->name;
            }
        }

        return $micrositeName === false ? '' : $micrositeName;
    }

    public function get4PDesignation() {
        $customer = $this->customer;
        $customer4P = $customer->customer4P;

        if ($customer4P != null) {
            return $customer4P->fourp;
        }

        return '';
    }

    public function getRewardsTier() {
        return rewards()->tierName();
    }

    public function getTotalSpend() {
        return rewards()->totalSpending();

    }

    public function getCategoryGroupSpendingArray() {
        return rewards()->productCategoryGroups($this->customer);
    }

    public function getSpendNeeded() {
        return rewards()->calculateSpendingToUpgrade();
    }

    public function getCategoriesNeeded() {
        return rewards()->calculateCategoriesToUpgrade();
    }

    public function getCustomerCategory() {
        $customer = $this->customer;
        $customerCategory = $customer->overrideCustomerCategory ? $customer->overrideCustomerCategory : $customer->customerCategory;

        if ($customerCategory != null)
            return $customerCategory->cust_category;
        else {
            return '';
        }
    }

    public function getLanguageCodeAttribute() {

        if ($this->customer->customerLastLanguageUsed != null) {
            return $this->customer->customerLastLanguageUsed->language_code;
        }

        $language_code = false;

        $customerWebSilo = $this->customer->webSilo;
        if ($customerWebSilo && $customerWebSilo->is_visible && !is_null($customerWebSilo->defaultLanguage())) {
            $language_code = $customerWebSilo->defaultLanguage()->language_code;
        }

        if ($language_code === false) {
            $userWebSilo = $this->defaultWebSilo;
            if ($userWebSilo && !is_null($userWebSilo->defaultLanguage())) {
                $language_code = $userWebSilo->defaultLanguage()->language_code;
            }
        }

        return $language_code === false ? '' : $language_code;
    }
    
    public function getKlaviyoProfileArrayAttribute() {
        $array = [
            'id' => $this->id,
            '$email' => $this->email,
            'accountNumber' => $this->customer->cust_no,
            'name' => $this->name,
            'salesRep' => $this->getPrimarySalesRep(),
            'lastSalesCallDate' => $this->getLastSalesCallDate(),
            'licenseExpiration' => $this->getLicenseExpiration(),
            'region' => $this->getRegion(),
            'buyingGroup' => $this->getBuyingGroupName(),
            'microsite' => $this->micrositeName,
            '4pDesignation' => $this->get4PDesignation(),
            'rewardsTier' => rewards()->tierName(false, $this->customer),
            'rewardsTierLevel' => rewards()->tier($this->customer),
            'rewardsEligible' => rewards()->isEligible($this->customer),
            'totalSpend' => rewards()->totalSpending($this->customer),
            'spendNeeded' => rewards()->calculateSpendingToUpgrade($this->customer),
            'categoriesNeeded' => rewards()->calculateCategoriesToUpgrade($this->customer),
            'customerCategory' => $this->getCustomerCategory(),
            'lastWebOrderDate' => $this->webOrders->max('order_date'),
            'currency' => $this->customer->currency,
            'language' => $this->languageCode,
            'lastOrderDate' => $this->customer->recentOrders(1)->max('order_date'),
        ];

        foreach ($this->getCategoryGroupSpendingArray() as $productCategorySpending) {
            $array['spending-' . $productCategorySpending->webGroupSlug] = $productCategorySpending->spending;
        }
        return $array;
    }

    public function klaviyoIdentify() {
        $client = new Klaviyo(config('klaviyo.private_key'), config('klaviyo.public_key'));
        $array = $this->klaviyoProfileArray;
        $event = new KlaviyoProfile($array);

        $response = $client->publicAPI->identify($event);
        Log::info("Klaviyo idenfity response: " . $response);
    }

    public function klaviyoTrack($metricName, $properties) {
        $client = new Klaviyo(config('klaviyo.private_key'), config('klaviyo.public_key'));
        $event = new KlaviyoEvent([
            'event' => $metricName,
            'customer_properties' => [
                'id' => $this->id,
                '$email' => $this->email
            ],
            'properties' => $properties,
        ]);

        $client->publicAPI->track($event);
    }

    public function klaviyoListSubscribeWithConsent() {
        $listId = Arr::get($this, 'webSilo.klaviyo_list_id', false);

        if (!$listId) return false;

        $klaviyoPrivateApiKey = config('klaviyo.private_key');
        $params = [
            'profiles' => [
                [
                    'email' => $this->email,
                    '$consent' => ['email', 'web'],
                ],
            ]
        ];
        $curl = curl_init();
        curl_setopt_array($curl, [
            CURLOPT_URL => "https://a.klaviyo.com/api/v2/list/$listId/subscribe?api_key=$klaviyoPrivateApiKey",
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => '',
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => 'POST',
            CURLOPT_POSTFIELDS => json_encode($params),
            CURLOPT_HTTPHEADER => [
                'Accept: application/json',
                'Content-Type: application/json',
            ],
        ]);

        $response = curl_exec($curl);
        $error = curl_error($curl);
        curl_close($curl);

        if ($error) {
            Log::error("Error while performing Klaviyo marketing opt-in for user id " . $this->id, ['apiParams' => $params, 'curlError' => $error, 'curlResponse' => $response]);
        } else {
            Log::info("Klaviyo marketing opt-in for user id " . $this->id, ['apiParams' => $params, 'curlResponse' => $response]);
        }
    }

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

    /**
     * @return string
     */
    public static function getActiveUserPermittedDivisionsString(): string {
        $activeUser = auth()->user();
        $permittedDivisions = $activeUser->divisionPermissions()->pluck('slug')->toArray();
        if (empty($permittedDivisions)) {
            $permittedDivisions = 'NO DIVISION PERMISSIONS';
        } else {
            $permittedDivisions = implode("','", $permittedDivisions);
        }
        return $permittedDivisions;
    }

    public function sendPasswordResetNotification($token) {
        if (is_null($this->customer)) {
            $host = URL::getRequest()->getHttpHost();
            $webUrl = WebUrl::where('url', $host)->first();
            if (isset($webUrl)) {
                $webSilo = $webUrl->webSilo;
                if (isset($webSilo)) {
                    if (!is_null($webSilo->defaultLanguage())) {
                        $langObj = $webSilo->defaultLanguage();
                    } else {
                        $langObj = $webSilo->allowedLanguages->first();
                    }
                    $appName = $webSilo->display_name;
                }
            }

            if (empty($langObj)) {
                $langCode = AvailableLanguage::DEFAULT_LANG_CODE;
            } else {
                $langCode = $langObj->language_code;
            }

            if (empty($appName)) $appName = "Hilco Vision";

            $this->notify(new HilcoResetPasswordNotification($token, $langCode, $appName));
            return;
        }

        $userDefaultWebSilo = $this->customer->webSilo;
        $appName = $userDefaultWebSilo->display_name;
        if (!is_null($userDefaultWebSilo->defaultLanguage())) {
            $langObj = $userDefaultWebSilo->defaultLanguage();
        } else {
            $langObj = $userDefaultWebSilo->allowedLanguages->first();
        }
        if (is_null($langObj)) {
            $langCode = app()->getLocale();
        } else {
            $langCode = $langObj->language_code;
        }
        $this->notify(new HilcoResetPasswordNotification($token, $langCode, $appName));
    }

    use HasCommitSequence, BypassWith;
}