<?php

namespace Hilco\Models;

use Auth;
use Carbon\Carbon;
use DB;
use Debugbar;
use Exception;
use HilcoB2B\M3Request;
use Hilco\GuzzleWrappers\APIGuzzleWrapper;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Klaviyo\Klaviyo;
use Klaviyo\Model\EventModel as KlaviyoEvent;
use Log;

/**
 * Hilco\Models\SubscriptionGroup
 * @property Customer $billToCustomer
 * @property Collection|SubscriptionItem[] $subscriptionItems
 */
class SubscriptionGroup extends UuidModel
{
    protected $table = 'SubscriptionGroups';
    protected $fillable = ['subscriptiongroup_type'];


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

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

    public function customerShippingAddress() {
        return $this->hasOne(CustomerShippingAddress::class, 'id', 'customershippingaddress_id');
    }

    public function createdByWebUser() {
        return $this->hasOne(WebUser::class, 'id', 'createdby_webuser_id');
    }
    public function modifiedByWebUser() {
        return $this->hasOne(WebUser::class, 'id', 'lastmodifiedby_webuser_id');
    }

    public function subscriptionItems() {
        return $this->hasMany(SubscriptionItem::class, 'subscriptiongroup_id', 'id');
    }

    public function promotions() {
        return $this->belongsToMany(Promotion::class,
            'Promotion_SubscriptionGroup',
            'subscriptiongroup_id',
            'promotion_id')
            ->wherePivot('applied', '=', 1)
            ->wherePivot('deleted_at', '=', '0000-00-00 00:00:00');
    }

    public function subscriptionPeriod() {
        return $this->hasOne(SubscriptionPeriod::class, 'id', 'subscriptionperiod_id');
    }

