<?php

/**
 * Complimented Items
 *
 * Get Complimented items from item
 *
 * @author Andrew Morgan <andrew.morgan@mtcmedia.co.uk>
 * @contributor Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
 */

namespace Mtc\Plugins\ComplimentedItems\Classes;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Item;
use Member;
use Twig\Environment;

class ComplimentedItems extends Model
{
    /**
     * @var string Classes associated table
     */
    protected $table = 'items_complimented';

    /**
     * @param string[] $fillable Fillable fields when using ->fill()
     */
    protected $fillable = [
        'item_id',
        'complimented_item_id',
        'order',
    ];

    /**
     * ComplimentedItems::addTabNav()
     *
     * Add tabNav item to item navigation
     *
     * @access public
     * @static
     * @param Item $item Currently opened item
     * @param array $request $_REQUEST
     * @param Environment $twig Twig Handler
     * @return string $nav_item html code for navigation item
     */
    public static function addTabNav(Item $item, array $request, Environment $twig)
    {
        if (empty(COMPLIMENTED_MAX) || empty($request['id'])) {
            return false;
        }
        $nav_item = $twig->render('ComplimentedItems/admin/tabnav.twig', [
            'item' => $item,
            'request' => $request,
            'request_uri' => $_SERVER['REQUEST_URI'],
        ]);

        return $nav_item;
    }

    /**
     * ComplimentedItems::getComplimented()
     *
     * Retrieves items complimented items.
     * Called via 'render_item' hook in shop/item.php
     * Does not return data, instead adds complimented items as twig variable
     *
     * @param array $args list of arguments
     * @return null
     */
    public static function getComplimented($args)
    {
        $item = &$args[0];
        $twig = &$args[1];
        $member = &$args[2];

        if (defined('COMPLIMENTED_SUPPLEMENT') && COMPLIMENTED_SUPPLEMENT === true) {
            $complimented_items = self::getItems($item, $member);
        }

        $complimented_item_html = [];
        if (! empty($complimented_items)) {
            foreach ($complimented_items as $complimented_id) {
                $complimented_item = new Item;
                $complimented_item->Get_Item($complimented_id);
                $complimented_item_html[] = $twig->render('shop/item/small.twig', array(
                    'item' => $complimented_item,
                ));
            }
        }

        $twig->addGlobal('complimented_items', $complimented_item_html);
    }

    /**
     * ComplimentedItems::getItems()
     *
     * Retrieve Complimented items for a product
     *
     * @access public
     * @static
     * @param Item $item item for whom to fetch complimented items
     * @param mixed $member Member object or null if members disabled
     * @return array $complimented list of complimented items
     */
    public static function getItems(Item $item, $member)
    {
        $complimented = [];

        if (count($complimented) < COMPLIMENTED_MAX) {
            // Retrieve all Complementary items
            $complimented = self::leftJoin('items', function ($join): void {
                $join->on('items.id', '=', 'items_complimented.item_id')
                    ->where('items.deleted', '=', 0);
            })
                ->where('item_id', $item->id)
                ->take(COMPLIMENTED_MAX)
                ->pluck('complimented_item_id')
                ->toArray();
        }

        if (COMPLIMENTED_SUPPLEMENT === true && count($complimented) < COMPLIMENTED_MAX) {
            $complimented = self::getSupplemented($complimented, $item, $member);
        }

        return $complimented;
    }

    /**
     * ComplimentedItems::getSupplemented()
     *
     * Retrieve additional complimented items for a product
     *
     * @access public
     * @static
     * @param array $complimented List of found complimented items
     * @param Item $item current item
     * @param mixed $member Member object or null if members disabled
     * @return array $complimented list of additional complimented items
     */
    public static function getSupplemented($complimented, Item $item, $member)
    {
        // member specific supplementation
        if (COMPLIMENTED_MEMBER_SPECIFIC === true) {
            if (! empty($member) && $member->id > 0) {
                $complimented = self::getMemberSpecific($complimented, $item, $member->id);
            }
        }

        if (count($complimented) < COMPLIMENTED_MAX) {
            $items_needed = COMPLIMENTED_MAX - count($complimented);
            $query = DB::table('items')
                ->where('deleted', 0)
                ->where('hidden', 0)
                ->where('items.id', '!=', $item->id)
                ->where('stock', '>', 0)
                ->orderByRaw('RAND()')
                ->select('items.id');

            if (COMPLIMENTED_COLOR_SPECIFIC === true) {
                $query->where('colour', $item->colour);
            }

            // If item has brand, find similar brands
            if ($item->brand > 0) {
                $results = $query
                    ->take($items_needed)
                    ->whereNotIn('items.id', $complimented)
                    ->leftJoin('items_brands', function ($join) use ($item): void {
                        $join->on('items_brands.item_id', '=', 'items.id')
                            ->where('brand_id', '=', $item->brand);
                    })
                    ->where('items_brands.brand_id', $item->brand) // filter items by brand
                    ->pluck('id');
                // Compatibility between L5.2 and L5.4
                if (! is_array($results)) {
                    $results = $results->toArray();
                }
                $items_needed -= count($results); // subtract recieved items from items needed var
                $complimented = array_merge($complimented, $results);
            }

            // Retrieve items from the same categories
            $items_needed = COMPLIMENTED_MAX - count($complimented);
            if (! empty($item->categories) && $items_needed > 0) {
                $results = $query
                    ->take($items_needed)
                    ->whereNotIn('items.id', $complimented)
                    ->leftJoin('items_categories', function ($join) use ($item): void {
                        $join->on('items_categories.item_id', '=', 'items.id')
                            ->whereIn('cat_id', array_keys($item->categories));
                    })
                    ->pluck('id');
                // Compatibility between L5.2 and L5.4
                if (! is_array($results)) {
                    $results = $results->toArray();
                }
                $complimented = array_merge($complimented, $results);
            }
        }

        return $complimented;
    }

