<?php

use Illuminate\Support\Facades\DB;
use Mtc\Shop\Item as ItemModel;

class DiscountOffer
{
    private static array $discountCache = [];

    var $id;
    var $name;
    var $type;
    var $start_date;
    var $end_date;
    var $value1;
    var $value2;
    var $restrictions_brand = array();
    var $restrictions_band = array();
    var $restrictions_categories = array();
    var $restrictions_items = array();
    var $restrictions_campaigns = array();

    var $restrictions_min_value;

    var $restrictions_apply_once;

    var $redemptions;

    var $items_matching = array();

    var $items_required;

    var $discount_value;

    var $freeitem;

    var $disabled;

    var $firstorder;

    var $friendlyname;

    var $allstock;
    public $loop_num = 0;

    private static function getCachedDiscountOffer(int $id): DiscountOffer
    {
        if (!isset(self::$discountCache[$id])) {
            self::$discountCache[$id] = new self($id);
        }

        return self::$discountCache[$id];
    }

    /**
     * DiscountOffer constructor.
     *
     * @param int $id ID of offer to load
     * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     */
    public function __construct($id = 0)
    {
        if ((int)$id > 0) {
            $this->Get($id);
        }
    }

    private function setArrayValues(array $data)
    {
        $data['restrictions_brand'] = implode(",", $this->restrictions_brand);
        $data['restrictions_band'] = implode(",", $this->restrictions_band);
        $data['restrictions_categories'] = implode(",", $this->restrictions_categories);
        $data['restrictions_items'] = implode(",", $this->restrictions_items);
        $data['restrictions_campaigns'] = implode(",", $this->restrictions_campaigns);
    }

    /**
     * @return void
     */
    public function Add(): void
    {
        $data = (array)$this;
        $this->setArrayValues($data);
        $discountOffer = \Mtc\Shop\DiscountOffer::query()
            ->create($data);

        $this->id = $discountOffer->id;
    }

    /**
     * @return void
     */
    public function Update(): void
    {
        $data = (array)$this;
        $this->setArrayValues($data);
        \Mtc\Shop\DiscountOffer::query()
            ->where('id', $this->id)
            ->update($data);
    }

    function Get($id): void
    {
        $discountOffer = \Mtc\Shop\DiscountOffer::query()
            ->find($id);

        if (empty($discountOffer)) {
            return;
        }
        $data = $discountOffer->toArray();

        $this->id = $data['id'];
        $this->name = $data['name'];
        $this->type = $data['type'];
        $this->start_date = $data['start_date'];
        $this->end_date = $data['end_date'];
        $this->value1 = $data['value1'];
        $this->value2 = $data['value2'];


        if ($data['restrictions_brand'] != '') {
            $this->restrictions_brand = explode(",", $data['restrictions_brand']);
        } else {
            $this->restrictions_brand = array();
        }

        if ($data['restrictions_band'] != '') {
            $this->restrictions_band = explode(",", $data['restrictions_band']);
        } else {
            $this->restrictions_band = array();
        }

        if ($data['restrictions_categories'] != '') {
            $this->restrictions_categories = explode(",", $data['restrictions_categories']);
        } else {
            $this->restrictions_categories = array();
        }

        if ($data['restrictions_items'] != '') {
            $this->restrictions_items = explode(",", $data['restrictions_items']);
        } else {
            $this->restrictions_items = array();
        }

        if ($data['restrictions_campaigns'] != '') {
            $this->restrictions_campaigns = explode(",", $data['restrictions_campaigns']);
        } else {
            $this->restrictions_campaigns = array();
        }

        $this->restrictions_min_value = $data['restrictions_min_value'];
        $this->redemptions = $data['redemptions'];
        $this->disabled = $data['disabled'];

        $this->freeitem = $data['freeitem'];

        $this->firstorder = $data['firstorder'];


        $this->allstock = $data['allstock'];

        if ($data['type'] == 'nforn') {
            $this->items_required = $data['value1'];
            $this->friendlyname = round($data['value1']) . ' for the price of ' . round($data['value2']);
        }

        if ($data['type'] == 'nforpricen') {
            $this->items_required = $data['value1'];
        }

        if ($data['type'] == 'amountoff') {
            $this->items_required = 1;
        }

        if ($data['type'] == 'percentoff') {
            $this->items_required = 1;
        }

        if ($data['type'] == 'freewithorder') {
            $this->items_required = 1;
            $this->restrictions_apply_once = true;
        }
    }

