<?php


namespace Mtc\Plugins\BasketBuilder\Classes;

use App\Events\OrderCreatedEvent;
use Carbon\Carbon;
use DeliveryMethod;
use Exception;
use Illuminate\Support\Facades\Event;
use Mtc\Core\Admin\Menu;
use Mtc\Modules\Members\Models\Member;
use Mtc\Shop\Basket;
use Mtc\Shop\Item;
use Mtc\Shop\Order;

/**
 * Basket Builder Class
 *
 * Class BasketBuilder
 * @package Mtc\Plugins\Wisebee\Classes
 */
class BasketBuilder
{
    const MENU_BASKET_BUILDER = 'Basket Builder';

    public $memberId;
    public $parentId;
    public $items;
    public $deliveryMethodId;
    public $shippingAddress;

    /**
     * Initializes the plugin
     *
     * @return void
     */
    public static function init(): void
    {
        self::injectAdminMenuItems();
    }

    /**
     * Finds items based on query
     *
     * @param string $searchQuery
     * @return array
     */
    public function findItems(string $searchQuery): array
    {
        if (empty($searchQuery)) {
            return [];
        }

        return Item::searchItems($searchQuery)
            ->toArray();
    }

    /**
     * @return Order
     * @throws Exception
     */
    public function createBasket(): Order
    {
        $member = Member::query()
            ->with('addressBilling')
            ->find($this->memberId);

        if (empty($member)) {
            throw new Exception('Member not found!');
        }

        if (empty($this->items)) {
            throw new Exception('No basket items provided!');
        }

        $member->save();

        /** @var Basket $basket */
        $basket = Basket::query()
            ->create([
                'date' => Carbon::now(),
                'delivery' => $this->deliveryMethodId,
                'member' => $this->memberId,
                'billing_address_used' => 0,
            ]);

        $memberAddress = $member->addressBilling->toArray();
        $memberAddress['gender'] = $member->gender;

        $basket->billingAddress()
            ->create($memberAddress);

        $this->shippingAddress['gender'] = $member->gender;
        $this->shippingAddress['type'] = 'shipping';
        $this->shippingAddress['country'] = $this->shippingAddress['country'] ?: 'GB';

        $basket->shippingAddress()
            ->create($this->shippingAddress);

        $basket->info()
            ->create([
                'email' => $member->email,
                'contact_no' => $member->contact_no,
                'dob_date' => $member->dob_date,
                'dob_month' => $member->dob_month,
                'dob_year' => $member->dob_year,
            ]);

        foreach ($this->items as $item) {
            $plu = $item['PLU'] ?? ($item['epos_code'] ?? null);
            if (!empty($item['customItem'])) {
                $this->createCustomItem($item);
            }
            $basket->items()
                ->create([
                    'item_id' => $item['id'],
                    'size' => $item['size'],
                    'quantity' => $item['quantity'],
                    'PLU' => $plu ?? '',
                    'sizeid' => $item['size_id'] ?: 0,
                ]);
        }

        $basket->load('items');
        $basket->load('info');
        $basket->load('address');

        $member->basket_id = $basket->id;
        $member->save();

        return $this->createOrder($basket);
    }

    /**
     * Creates a new product
     *
     * @param array $itemData
     */
    protected function createCustomItem(array &$itemData)
    {
        $item = Item::query()
            ->create([
                'name' => $itemData['name'],
                'price' => $itemData['size_price'],
                'price_exvat' => $itemData['size_price'],
                'hidden' => 1,
                'product_type' => 'prescription',
            ]);
        $itemData['id'] = $item->id;

        $size = Item\Size::query()
            ->create([
                'item_id' => $item->id,
                'size' => $itemData['size'],
                'quantity' => $itemData['qty'],
                'pack_size' => $itemData['qty'],
                'label_count' => 1,
                'strength' => $itemData['strength'],
                'price' => $itemData['size_price'],
                'price_exvat' => $itemData['size_price'],
                'stock' => $itemData['quantity'],
            ]);
        $itemData['size_id'] = $size->id;
    }