    /**
     * Get member specific complimented items
     * based on the member's previous purchase
     * history.
     *
     * @access public
     * @static
     * @param array $complimented list of found complimented items
     * @param Item $item current item
     * @param int $member_id ID of member for whom to find complimented items
     * @return array $complimented list of member specific complimented items
     */
    public static function getMemberSpecific($complimented, Item $item, $member_id)
    {
        // return if no member id is set
        if (empty($member_id) || ! is_numeric($member_id)) {
            return $complimented;
        }

        if (count($complimented) >= COMPLIMENTED_MAX) {
            return $complimented;
        }

        $fav_brand = MemberItemPreference::query()
            ->where('member_id', $member_id)
            ->select([
                'brand_id',
                DB::raw('COUNT(*) AS `count`'),
            ])
            ->groupBy('brand_id')
            ->orderBy('count', 'DESC')
            ->first();

        if ($fav_brand) {
            // set the number of brand items to supplement
            $num_brand_items_needed = COMPLIMENTED_MAX - count($complimented);
            if ($num_brand_items_needed > COMPLIMENTED_MEMBER_SPECIFIC_BRANDS_NUM) {
                $num_brand_items_needed = COMPLIMENTED_MEMBER_SPECIFIC_BRANDS_NUM;
            }

            if ($num_brand_items_needed > 0) {
                $query = DB::table('items')
                    ->leftJoin('items_brands', function ($join) use ($fav_brand): void {
                        $join->on('items_brands.item_id', '=', 'items.id')
                            ->where('brand_id', '=', $fav_brand->brand_id);
                    })
                    ->where('deleted', 0)
                    ->where('items.id', '!=', $item->id)
                    ->where('stock', '>', 0)
                    ->where('hidden', 0)
                    ->whereNotIn('items.id', $complimented)
                    ->orderByRaw('RAND()')
                    ->select('items.id');

                $results = $query->take($num_brand_items_needed)->pluck('id');
                $complimented = array_merge($complimented, $results);
            }
        }

        $fav_cat = MemberItemPreference::query()
            ->where('member_id', $member_id)
            ->select([
                'cat_id',
                DB::raw('COUNT(*) AS `count`'),
            ])
            ->groupBy('cat_id')
            ->orderBy('count', 'DESC')
            ->first();

        if ($fav_cat) {
            // set the number of brand items to supplement
            $num_category_items_needed = COMPLIMENTED_MAX - count($complimented);
            if ($num_category_items_needed > COMPLIMENTED_MEMBER_SPECIFIC_CAT_NUM) {
                $num_category_items_needed = COMPLIMENTED_MEMBER_SPECIFIC_CAT_NUM;
            }

            if ($num_category_items_needed > 0) {
                $query = DB::table('items')
                    ->leftJoin('items_categories', function ($join) use ($fav_cat): void {
                        $join->on('items_categories.item_id', '=', 'items.id')
                            ->where('cat_id', '=', $fav_cat->cat_id);
                    })
                    ->where('deleted', 0)
                    ->where('items.id', '!=', $item->id)
                    ->where('stock', '>', 0)
                    ->where('hidden', 0)
                    ->whereNotIn('items.id', $complimented)
                    ->orderByRaw('RAND()')
                    ->select('items.id');

                $results = $query->take($num_category_items_needed)->pluck('id');
                $complimented = array_merge($complimented, $results);
            }
        }

        return $complimented;
    }
}