    /**
     * Check if basket item is valid for a discount
     *
     * @param  $basket_item
     * @param  $item_position
     * @param  $order_value
     * @param  $existing_discounts
     * @param  $basket_items
     * @param  $email
     * @param Item[] $basket_item_objects All basket items as Item objects for data checks
     * @return bool
     */
    public function check($basket_item, $item_position, $order_value, $existing_discounts, $basket_items, $email, $basket_item_objects)
    {
        $matched = false;

        // Use array of all items provided rather than loading all item data on every basket item check
        foreach ($basket_item_objects as $key => $item) {
            if ($key == $basket_item['item_id']) {
                break;
            }
        }

        //Check categories
        if (!empty($item->categories)) {
            foreach ($item->categories as $cat_data) {
                if (in_array($cat_data['cat_id'], $this->restrictions_categories)) {
                    $matched = true;
                }
                if (in_array($cat_data['cat_id'], $this->restrictions_campaigns)) {
                    $matched = true;
                }
            }
        }

        //Check Brands
        if (in_array($item->brand, $this->restrictions_brand)) {
            $matched = true;
        }

        //Check Items
        if (in_array($item->id, $this->restrictions_items)) {
            $matched = true;
        }

        //If theres no other restictions then apply to all
        if (empty($this->restrictions_items)
            && empty($this->restrictions_categories)
            && empty($this->restrictions_brand)
            && empty($this->restrictions_campaigns)
            && $this->allstock == 1
        ) {
            $matched = true;
        }

        if ($order_value < $this->restrictions_min_value) {
            $matched = false;
        }


        if ($this->restrictions_apply_once) {
            foreach ($existing_discounts as $discount) {
                if ($discount['discount_id'] == $this->id) {
                    $matched = false;
                }
            }
        }

        //Check the only item in the basket isn't the free one
        if ($this->type == 'freewithorder'
            && count($basket_items) == 1
            && $basket_items[0]['item_id'] == $this->freeitem
        ) {
            $matched = false;
        }

        if ($this->firstorder) {
            if (!empty($email)) {
                $matched = false;
            } else {
                if (DB::table('order_info')->where('email', $email)->count()
                ) {
                    $matched = false;
                }
            }
        }

        if ($matched) {
            $this->loop_num++;
            $basket_item['position'] = $item_position;
            $this->items_matching[] = $basket_item;
            if ($this->loop_num == $this->items_required) {
                $this->Calculate_Discount();
                $this->loop_num = 0;
                if ($this->discount_value <= 0) {
                    $this->discount_value = 0;
                    return true;
                } else {
                    return true;
                }
            }
        }
        return false;
    }

    function Calculate_Discount()
    {
        //  echo "calculating discount";

        if ($this->type == 'nforn') {

            $values = array();

            $currentvalue = $this->items_matching[0]['item_price'] * $this->value1;
            $totalvalue = 0;
            foreach ($this->items_matching as $item) {
                $values[] = $item['item_price'];
                $totalvalue += $item['item_price'];
            }


            rsort($values);

            //print_r($values);

            $remainingvalue = 0;
            for ($i = 0; $i < $this->value2; $i++) {
                $remainingvalue += $values[$i];

            }

            //echo "Remaing value:" . $remainingvalue;
            //exit();


            // $newvalue=$this->items_matching[0]['item_price']*$this->value2;

            $this->discount_value = $totalvalue - $remainingvalue;
        }


        if ($this->type == 'nforpricen') {
            // echo "nforn";

            //print_r($this->items_matching);

            $currentvalue = 0;

            foreach ($this->items_matching as $item) {
                $currentvalue += $item['item_price'];
            }

            //    $currentvalue=$this->items_matching[0]['item_price']*$this->value1;
            $newvalue = $this->value2;

            $this->discount_value = $currentvalue - $newvalue;
        }

        if ($this->type == 'amountoff') {
            $this->discount_value = $this->value1;
        }

        if ($this->type == 'percentoff') {
            $this->discount_value = round($this->items_matching[0]['item_price'] * $this->value1 / 100, 2);
        }

        if ($this->type == 'freewithorder') {

        }

    }


    function AddRedemption()
    {
        $this->redemptions++;
        $this->Update();

    }

    /**
     * Check to see if this multi buy discount applies to this item
     *
     * @param Item $item item objected
     * @return bool
     */


    function checkItem($item)
    {

        $matched = false;

        $categoryIds = [];
        if (!empty($item['category_ids'])) {
            $categoryIds = array_map('intval', (array)$item['category_ids']);
        } elseif (!empty($item->categories)) {
            foreach ($item->categories as $cat_data) {
                $categoryIds[] = isset($cat_data['cat_id']) ? (int)$cat_data['cat_id'] : (int)$cat_data['id'];
            }
        }

        if (!empty($categoryIds)) {
            if (!empty($this->restrictions_categories) && array_intersect($categoryIds, $this->restrictions_categories)) {
                $matched = true;
            }
            if (!empty($this->restrictions_campaigns) && array_intersect($categoryIds, $this->restrictions_campaigns)) {
                $matched = true;
            }
        }

        $brandId = $item['brand'] ?? $item->brand ?? null;
        if (!empty($this->restrictions_brand) && $brandId !== null && in_array($brandId, $this->restrictions_brand)) {
            $matched = true;
        }

        $itemId = $item['item_id'] ?? $item->id ?? null;
        if (!empty($this->restrictions_items) && $itemId !== null && in_array($itemId, $this->restrictions_items)) {
            $matched = true;
        }

        if (!$matched && empty($this->restrictions_items)
            && empty($this->restrictions_categories)
            && empty($this->restrictions_brand)
            && empty($this->restrictions_campaigns)
            && $this->allstock == 1
        ) {
            $matched = true;
        }

        return $matched;

    }