    public static function sendKlaviyoEventForSubscriptionGroup($eventName, $subscriptionGroup, $cartStatus, $changedItem = null, $error = null) {

        $webSilo = $subscriptionGroup->modifiedByWebUser->customer->webSilo;
        $client = new Klaviyo($webSilo->klaviyo_api_key, $webSilo->klaviyo_company_id);

        $itemArray = [];
        foreach($subscriptionGroup->subscriptionItems as $subscriptionItem) {
            $itemArray[] = $subscriptionItem->getBaseItemWithPricesKlaviyoArray(
                $cartStatus['items'][$subscriptionItem->id]['discountedPrice'],
                $cartStatus['items'][$subscriptionItem->id]['discountedPrice']
            );
        }

        if ($changedItem != null) {
            $event = new KlaviyoEvent([
                'event' => $eventName,
                'customer_properties' => [
                    '$id' => $subscriptionGroup->modifiedByWebUser != null ? $subscriptionGroup->modifiedByWebUser->id : '',
                    '$email' => $subscriptionGroup->modifiedByWebUser != null ? $subscriptionGroup->modifiedByWebUser->email : ''
                ],
                'properties' => [
                    'error_message' => $error != null ? $error : '',

                    'group_name' => $subscriptionGroup->label,
                    'next_order_date' => $subscriptionGroup->next_order_date,
                    'cadence' => $subscriptionGroup->subscriptionPeriod->period_name,
                    'delayed_counter' => $subscriptionGroup->delayed_counter,
                    'group_type' => $subscriptionGroup->subscriptiongroup_type,
                    
                    'billto_customer_number' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->cust_no : '',
                    'billto_customer_name' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->cust_name : '',
                    'billto_addr_1' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_addr_1 : '',
                    'billto_addr_2' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_addr_2 : '',
                    'billto_addr_3' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_addr_3 : '',
                    'billto_addr_4' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_addr_4 : '',
                    'billto_addr_5' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_addr_5 : '',
                    'billto_city' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_city : '',
                    'billto_state' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_state : '',
                    'billto_zip' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_zip_postal : '',
                    'billto_country' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_country : '',

                    'shipto_addr_number' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->addr_no : '',
                    'shipto_customer_name' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->cust_name : '',
                    'shipto_addr_1' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->addr_1 : '',
                    'shipto_addr_2' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->addr_2 : '',
                    'shipto_addr_3' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->addr_3 : '',
                    'shipto_addr_4' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->addr_4 : '',
                    'shipto_addr_5' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->addr_5 : '',
                    'shipto_city' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->city : '',
                    'shipto_state' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->state : '',
                    'shipto_zip' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->postal_cd : '',
                    'shipto_country' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->country : '',

                    'items' => $itemArray,
                    'changedItem' => $changedItem,
                    'discountTotal' => number_format($cartStatus['baseTotal'] - $cartStatus['discountedTotal'], 2),
                    'taxAmount' => $cartStatus['taxAmount'],
                    'orderTotal' => $cartStatus['orderTotal'],
                ],
            ]);
        } else {
            $event = new KlaviyoEvent([
                'event' => $eventName,
                'customer_properties' => [
                    '$id' => $subscriptionGroup->modifiedByWebUser != null ? $subscriptionGroup->modifiedByWebUser->id : '',
                    '$email' => $subscriptionGroup->modifiedByWebUser != null ? $subscriptionGroup->modifiedByWebUser->email : ''
                ],
                'properties' => [
                    'error_message' => $error != null ? $error : '',

                    'group_name' => $subscriptionGroup->label,
                    'next_order_date' => $subscriptionGroup->next_order_date,
                    'cadence' => $subscriptionGroup->subscriptionPeriod->period_name,
                    'delayed_counter' => $subscriptionGroup->delayed_counter,
                    'group_type' => $subscriptionGroup->subscriptiongroup_type,


                    'billto_customer_number' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->cust_no : '',
                    'billto_customer_name' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->cust_name : '',
                    'billto_addr_1' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_addr_1 : '',
                    'billto_addr_2' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_addr_2 : '',
                    'billto_addr_3' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_addr_3 : '',
                    'billto_addr_4' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_addr_4 : '',
                    'billto_addr_5' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_addr_5 : '',
                    'billto_city' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_city : '',
                    'billto_state' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_state : '',
                    'billto_zip' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_zip_postal : '',
                    'billto_country' => $subscriptionGroup->billToCustomer != null ? $subscriptionGroup->billToCustomer->bill_country : '',

                    'shipto_addr_number' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->addr_no : '',
                    'shipto_customer_name' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->cust_name : '',
                    'shipto_addr_1' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->addr_1 : '',
                    'shipto_addr_2' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->addr_2 : '',
                    'shipto_addr_3' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->addr_3 : '',
                    'shipto_addr_4' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->addr_4 : '',
                    'shipto_addr_5' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->addr_5 : '',
                    'shipto_city' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->city : '',
                    'shipto_state' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->state : '',
                    'shipto_zip' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->postal_cd : '',
                    'shipto_country' => $subscriptionGroup->customerShippingAddress != null ? $subscriptionGroup->customerShippingAddress->country : '',

                    'items' => $itemArray,
                    'discountTotal' => number_format($cartStatus['baseTotal'] - $cartStatus['discountedTotal'], 2),
                    'taxAmount' => $cartStatus['taxAmount'],
                    'orderTotal' => $cartStatus['orderTotal'],
                ],
            ]);
        }

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

    public function orders() {
        return $this->belongsToMany(Order::class, 'SalesOrder_SubscriptionGroup', 'subscriptiongroup_id', 'salesorder_id');
    }

    public function getOrderCounterAttribute() {
        return $this->orders()->count();
    }

    public function scopeSubscriptiongroupType($query, $type) {
        $query->where('subscriptiongroup_type', $type);
    }

    public function calculateAndUpdateSubscriptionGroupType() {
        $type = 'default';
        foreach ($this->subscriptionItems as $item) {
            if (in_array(\Arr::get($item, 'part.item_type', ''), config('hilco.custom_item_types', ['D15']))) {
                $type = 'custom';
            }
        }
        $this->subscriptiongroup_type = $type;
        $this->save();
    }

    public function submissionRule() {
        return $this->hasOne(SubscriptionSubmissionRules::class, 'subscriptiongroup_type', 'subscriptiongroup_type');
    }

    public function notificationRules() {
        return $this->hasMany(SubscriptionNotificationRules::class, 'subscriptiongroup_type', 'subscriptiongroup_type');
    }

    public function getNextSubmissionDateAttribute() {
        $submissionRule = $this->submissionRule;
        if (is_null($submissionRule)) return false;

        if ($submissionRule->count_from == 'next_order_date') $sourceDate = Carbon::create($this->next_order_date);
        else $sourceDate = false;

        if ($sourceDate === false) return false;

        $counter = $submissionRule->count_days;
        while ($counter > 0) {
            if ($submissionRule->day_type === 'calendar') {
                $sourceDate = $sourceDate->subDay();
            } else if ($submissionRule->day_type === 'business') {
                $sourceDate = $sourceDate->subDay();
                while ($sourceDate->isWeekend()) {
                    $sourceDate = $sourceDate->subDay();
                }
            } else {
                return false;
            }

            $counter--;
        }

        return $sourceDate->toDateString();
    }

    public function getNextNotificationDatesAttribute() {
        $notificationRules = $this->notificationRules;
        if (is_null($notificationRules)) return [];

        $return = [];

        foreach ($notificationRules as $notificationRule) {

            if ($notificationRule->count_from === 'next_order_date') $sourceDate = Carbon::create($this->next_order_date);
            else if ($notificationRule->count_from === 'submission_date') $sourceDate = Carbon::create($this->next_submission_date);
            else $sourceDate = false;

            ///WHAT
            if ($sourceDate === false) {
                $return[$notificationRule->notification_type] = false;
                continue;
            }

            $counter = $notificationRule->count_days;
            while ($counter > 0) {
                if ($notificationRule->day_type === 'calendar') {
                    $sourceDate = $sourceDate->subDay();
                } else if ($notificationRule->day_type === 'business') {
                    $sourceDate = $sourceDate->subDay();
                    while ($sourceDate->isWeekend()) {
                        $sourceDate = $sourceDate->subDay();
                    }
                } else {
                    return false;
                }

                $counter--;
            }

            $return[$notificationRule->notification_type] = $sourceDate->toDateString();
        }

        return $return;
    }

    /**
     * @param $customerPriceListName
     * @param $siloPriceListName
     * @param $currencyCode
     * @param $webCartItems
     * @return array
     * [
     *     part_id => [
     *         'baseprice' => base price of the part from the price list used,
     *         'isContractPrice' => boolean value from PriceLists->contract_flag,
     *         'priceListUsed' => name of the price list that was used,
     *         'totalQuantity' => total quantity of this part in the cart
     *     ]
     * ]
     */
    public static function getDistinctPartInfo($subscriptionItems, $customer = null, $webuser = null, $useDummy = true) {
        if ($customer == null) {
            $customer = b2b()->activeCustomer();
        }

        if ($webuser == null) {
            $webuser = auth()->user();
        }

        $distinctPartQuantities = [];
        foreach ($subscriptionItems as $subscriptionItem) {
            if ($subscriptionItem->isAvailable()) {
                $distinctPartQuantities[$subscriptionItem->part->id]['partNumber'] = $subscriptionItem->part->part_number;
                array_increment($distinctPartQuantities[$subscriptionItem->part->id],'quantity', $subscriptionItem->quantity);
            }
        }

        $distinctLineQuantities = [];

        foreach($subscriptionItems as $subscriptionItem){
            if ($subscriptionItem->isAvailable()) {
                array_push($distinctLineQuantities, [
                    'partId' => $subscriptionItem->part_id,
                    'partNumber' => $subscriptionItem->part->part_number,
                    'quantity' => $subscriptionItem->quantity,
                ]);
            }
        }

        $distincts = [
            'distinctLineInfo' => [],
            'distinctPartInfo' => []
        ];
        try {
            $distinctPartInfo = self::fetchAllItemPrices($distinctPartQuantities, false, $customer, $webuser, $useDummy);
            $distinctLineInfo = self::fetchAllItemPrices($distinctLineQuantities, true, $customer, $webuser, $useDummy);
            $distincts = [
                'distinctLineInfo' => $distinctLineInfo,
                'distinctPartInfo' => $distinctPartInfo
            ];
        }catch(Exception $e){
            Log::error("Exception caught while fetching prices: " . $e->getMessage());
        }

        return $distincts;
    }

    public static function fetchAllItemPrices($pricingData, $includeDiscounts = true, $customer = null, $webuser = null, $useDummy = true){
        $client = null;
        $guzzle = new M3Request();


        if ($customer == null) {
            $customer = b2b()->activeCustomer();
        } 

        $webSilo = b2b()->activeWebSilo();
        $facility = $customer->activeSegment->facility_id;

        if ($useDummy) {
            // This is where we need overrides for using the dummy customer (See Email from Kelly - Pricing Flow VW 12-17-20.docx)

            $customersegment = $customer->activeSegment;
            $soldto = $customer;
            $soldtoparent = $customersegment->parentCustomer; // could be null
            $payer = $customersegment->billToCustomer;
            $payerParent = $payer->activeSegment->parentCustomer; // could be null

            $dummyApplies = true;

            // Check to see if these overrides should just be skipped and go straight to never showing dummy
            if ($webSilo->never_override_listprice == 1) {
                $dummyApplies = false;
            } else {
                //Otherwise check the rules for specific circumstance

                //if sold-to = payer
                if ($webSilo->soldto_payer_listprice == 1 && $soldto->cust_no == $payer->cust_no) {
                    $dummyApplies = false;
                }
                // if sold-to parent = payer
                if ($webSilo->soldtoparent_payer_listprice == 1 && $soldtoparent != null && $soldtoparent->cust_no == $payer->cust_no) {
                    $dummyApplies = false;
                }
                // if soldto parent = payer parent
                if ($webSilo->soldtoparent_payerparent_listprice == 1 &&  $soldtoparent != null && $payerParent != null && $soldtoparent->cust_no = $payerParent->cust_no) {
                    $dummyApplies = false;
                }
            }

            if ($dummyApplies) {
                $customer = $customer->getDummyCustomer();
            }
        }

        if ($webuser == null) {
            $webuser = auth()->user();
        }

        if(empty($guzzle->client)){
            Log::error('Exception Caught while performing ' . __FUNCTION__ . ': Guzzle Http Client does not exist. Base URI may be missing!');
            return trans('messages.temporarily_unavailable');
        }else{
            $client = $guzzle->client;
        }

        $chunks = array_chunk($pricingData, 50);
        $response = [];

        foreach($chunks as $chunk){
//            Log::debug("Calling getItemPrice on Hilco API: webUserID => " . auth()->user()->id . " customerNumber => " . $customer->cust_no . " requestInfo => " . json_encode($chunk));


            $chunkResponse = json_decode($client->post('v4/' . 'getItemPrice', ['form_params' => ['requestFrom' => 'b2b', 'webuser' => $webuser->id,
                'customer_number' => $customer->cust_no, 'pricingData' => $chunk, 'facility' => $facility],
                'headers' => ['Accept' => 'application/json']])->getBody()->getContents(), true);

//            Log::debug("B2B getItemPrice chunked response : " . json_encode($chunkResponse));
            $response = array_merge($response, $chunkResponse);
        }

        $prices = [];

        foreach($response as $infoId => $partPriceInfo) {
            if (is_countable($partPriceInfo['details']) && count($partPriceInfo['details']) == 1) {
//                Log::error('Exception Caught while performing ' . __FUNCTION__ . ': ' . $partPriceInfo['details']);
                $prices[$infoId]['basePrice'] = trans('messages.temporarily_unavailable');
            } else if (\Illuminate\Support\Arr::has($partPriceInfo['details'], 'MIRecord')) {
                $results = $partPriceInfo['details']['MIRecord'][0]['NameValue'];
                foreach ($results as $result) {
                    if ($result['Name'] == 'SAPR') {
                        $returnMe = trim($result['Value'], ' ');
                        if (empty($returnMe)) {
                            $prices[$infoId]['basePrice'] = trans('messages.temporarily_unavailable');
                        }else {
                            $prices[$infoId]['basePrice'] = trim($result['Value'], ' ');
                        }
                    }

                    if(!empty($prices[$infoId])) {
                        if ($prices[$infoId]['basePrice'] != trans('messages.temporarily_unavailable')) {
                            if ($result['Name'] == 'NTCD') {
                                $prices[$infoId]['isContractPrice'] = trim($result['Value'], ' ') ? true : false;
                            } else if ($result['Name'] == 'PRRO') {
                                $prices[$infoId]['priceListUsed'] = trim($result['Value'], ' ');
                            }
                        }
                    }
                }

                if($prices[$infoId]['basePrice'] != trans('messages.temporarily_unavailable') && $includeDiscounts) {
                    foreach ($partPriceInfo['discounts'] as $discountNum => $discountInfo) {
                        $discType = null;
                        $hasDiscount = false;
                        foreach ($discountInfo as $key => $value) {
                            if (Str::contains($key, 'TX8')) {
                                $m3DiscountCode = M3DiscountCode::where('discount_code', '=', trim($value, ' '))->first();
                                if(!empty($m3DiscountCode)) {
                                    switch ($m3DiscountCode->discount_type) {
                                        case 'Other':
                                            $discType = 'customer';
                                            break;
                                        case 'Rewards':
                                            $discType = 'rewards';
                                            break;
                                        case 'One-Off':
                                            $discType = 'promotion';
                                            break;
                                    }
                                }else{
                                    $discType = trim($value, ' ');
                                }
                            } else if (Str::contains($key, 'DIC')) {
                                if ($value > 0) {
                                    $hasDiscount = true;
                                }
                            }
                        }

                        if ($hasDiscount && ($discType != null || $discType != '')) {
                            if ($discType == 'customer') {
                                $prices[$infoId]['discounts']['customer'] = $discountInfo;
                            } else if($discType == 'rewards'){
                                $quantity = !empty($pricingData[$infoId]['quantity']) ? $pricingData[$infoId]['quantity'] : 1;
                                $price = round($prices[$infoId]['basePrice'] * $quantity, 2);
                                $prices[$infoId]['discounts']['rewards'] = self::populateRewardsDiscInfo($partPriceInfo['part'], $price, $discountInfo, $customer);
                            } else if($discType == 'promotion'){
                                $prices[$infoId]['discounts']['promotion'] = $discountInfo;
                            } else{
                                $prices[$infoId]['discounts'][$discType] = $discountInfo;
                            }
                        }
                    }
                }
            } else {
//                Log::error('Exception Caught while performing ' . __FUNCTION__ . ': ' . $partPriceInfo['details']['Message']);
                $prices[$infoId]['basePrice'] = trans('messages.temporarily_unavailable');
            }

            if(!empty($pricingData[$infoId]['quantity'])){
                $prices[$infoId]['totalQuantity'] = $pricingData[$infoId]['quantity'];
            }else{
                foreach($pricingData as $priceData){
                    if(!empty($priceData['quantity'])){
                        if($priceData['partNumber'] == $partPriceInfo['part']){
                            $prices[$infoId]['totalQuantity'] = $priceData['quantity'];
                        }
                    }
                }
            }

            if (!empty($pricingData[$infoId]['partId'])) {
                $prices[$infoId]['partId'] = $pricingData[$infoId]['partId'];
            }else if(!empty($pricingData[$infoId]['familyId'])){
                $prices[$infoId]['familyId'] = $pricingData[$infoId]['familyId'];
            }else if(!empty($partPriceInfo['part'])){
                $prices[$infoId]['partNum'] = $partPriceInfo['part'];
            }
        }

        return $prices;
    }

    private static function populateRewardsDiscInfo($partNumber, $regularExtPrice, $discInfo, $customer = null){

        if ($customer == null) {
            $customer = b2b()->activeCustomer();
        }
        $discPercentage = 0;

        $part = Part::wherePartNo($partNumber)->whereNull('deleted_at')->first();
        if(!is_null($part)){
            if(!$part->isRewardsExcluded()) {
                $discPercentage = rewards()->discountForPart($part, $customer);
                $discAmount = round($regularExtPrice * ($discPercentage / 100), 2);
                foreach ($discInfo as $key => $value) {
                    if (strpos($key, "DIP") !== false) {
                        $discInfo[$key] = $discPercentage;
                    } else if (strpos($key, "DIA") !== false) {
                        $discInfo[$key] = $discAmount;
                    }
                }
            }else{
                foreach ($discInfo as $key => $value) {
                    if (strpos($key, "DIP") !== false) {
                        $discInfo[$key] = 0;
                    } else if (strpos($key, "DIA") !== false) {
                        $discInfo[$key] = 0;
                    }
                }
            }
        }

        return $discInfo;
    }
}