    /**
     * Creates order from basket
     *
     * @param Basket $basket
     * @return Order
     * @throws Exception
     */
    protected function createOrder(Basket $basket): Order
    {
        $deliveryMethod = DeliveryMethod::query()
            ->find($basket->delivery);

        if (empty($deliveryMethod)) {
            throw new Exception('Delivery method not found!');
        }

        /** @var Order $order */
        $order = Order::query()->create([
            'parent_id' => $this->parentId,
            'basket_id' => $basket->id,
            'failed' => 0,
            'delivery_name' => $deliveryMethod->name,
            'delivery_method_id' => $deliveryMethod->id,
            'delivery_cost' => $deliveryMethod->cost,
            'cost_total' => 0,
            'cost_total_exvat' => 0,
            'member' => $basket->member,
            'admin_id' => (int)$_SESSION['adminId'],
            'exported' => null,
            'coupon' => !empty($basket->coupon) ? '1' : '0',
            'coupon_code' => $basket->coupon ?? '',
            'source' => SITE,
        ]);

        $total = $deliveryMethod->cost;
        $totalExVat = $deliveryMethod->cost;

        foreach ($basket->items as $basketItem) {
            $size = null;
            $item = Item::query()
                ->find($basketItem->item_id);
            if (!empty($basketItem->sizeid)) {
                $size = Item\Size::query()
                    ->find($basketItem->sizeid);
                $price = $size->price;
                $priceExVat = $size->price_exvat;
            } else {
                $price = $item->price;
                $priceExVat = $item->price_exvat;
            }

            $total += $price * $basketItem->quantity;
            $totalExVat += $priceExVat * $basketItem->quantity;

            $orderItemData = $basketItem->toArray();
            $orderItemData['item_name'] = $item->name;
            $orderItemData['size'] = $size ? $size->size : 'One Size';
            $orderItemData['approved'] = '1';
            $orderItemData['item_price'] = $price;
            $orderItemData['price_paid'] = $price;
            $orderItemData['item_price_exvat'] = $priceExVat;
            $orderItemData['price_paid_exvat'] = $priceExVat;

            $order->items()
                ->create($orderItemData);
        }

        $order->cost_total = $total;
        $order->cost_total_exvat = $totalExVat;
        $order->basket_builder = 1;

        $order->save();

        // Add information about order->info based on basket->info
        $order->info()
            ->create($basket->info->toArray());

        foreach ($basket->address as $address) {
            $order->address()->create($address->toArray());
        }

        $basketLegacy = new \Basket($basket->id);
        $basketLegacy->Go_Basket();

        Event::dispatch(OrderCreatedEvent::class, new OrderCreatedEvent($order, $basketLegacy));

        return $order;
    }

    /**
     * Sends a payment request to member
     *
     * @param $orderId
     * @throws Exception
     */
    public function sendToMember($orderId)
    {
        global $twig;

        $order = Order::query()
            ->with('info')
            ->with('billingAddress')
            ->find($orderId);

        if (empty($order)) {
            throw new Exception('Order not found!');
        }

        $options = [
            'order' => $order,
            'paymentUrl' => SITE_URL . '/shop/checkout/basket_overview.php?order_id=' . $orderId,
        ];

        ob_start();

        echo $twig->render('emails/paymentEmail.twig', $options);

        $mail = ob_get_clean();
        email($order->info->email, "Your " . config('app.name') . " Order Payment", $mail);
    }

    /**
     * Injects menu items in admin menu
     *
     * @return void
     */
    protected static function injectAdminMenuItems(): void
    {
        if (Menu::query()->where('title', self::MENU_BASKET_BUILDER)->exists()) {
            return;
        }

        $menuBB = Menu::query()
            ->create([
                'sub_id' => '0',
                'title' => self::MENU_BASKET_BUILDER,
                'icon' => '',
                'order' => '2',
            ]);

        $subItems = [
            [
                'sub_id' => $menuBB->id,
                'title' => 'View Orders',
                'path' => '/plugins/BasketBuilder/admin/index.php',
                'activePath' => '/plugins/BasketBuilder/admin/index',
                'icon' => 'fa fa-list-ol',
                'order' => '1',
            ],
            [
                'sub_id' => $menuBB->id,
                'title' => 'Create a Basket',
                'path' => '/plugins/BasketBuilder/admin/add.php',
                'activePath' => '/plugins/BasketBuilder/admin/add',
                'icon' => 'fa fa-shopping-cart',
                'order' => '2',
            ],
        ];

        foreach ($subItems as $subItem) {
            Menu::query()
                ->create($subItem);
        }

    }

    /**
     * Whether the basket was created via basket builder
     *
     * @param $basketID
     * @return bool
     */
    public static function isBuilderBasket($basketID): bool
    {
        if (!$order = Order::query()->where('basket_id', $basketID)->first()) {
            return false;
        }
        return !empty($order->basket_builder);
    }
}