    private static function buildBasketItemObject(Basket $basket, array &$cache, int $itemId)
    {
        if (isset($cache[$itemId])) {
            return $cache[$itemId];
        }

        $itemModel = method_exists($basket, 'getCachedItemModel') ? $basket->getCachedItemModel($itemId) : null;

        if ($itemModel instanceof ItemModel) {
            $item = new Item();
            $item->id = $itemModel->id;
            $brand = $itemModel->brands->first();
            $item->brand = $brand ? $brand->id : null;
            $item->band = $itemModel->getAttribute('band');
            $item->categories = $itemModel->categories->map(function ($category) {
                return [
                    'cat_id' => $category->id,
                    'id' => $category->id,
                ];
            })->values()->toArray();

            return $cache[$itemId] = $item;
        }

        return $cache[$itemId] = new Item($itemId);
    }

    /**
     * Apply all relevant discount offers on basket items.
     * Originally this was recursive which caused fatal error
     * when certain basket item amount reached. Currently uses looping.
     *
     * @param Basket $basket Current basket
     * @author  Alan Reid
     * @author  Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     * @version 2 03/09/2016
     */
    public static function applyOnBasket(Basket $basket)
    {
        $discounts = DB::table('discountoffers')
            ->where('disabled', 0)
            ->pluck('id');

        foreach ($discounts as $key => $discount) {
            $discounts[$key] = self::getCachedDiscountOffer((int) $discount);
        }

        $individual_items = [];
        $basket_item_objects = [];
        foreach ($basket->items as $item) {
            $quantity = $item['quantity'];
            $item['quantity'] = 1;
            if (empty($basket_item_objects[$item['item_id']])) {
                $basket_item_objects[$item['item_id']] = self::buildBasketItemObject($basket, $basket_item_objects, $item['item_id']);
            }
            for ($i = 0; $i < $quantity; $i++) {
                $individual_items[] = $item;
            }
        }

        $discounts_grouped = [];

        // Lets loop through all discounts
        foreach ($discounts as $discount) {
            foreach ($individual_items as $index => $item) {

                // If item doesn't have a discount and discount can be applied
                if (empty($item['discounted'])
                    && $discount->check(
                        $item,
                        $index,
                        $basket->cost_subtotal,
                        $basket->discounts_found,
                        $basket->singleitems,
                        $basket->info['email'],
                        $basket_item_objects
                    )
                ) {
                    // If discount gives an item with purchase
                    if ($discount->type == 'freewithorder') {
                        $discount_value = 0;
                        //Check If item is already in basket
                        foreach ($basket->items as $basket_item) {
                            if ($basket_item['item_id'] == $discount->freeitem) {
                                $item_found = true;
                                $discount_value = $item['item_price'];
                            }
                        }

                        if (empty($item_found)) {
                            $tmp_item = new Item();
                            $tmp_item->Get_Item($discount->freeitem);
                            $discount_value = $tmp_item->price;

                            $params = (array)DB::table('items_sizes')
                                ->where('item_id', $discount->freeitem)
                                ->first();

                            if ($params) {
                                $params['id'] = $params['item_id'];
                                $params['quantity'] = 1;
                                $basket->Add_Item($params);
                            } else {
                                // If item has no sizes add main item to basket
                                $params = [
                                    'quantity' => 1,
                                    'id' => $discount->freeitem
                                ];
                                $basket->Add_Item($params);
                            }

                            $individual_items[] = [
                                'item_id' => $discount->freeitem,
                                'quantity' => 1
                            ];
                        }
                        $basket->discounts_found[] = [
                            'discount_amount' => $discount_value,
                            'discount_type' => $discount->type,
                            'discount_id' => $discount->id,
                            'discount_name' => $discount->name
                        ];
                    } else {
                        if ($discount->discount_value > 0) {
                            // Grouping discounts by discount ID and adding up total discount.
                            if ($discounts_grouped[$discount->id]) {
                                $discounts_grouped[$discount->id]['discount_amount'] += $discount->discount_value;
                                $discounts_grouped[$discount->id]['count']++;
                            } else {
                                $discounts_grouped[$discount->id]['discount_name'] = $discount->name;
                                $discounts_grouped[$discount->id]['discount_type'] = $discount->type;
                                $discounts_grouped[$discount->id]['discount_amount'] = $discount->discount_value;
                                $discounts_grouped[$discount->id]['count'] = 1;
                            }

                        }

                        foreach ($discount->items_matching as $matching_item) {
                            $individual_items[$matching_item['position']]['discounted'] = true;
                        }
                        $discount->items_matching = [];
                    }
                }

            }

        }

        // Adding grouped discounts to basket
        foreach ($discounts_grouped as $discount_id => $discount_group) {
            $basket->discounts_found[] = [
                'discount_amount' => $discount_group['discount_amount'],
                'discount_type' => $discount_group['discount_type'],
                'discount_id' => $discount_id,
                'discount_name' => $discount_group['discount_name'] . ($discount_group['count'] > 1 ? " (x" . $discount_group['count'] . ")" : "")
            ];
        }

    }

}
