<?php

use App\DoctorApprovalNotifications;
use Carbon\Carbon;
use Illuminate\Database\Capsule\Manager as DB;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Collection;
use Mtc\Core\Admin\User;
use Mtc\Core\Models\Country;
use Mtc\Core\Models\CountryState;
use Mtc\Modules\Members\Models\Member;
use Mtc\Plugins\Clinic\Src\Logger;
use Mtc\Plugins\Refunds\Classes\Refund;
use Mtc\Plugins\Refunds\Classes\RefundItem;
use Mtc\Shop\Order\Protx;
use Mtc\Shop\Coupon;
use Mtc\Shop\Coupon\Code as CouponCode;
use Mtc\Shop\Events\OrderGetItemsEvent;
use Mtc\Shop\Events\OrderNoteCreatedEvent;
use Mtc\Shop\Events\OrderPaidEvent;
use Mtc\Shop\Item as ShopItem;
use Mtc\Shop\Order\Address;
use Mtc\Shop\Order\Contact;
use Mtc\Shop\Order\Coupon as OrderCoupon;
use Mtc\Shop\Order\Discount;
use Mtc\Shop\Order\Info;
use Mtc\Shop\Order\Item as OrderItem;
use Mtc\Shop\Order\Note;
use Mtc\Shop\OrderStatusEmailText;
use Mtc\Shop\OrderStatusLog;
use MtcPharmacy\Multisite\Classes\MultisiteSite;
use Twig\Environment;

/**
 * Order
 *
 * @package mtc ecommerce
 * @author mtc.
 * @copyright 2013 mtc. http://www.mtcmedia.co.uk/
 * @version 2013
 * @access public
 */
class Order
{
    const PAYMENT_TYPE_SAGEPAY_REGULAR = 'SagePay_reg';
    const PAYMENT_TYPE_SAGEPAY_SERVER  = 'SagePay_server';
    const PAYMENT_TYPE_PAYPAL          = 'PayPal';
    const PAYMENT_TYPE_WORLDPAY        = 'WordPay';
    const PAYMENT_TYPE_BARCLAYCARD     = 'Barclaycard';
    const PAYMENT_TYPE_BANK_OF_IRELAND = 'Bank of Ireland';

    const LABEL_INVOICE_PATH = '/uploads/files/merged_label_invoices';

    /**
     * Check if two amounts match approximately
     * @author Aleksey Lavrinenko
     *
     * @param $amount_this
     * @param $amount_matching
     * @return bool
     */
    public static function amountsMatch($amount_this, $amount_matching)
    {
        return abs($amount_this - $amount_matching) < 0.02;
    }

    /**
     * @var int null
     */
    private $id;

    /**
     * @var int null
     */
    private $basket_id;

    /**
     * @var array
     */
    private $items;

    /**
     * @var Collection|null Preloaded order items grouped for this order.
     */
    private $preloadedOrderItems;

    /**
     * @var array|null Preloaded addresses keyed by type.
     */
    private $preloadedAddresses;

    /**
     * @var array|null Preloaded order info.
     */
    private $preloadedInfo;

    /**
     * @var \Mtc\Shop\Order|null Cached eloquent model instance.
     */
    private $eloquentModel;

    /**
     * @var array Cached refunded order item ids for quick lookup.
     */
    private $refundedOrderItemIds = [];

    /**
     * @var \Mtc\Core\Admin\User|null Preloaded admin user who placed the order.
     */
    private $adminUser;

    /**
     * @var Member|null Preloaded member model for this order.
     */
    private $memberModel;

    /**
     * Flag to skip expensive data loading when displaying large grids.
     *
     * @var bool
     */
    private $lightweightMode = false;

    /**
     * @var array|null Preloaded payment data when lightweight mode is active.
     */
    private $preloadedPaymentData;

    /**
     * @var double
     */
    private $cost_total;

    /**
     * @var double
     */
    private $cost_subtotal;

    /**
     * @var double
     */
    private $cost_total_exvat;

    /**
     * @var double
     */
    private $cost_subtotal_exvat;

    /**
     * @var object
     */
    private $delivery;

    /**
     * @var double
     */
    private $cost_vat;

    /**
     * @var double
     */
    private $vat_deductable_amount;

    /**
     * @var array
     *
     */
    private $coupon;

    /**
     * @var array
     *
     */
    public $info;

    /**
     * @var array
     */
    public $address;

    /**
     * @var int
     */
    public $status;

    /**
     * @var
     */
    public $failed;


    public $exported;

    /**
     * @var int
     */
    public $member;
    /**
     * @var int
     */
    public $admin_id;

    /**
     * @var string
     */
    public $ga_ga;

    /**
     * @var string
     */
    public $ref;

    /**
     * @var string
     */
    public $keywords;

    /**
     * @var string
     *
     */
    private $order_date;

    /**
     * @var bool
     */
    public $no_vat; //This will be changed

    /**
     * @var null|int
     */
    public $paid;

    public $coupon_code;
    public $coupon_freedelivery;
    public $coupon_amountof;
    public $coupon_percentoff;
    public $coupon_deduct;

    public $payment;
    public $payment_paypal;
    public $payment_evopay;
    public $payment_gateway;
    public $payment_icon;
    public $payment_google;
    public $payment_worldpay;
    public $payment_stripe;
    public $payment_trade;
    public $card_type;
    public $order_ref;
    public $doctor_note;
    public $pharmacist_note;
    public $patient_notes;
    public $rm_error;
    public $rm_failed;

    /**
     * @var array
     */
    public $payment_barclaycard;

    /**
     * @var array
     */
    public $payment_bank_of_ireland;
    public $payment_realex;

    public $type;
    public $transaction_number;
    public $amount_paid;
    public $trustpilot_sent;
    public $flag;
    public $message_id;
    public $id_check_confirmed;
    public $ordered_within_four_weeks;


    /* Discounts */

    public $discounts;

    public $tracking_no;
    public $shipping_label;

    public $refundAmount = 0;


    /**
     * Order constructor.
     * @param null $id
     * @throws Exception
     */
    public function __construct($id = null)
    {
        /* Initialize */
        $this->no_vat = false;

        $this->info = array();
        $this->address = array();
        $this->discounts=array();

        /* Delivery */
        $delivery = array('name', 'cost', 'id');
        $this->delivery = (object)$delivery;

        if (empty($id)) {
            return;
        }

        $this->build(\Mtc\Shop\Order::query()->find($id)->toArray());
    }

    /**
     * @param array $data
     * @return void
     */
    public function build(array $data): void
    {

        $this->id = $data['id'];
        $this->basket_id = $data['basket_id'];
        $this->status = $data['status'];
        $this->member = $data['member'];
        $this->admin_id = $data['admin_id'];
        $this->type = $data['type'];
        $this->coupon = $data['coupon'];

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

        /* Payment */
        $this->cost_total = $data['cost_total'];
        $this->cost_total_exvat = $data['cost_total_exvat'];

        /* Delivery */
        $this->delivery->name = $data['delivery_name'];
        $this->delivery->cost = $data['delivery_cost'];
        $this->delivery->id = $data['delivery_method_id'];
        $this->delivery->instructions = $data['delivery_instructions'];

        $this->exported = $data['exported'];
        $this->failed = $data['failed'];

        /* SEO */
        $this->ga_ga   = $data['ga_ga'];
        $this->ref = $data['ref'];
        $this->order_ref = $data['order_ref'];
        $this->keywords = $data['keywords'];

        $this->order_date = $data['date'];

        $this->trustpilot_sent = $data['trustpilot_sent'];
        $this->flag = $data['flag'];
        $this->message_id = $data['message_id'];
        $this->tracking_no = $data['tracking_no'];
        $this->shipping_label = $data['shipping_label'];
        $this->id_check_confirmed = $data['id_check_confirmed'];
        $this->doctor_note = $data['doctor_note'];
        $this->pharmacist_note = $data['pharmacist_note'];
        $this->patient_notes = $data['patient_notes'];
        $this->rm_error = $data['rm_error'];
        $this->rm_failed = $data['rm_failed'];

        $this->initializeOrderInfo();

        $this->initializeOrderAddress();

        if (!$this->lightweightMode) {
            $this->initializePayment();
            $this->getDiscounts();
            $this->setRefundAmount();

            if ($data['coupon'] == 1)
            {
                $this->initializeOrderCoupon();
            }
        } else {
            $this->applyPreloadedPaymentData();
        }
    }

    private function applyPreloadedPaymentData(): void
    {
        if ($this->preloadedPaymentData === null) {
            return;
        }

        $type = $this->preloadedPaymentData['type'] ?? null;
        $data = $this->preloadedPaymentData['data'] ?? [];

        switch ($type) {
            case 'protx':
                $this->payment = $data;
                $this->amount_paid = $data['Amount'] ?? null;
                $this->order_ref = $data['VendorTxCode'] ?? $this->order_ref;
                $this->payment_gateway = "SagePay";
                if (!empty($data['3DSecureStatus']) && strpos($data['3DSecureStatus'], 'PAYPAL') !== false) {
                    $this->card_type = "PAYPAL";
                }
                $this->payment_icon = '/shop/admin/images/sp.png';
                break;
            case 'evopay':
                $this->payment_evopay = $data;
                $this->amount_paid = $data['amount_paid'] ?? null;
                $this->order_ref = $data['tnx_id'] ?? $this->order_ref;
                $this->payment_gateway = "EvoPay";
                $this->payment_icon = '/shop/admin/images/evopay.png';
                break;
            case 'paypal':
                $this->payment_paypal = $data;
                $this->amount_paid = $data['mc_gross'] ?? null;
                $this->order_ref = $data['txn_id'] ?? $this->order_ref;
                $this->payment_gateway = "PayPal";
                $this->payment_icon = '/shop/admin/images/pp.png';
                break;
            case 'worldpay':
                $this->payment_worldpay = $data;
                $this->amount_paid = $data['amount'] ?? null;
                $this->order_ref = $data['order_ref'] ?? $this->order_ref;
                $this->payment_gateway = "Worldpay";
                $this->payment_icon = '/shop/admin/images/worldpay.gif';
                break;
            case 'stripe':
                $this->payment_stripe = $data;
                $this->amount_paid = $data['amount'] ?? null;
                $this->order_ref = $data['order_ref'] ?? $this->order_ref;
                $this->payment_gateway = "Stripe";
                $this->payment_icon = '/shop/admin/images/stripe_logo.png';
                break;
            case 'realex':
                $this->payment_realex = $data;
                $this->amount_paid = $data['amount_paid'] ?? null;
                $this->order_ref = $data['tnx_id'] ?? $this->order_ref;
                $this->payment_gateway = "Realex";
                $this->payment_icon = '/shop/admin/images/realex.png';
                break;
            default:
                if (!empty($data['payment_icon'])) {
                    $this->payment_icon = $data['payment_icon'];
                }
                break;
        }
    }

    /**
     * Preload raw order item data.
     */
    public function setOrderItemsCollection(?Collection $orderItems): void
    {
        $this->preloadedOrderItems = $orderItems;
    }

    /**
     * Preload address data keyed by type.
     */
    public function setPreloadedAddresses(?array $addresses): void
    {
        $this->preloadedAddresses = $addresses;
    }

    /**
     * Preload order info data.
     */
    public function setPreloadedInfo(?array $info): void
    {
        $this->preloadedInfo = $info;
    }

    /**
     * Inject an eloquent model instance to avoid additional queries.
     */
    public function setEloquentModel(?\Mtc\Shop\Order $order): void
    {
        $this->eloquentModel = $order;
    }

    /**
     * Cache refunded order item ids.
     */
    public function setRefundedItemIds(array $orderItemIds): void
    {
        $this->refundedOrderItemIds = array_fill_keys($orderItemIds, true);
    }

    /**
     * Enable or disable lightweight mode.
     */
    public function enableLightweightMode(bool $enabled = true): void
    {
        $this->lightweightMode = $enabled;
    }

    public function setPreloadedPaymentData(?array $paymentData): void
    {
        $this->preloadedPaymentData = $paymentData;
    }

    /**
     * Check if a specific order item has been refunded.
     */
    public function isItemRefunded(int $orderItemId): bool
    {
        return isset($this->refundedOrderItemIds[$orderItemId]);
    }

    /**
     * Attach an admin user instance for this order.
     */
    public function setAdminUser(?\Mtc\Core\Admin\User $adminUser): void
    {
        $this->adminUser = $adminUser;
    }

    /**
     * Retrieve the cached admin user (if available).
     */
    public function getAdminUser(): ?\Mtc\Core\Admin\User
    {
        if ($this->adminUser instanceof \Mtc\Core\Admin\User) {
            return $this->adminUser;
        }

        if ($this->admin_id) {
            $this->adminUser = User::query()->find($this->admin_id);
        }
        return $this->adminUser;
    }

    /**
     * Attach a member model instance for this order.
     */
    public function setMemberModel(?Member $member): void
    {
        $this->memberModel = $member;
    }

    /**
     * Retrieve the cached member model if available.
     */
    public function getMemberModel(): ?Member
    {
        if ($this->memberModel instanceof Member) {
            return $this->memberModel;
        }

        $eloquent = $this->eloquent();
        if ($eloquent && $eloquent->relationLoaded('customer')) {
            $this->memberModel = $eloquent->customer;
        }

        return $this->memberModel;
    }

    /**
     * @return int|null
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return int|null
     */
    public function getBasketId()
    {
        return $this->basket_id;
    }

    /**
     * @return string|null
     */
    public function getCustomerName()
    {
        return $this->address['billing']['firstname'] . ' ' . $this->address['billing']['lastname'];
    }

    /**
     * @return object
     * This function returns object with public members name and cost
     */
    public function getDelivery()
    {
        return $this->delivery;
    }

    /**
     * @param string $format
     * @param string $default
     * @return bool|null|string
     * Method returns formatted string of order date
     */
    public function getOrderDate($format = 'd/m/Y H:i:s', $default = '')
    {
        return convert_date($this->order_date, $format, $default, 'Y-m-d H::s');
    }

    /**
     * Initialize order information
     * @return void
     */
    private function initializeOrderInfo(): void
    {
        if ($this->preloadedInfo !== null) {
            $this->info = $this->preloadedInfo;
            return;
        }

        $this->info = Info::query()
            ->where('order_id', $this->id)
            ->firstOrNew()
            ->toArray();
    }

    /**
     * Initialize array with shipping and billing customer details
     * Determine 'No Vat' order according to location.
     */
    private function initializeOrderAddress()
    {
        if ($this->preloadedAddresses !== null) {
            $this->address = $this->preloadedAddresses;
        } else {
            $this->address = Address::query()
                ->where('order_id', $this->id)
                ->get()
                ->keyBy('type')
                ->toArray();
        }

        /* If Order is from outside EU and DISCOUNT_CAT is enabled,  we don't charge VAT */
        $shippingAddress = $this->address['shipping'] ?? [];
        $shippingCountry = $shippingAddress['country'] ?? '';
        $shippingPostcode = $shippingAddress['postcode'] ?? '';

        if (DISCOUNT_VAT && (!(Country::isEu($shippingCountry) || $shippingCountry == 'GB' || $shippingCountry == '')) ||
            in_array(strtoupper(substr($shippingPostcode, 0, 2)), array('JE', 'GY'))
        )
        {
            $this->no_vat = true;
        }
    }

    /**
     * Updates customer address
     *
     * @return void
     */
    public function updateCustomerOrderAddress(): void
    {
        foreach ($this->address as $data) {
            if (!$address = Address::query()->find($data['id'])) {
                continue;
            }
            $address->fill($data);
            $address->save();
        }
    }

    /**
     *  Updates customers basic details
     * @return void
     */
    public function updateCustomerOrderInfo(): void
    {
        if (!$info = Info::query()->find($this->info['id'])) {
            return;
        }
        $info->fill($this->info);
        $info->save();
    }

    /**
     *  Initialize payment data
     *  Sagepay, Paypal, Google, Worldpay
     */
    private function initializePayment()
    {

        /* Sagepay */

        if ($payment = Protx::query()->where('order_id', $this->id)->first()) {
            $this->payment = $payment->toArray();
            $this->amount_paid = $this->payment['Amount'];
            $this->order_ref = $this->payment['VendorTxCode'];
            $this->payment_gateway = "SagePay";
            if (substr_count($this->payment['3DSecureStatus'], "PAYPAL")) {
                $this->card_type = "PAYPAL";
            }
            $this->payment_icon = '/shop/admin/images/sp.png';
            return;
        }

        /* EvoPay */

        if ($payment = DB::table('order_evopay')->where('order_id', $this->id)->first()) {
            $this->payment_evopay = $payment->toArray();
            $this->amount_paid = $this->payment_evopay['amount_paid'];
            $this->order_ref = $this->payment_evopay['tnx_id'];
            $this->payment_gateway = "EvoPay";
            $this->payment_icon = '/shop/admin/images/evopay.png';
            return;
        }

        /* Paypal */

        if ($payment = DB::table('order_paypal')->where('order_id', $this->id)->first()) {
            $this->payment_paypal = $payment->toArray();
            $this->amount_paid = $this->payment_paypal['mc_gross'];
            $this->order_ref = $this->payment_paypal['txn_id'];
            $this->payment_gateway = "PayPal";
            $this->payment_icon = '/shop/admin/images/pp.png';
            return;
        }

        /* Worldpay */

        if ($payment = DB::table('order_worldpay')->where('order_id', $this->id)->first()) {
            $this->payment_worldpay = $payment->toArray();
            $this->amount_paid = $this->payment_worldpay['amount'];
            $this->order_ref = $this->payment_worldpay['order_ref'];
            $this->payment_gateway = "Worldpay";
            $this->payment_icon = '/shop/admin/images/worldpay.gif';

            return;
        }

        /* Stripe */

        if ($payment = DB::table('order_stripe')->where('order_id', $this->id)->first()) {
            $this->payment_stripe= (array) $payment;
            $this->amount_paid = $this->payment_stripe['amount'];
            $this->order_ref = $this->payment_stripe['order_ref'];
            $this->payment_gateway = "Stripe";
            $this->payment_icon = '/shop/admin/images/stripe_logo.png';
            return;
        }

        // Realex

        if ($payment = DB::table('order_realex')->where('order_id', $this->id)->first()) {
            $this->payment_realex = $payment->toArray();
            $this->amount_paid = $this->payment_realex['amount_paid'];
            $this->order_ref = $this->payment_realex['tnx_id'];
            $this->payment_gateway = "Realex";
            $this->payment_icon = '/shop/admin/images/realex.png';

            return;
        }

        Event::dispatch(__CLASS__ . '/' . __FUNCTION__, $this);
    }

    /**
     * @param $values
     */
    public function saveProtX($values)
    {
        //if amount is bigger than 1 000 pounds it Sagepay will return amount as 1,000.00 which when converted to double gives 1
        $values['Amount'] = doubleval(str_replace(',', '', $values['Amount']));

        Protx::query()
            ->create([
                'order_id' => $this->id,
                'VendorTxCode' => $values['VendorTxCode'],
                'VPSTxId' => $values['VPSTxId'] ?? null,
                'TxAuthNo' => $values['TxAuthNo'] ?? null,
                'SecurityKey' => $values['SecurityKey'] ?? null,
                'AVSCV2' => $values['AVSCV2'] ?? null,
                'AddressResult' => $values['AddressResult'] ?? null,
                'PostCodeResult' => $values['PostCodeResult'] ?? null,
                'CV2Result' => $values['CV2Result'] ?? null,
                'GiftAid' => $values['GiftAid'] ?? null,
                '3DSecureStatus' => $values['3DSecureStatus'] ?? null,
                'CAVV' => $values['CAVV'] ?? null,
                'Amount' => $values['Amount'] ?? null,
                'type' => $values['TxType'] ?? null,
            ]);

        \Mtc\Shop\Order::query()
            ->where('id', $this->id)
            ->update([
                'order_ref' => $values['VendorTxCode'],
            ]);
    }

    /**
     * @param $values
     */
    public function savePaypal($values)
    {
        $values['order_id'] = $this->id;
        DB::table('order_paypal')
            ->insert($values);

        \Mtc\Shop\Order::query()
            ->where('id', $this->id)
            ->update([
                'order_ref' => $values['txn_id'],
            ]);
    }

    /**
     * @param $values
     */
    public function saveWorldpay($values)
    {
        $this->payment_worldpay = $values;

        $params = [
            'order_id' => $this->id,
            'order_ref' => config('site.tnx_prefix') . $this->id,
            'transId' => $values['orderCode'],
            'amount' => ($values['amount'] / 100)
        ];

        DB::table('order_worldpay')
            ->insert($params);

        \Mtc\Shop\Order::query()
            ->where('id', $this->id)
            ->update([
                'order_ref' => config('site.tnx_prefix') . $this->id,
            ]);

    }

    /**
     * @param $values
     */
    public function saveStripe($values)
    {
        $this->payment_stripe = $values;

        $values['order_id'] = $this->id;
        DB::table('order_stripe')
            ->insert($values);

        \Mtc\Shop\Order::query()
            ->where('id', $this->id)
            ->update([
                'order_ref' =>$values['order_ref'],
            ]);
    }

    public function saveRealex()
    {
        $order = $this->eloquent();
        $order->order_ref = config('site.tnx_prefix') . $this->id;
        $order->save();
    }


    /**
     * @return array
     */
    public function getItems()
    {
        if (!empty($this->items)) {
            return $this->items;
        }

        $this->items = [];

        $orderItems = $this->preloadedOrderItems;
        if ($orderItems === null) {
            $orderItems = OrderItem::query()
                ->with('shipmentItems')
                ->with('item.brands')
                ->where('order_id', $this->id)
                ->orderBy('order_items.id')
                ->get();
        }


        foreach ($orderItems as $orderItem)
        {
            $data = $orderItem->toArray();
            if ($data['sale_item'])
            {
                $data['original_line_total'] = $data['item_price'] * $data['quantity'];
                $data['line_total'] = $data['sale_item'] * $data['quantity'];
            }
            else
            {
                $data['line_total'] = $data['item_price'] * $data['quantity'];
            }

            // fire an event the Laravel way
            $additions = Event::dispatch(new OrderGetItemsEvent($this, $data));
            if (!empty($additions)) {
                $data['additional'] = [];
                foreach ($additions as $addition_array) {
                    $data['additional'] = array_merge($data['additional'], $addition_array);
                }
            }

            $data['main_category_name'] = '(no category)';
            $data['item_url'] = '/shop/admin/products/item.php?id=' . ($data['item_id'] ?? '');
            $shop_item = $orderItem->item;
            if ($shop_item) {
                $data['item_url'] = '/shop/admin/products/item.php?id=' . $shop_item->getKey();
                $main_category = $shop_item->getMainCategory();
                if ($main_category) {
                    $data['main_category_name'] = $main_category->name;
                }
            }

            //Add Item to the list
            $this->items[$data['id']] = $data;


            //check if item has been shipped

            if ($orderItem->shipmentItems->count() > 0)
            {
                $this->items[$data['id']]['shipped'] = true;
            }
        }
        return $this->items;
    }

    /**
     *
     */
    private function calculateTotals()
    {

        //Totals
        $this->cost_subtotal = 0;

        // Totals Ex Vat
        $this->cost_subtotal_exvat = 0;
        $this->cost_vat = 0;

        $this->vat_deductable_amount = 0;

        foreach ($this->getItems() as $id_item => $order_item) {

            $this->cost_subtotal += $order_item['line_total'];

            // Calculate deducable VAT if available
            if ($order_item['vat_deductable'] == 1) {
                $this->vat_deductable_amount += ($order_item['item_price'] - $order_item['item_price_exvat']) * $order_item['quantity'];
            }

            $this->cost_subtotal_exvat += $order_item['item_price_exvat'] * $order_item['quantity'];

            //Add vat from each item
            $this->cost_vat += ($order_item['price_paid'] - $order_item['price_paid_exvat']) * $order_item['quantity'];
        }

        // Add vat from delivery
        $this->cost_vat += $this->getDelivery()->cost - ($this->getDelivery()->cost / (1 + VAT_RATE / 100));

    }

    /**
     * @return float
     */
    public function getTotalCost()
    {

        if ($this->no_vat === false) {
            return $this->cost_total;
        } else {
            return $this->cost_total_exvat;
        }

    }

    /**
     * @return float|null
     */
    public function getSubtotalCost()
    {
        if ($this->cost_subtotal === null) {
            $this->calculateTotals();
        }

        if ($this->no_vat === false) {
            return $this->cost_subtotal;
        } else {
            return $this->cost_subtotal_exvat;
        }
    }

    /**
     * @return float
     */
    public function getVatCost()
    {
        $this->calculateTotals();
        return $this->cost_vat;
    }

    /**
     *  Updates order status as paid
     */
    public function markPaid($payment_type = null)
    {
        \Mtc\Shop\Order::query()
            ->where('id', $this->id)
            ->update([
                'paid' => 1,
                'date' => Carbon::now(),
            ]);

        $this->paid = 1;

        $this->deductStock();
        $this->deductCoupon();

        $this->updateStatus(\Mtc\Shop\Order::STATUS_PROCESSING, false);

        if (defined('MEMBERS_BASKET') && MEMBERS_BASKET && $this->member > 0) {
            Member::unsetMemberBasket($this->member);
        }

        $this->checkAssessmentForGPNotification();

        try {
            // used in some plugins like Facebook
            HooksAdapter::do_action(__CLASS__ . '/' . __FUNCTION__, $this);

            // fire an event the Laravel way
            Event::dispatch(new OrderPaidEvent($this, $payment_type));
        } catch (\Exception $exception) {
            email(DEV_EMAIL, config('app.name') . ' Order Paid Event error', $exception->getMessage() . '<br />' . $exception->getTraceAsString());
        }

        if (!empty($this->basket_id)) {
            \Mtc\Shop\Order::query()
                ->where('basket_id', $this->basket_id)
                ->where('id', '!=', $this->id)
                ->where('paid', 0)
                ->get()
                ->each(function ($duplicate) {
                    self::deleteById($duplicate->id);
                });
        }
    }

    /**
     *
     */
    public function deductCoupon()
    {
        if (Coupon::exists($this->coupon_code)) {
            Coupon::where('code',$this->coupon_code)->decrement('redemptions');
        } elseif (CouponCode::exists($this->coupon_code)) {
            $coupon_code = CouponCode::where('code',$this->coupon_code)->first();
            $coupon_code->order_id = $this->id;
            $coupon_code->save();
        }

        HooksAdapter::do_action(__CLASS__ . '/' . __FUNCTION__, $this);
    }

    /**
     * Initialize coupon data
     */
    private function initializeOrderCoupon()
    {
        $order_coupon = OrderCoupon::where('order_id', $this->id)->first();

        if(!empty($order_coupon)) {
            $this->coupon_code = $order_coupon->code;
            $this->coupon_freedelivery = $order_coupon->freedelivery;
            $this->coupon_amountof = $order_coupon->amountoff;
            $this->coupon_percentoff = $order_coupon->percentoff;
            $this->coupon_deduct = $order_coupon->deduct;
        }
    }


    /**
     * @return bool
     */
    public function hasShipments()
    {
        return \Mtc\Shop\Order\Shipment::query()
            ->where('order_id', $this->id)
            ->exists();
    }

    /**
     * @return bool
     */
    public function hasShipmentsWithLabels()
    {
        return \Mtc\Shop\Order\Shipment::query()
            ->where('order_id', $this->id)
            ->whereNotNull('label')
            ->exists();
    }

    /**
     * @param int $newstatus
     * @param bool $sendemails
     * @throws Exception
     */
    public function updateStatus(int $newstatus, bool $sendemails = true): void
    {
        if ($this->status === $newstatus) {
            return;
        }

        \Mtc\Shop\Order::query()
            ->where('id', $this->id)
            ->update([
                'status' => $newstatus,
            ]);

        $this->status = $newstatus;

        OrderStatusLog::query()
            ->create([
                'order_id' => $this->id,
                'status' => $newstatus,
            ]);

        if ($sendemails) {
            $this->sendStatusMail();
        }
        Event::dispatch(new \Mtc\Shop\Events\OrderStatusChangedEvent($this, $newstatus));

        Event::dispatch('Order@updateStatus', $this);
    }


    /**
     * @return void
     */
    public function deductStock(): void
    {
        foreach ($this->getItems() as $item)
        {
            // echo "deducting stock";

            $itemd = new Item();
            $itemd->Get_Item($item['item_id']);

            if ($item['size'] != '') {
                $itemd->Deduct_Size_Stock($item['quantity'], $item['item_id'], $item['size']);
            }
            $itemd->Deduct_Stock($item['quantity']);

            ShopItem::query()
                ->where('id', $item['item_id'])
                ->increment('num_sold', $item['quantity']);
        }
    }

    /**
     * @param $status
     * @return string
     * @throws Exception
     */
    public function getOrderStatusText($status): string
    {
        $orderStatusEmailText = OrderStatusEmailText::query()
            ->where('status_id', $status)
            ->first();

        if (empty($orderStatusEmailText)) {
            return '';
        }
        return $orderStatusEmailText->text;
    }

    /** */
    public function sendStatusMail()
    {
        global $orders_statuses; // instantiated in shop/includes/header.inc.php
        global $status_send_mail; // instantiated in shop/includes/header.inc.php
        global $countries; // instantiated in core/includes/header.inc.php
        global $twig; // instantiated in core/includes/header.inc.php
        global $order_status_text; // called via this class getOrderStatusText method
        global $email_content; // instantiated in shop/includes/header.inc.php

        if (in_array($this->status, array_keys((array)$status_send_mail))) {

            $order_status_text = $this->getOrderStatusText($this->status);

            $line_items = $this->getLineItems();

            $tracking_no = '';
            if ((int)$this->status === \Mtc\Shop\Order::STATUS_SHIPPED) {
                $shipment = $this->eloquent()
                    ->shipments()
                    ->where('tracking_no', '!=', '')
                    ->first();
                if (!empty($shipment)) {
                    $tracking_no = $shipment->tracking_no;
                }
            }

            $site_config = MultisiteSite::getSiteConfig($this->eloquent()->info->site);

            $options = [
                'order' => $this, // order details
                'order_total' => $this->getTotalCost(), // orders total cost
                'email_content' => $email_content, // email content array contains site information for display within the template
                'line_items' => $line_items, // Order items with additional up to date information pulled from the item class
                'order_subtotal' => $this->getSubtotalCost(),
                'vat_cost' => $this->getVatCost(), // Total VAT cost of the order
                'order_date' => $this->getOrderDate('Y-m-d H:i:s'), // order date sent in full... twig can re-render into any date format from here
                'delivery_cost' => $this->getDelivery()->cost, // Delivery Cost
                'delivery_name' => $this->getDelivery()->name, // Delivery Name
                'order_status_text' => $order_status_text, // Order status text as defined in admin under shop/order status text
                'countries' => $countries, // Country name
                'tracking_no' => $tracking_no, // Tracking number
                'state_list' => CountryState::getCountryStateList(), // State name
                'site_config' => $site_config,
            ];

            ob_start();

            echo $twig->render($status_send_mail[$this->status], $options);

            $mail = ob_get_clean();

            $subject = "Your " . $site_config['SITE_NAME'] . " Order " . $this->order_ref . ": " . $orders_statuses[$this->status];

            $to = $this->info['email'];
            //if in DEV_MODE only send mail to customer if its an mtc address,
            //else send it to dev
            if (DEV_MODE) {
                $mtc = '@mtcmedia.co.uk';

                if (strlen($this->info['email']) && substr_compare($this->info['email'], $mtc, -strlen($mtc), strlen($mtc)) !== 0) {
                    $to = DEV_EMAIL;
                }

                $subject .= ' (DEV Copy)';
            }

            email($to, $subject, $mail, ['queue' => true]);

            if (! DEV_MODE) {
                email(config('site.shop_email'), "(Copy) {$subject}", $mail, ['queue' => true]);
            }
            $trustpilotBccEmail = !empty($site_config['TRUSTPILOT_BCC_EMAIL']) ?
                $site_config['TRUSTPILOT_BCC_EMAIL'] :
                TRUSTPILOT_BCC_EMAIL;

            if (
                ! DEV_MODE
                && (int)$this->status === \Mtc\Shop\Order::STATUS_SHIPPED
                && isset($trustpilotBccEmail)
            ) {
                $this->sendTrustpilotInvite($trustpilotBccEmail);
            }
        }
    }


    private function sendTrustpilotInvite($bcc_email)
    {
        $email_subject = 'Order shipped -- ' . $this->getId();

        $product_ids = [];
        foreach ($this->items as $order_item) {
            $order_item_eloquent = OrderItem::find($order_item['id']);
            if ($order_item_eloquent) {
                $product_ids[] = $order_item_eloquent->item->getFirstTrustpilotSku();
            }
        }

        $email_content = '
            <script type="application/json+trustpilot">
                {
                    "recipientName": "' . $this->getCustomerName() . '",
                    "referenceId": "' . $this->getId() . '",
                    "recipientEmail": "' . $this->info['email'] . '",
                    "productSkus": ' . json_encode($product_ids) . ',
                }
            </script>
        ';

        $params = [
            'queue' => true,
            //'dev_copy' => true,
        ];
        email($bcc_email, $email_subject, $email_content, $params);
    }

    /**
     * Order::batchInvoices
     *
     * Generate a batch invoice PDF.
     * This script groups multiple orders in one pdf
     * Used when generating invoices from Manage Orders page
     *
     * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     * @access public
     * @static
     * @param array $order_ids list of order ids
     * @param array $basket_countries globally defined basket countries
     * @param Environment $twig Twig Handler
     * @return mixed
     */
    public static function batchInvoices($order_ids, $basket_countries, Environment $twig)
    {
        $all_orders = [];

        foreach ($order_ids as $id) {
            $all_orders[] = new Order($id);
        }

        $pdf = self::generatePDFTemplate('KG');
        $pdf->SetMargins(3,3,3,3);

        foreach ($all_orders as $order) {

            $options = [
                'order' => $order, // order details
                'line_items' =>  $order->getLineItems(),
                'cost_subtotal' => $order->getSubtotalCost(),
                'vat_cost' => $order->getVatCost(),
                'order_date' => $order->getOrderDate('Y-m-d H:i:s'),
                'delivery_cost' => $order->getDelivery()->cost,
                'delivery_name' => $order->getDelivery()->name,
                'basket_countries' => $basket_countries,
                'order_status_text' => $order->getOrderStatusText($order->status),
            ];
            $invoice_html = $twig->render('emails/shop/sales_invoice_standalone.twig', $options);

            $pdf->AddPage();
            $pdf->writeHTML($invoice_html, true, false, true, false, '');
        }

        $pdf->lastPage();
        return $pdf->Output('invoice_batch.pdf', 'I');
    }

    /**
     * Order::batchPrescriptions
     *
     * Generate a batch prescription PDF.
     * This script groups multiple orders in one pdf
     * Used when generating prescriptions from Manage Orders page
     *
     * @author Uldis Zvirbulis <uldis.zvirbulis@mtcmedia.co.uk>
     * @access public
     * @static
     * @param array $order_ids list of order ids
     * @param array $basket_countries globally defined basket countries
     * @param Environment $twig Twig Handler
     * @return mixed
     * @throws Exception
     */
    public static function batchPrescriptions($order_ids, $basket_countries, Environment $twig)
    {
        $all_orders = [];

        foreach ($order_ids as $id) {
            $all_orders[] = new Order($id);
        }

        $pdf = self::generatePDFTemplate();
        $pdf->SetMargins(15,15);

        $x = 0;

        foreach ($all_orders as $order) {
            foreach($order->getLineItems() as $item) {

                if ((int)$item['approved'] !== 1) {
                    continue;
                }
                // Find the doctors approval for this product
                $item['approval'] = ApprovalLog::query()
                    ->where('order_item_id', '=', $item['id'])
                    ->first();

                // If no approval we don't have a prescription to print
                if (!$item['approval']) {
                    continue;
                }

                $item['size'] = ShopItem\Size::query()->find($item['sizeid']);

                $prescription_html = self::getPrescriptionHTML($order, $item, $basket_countries, $twig);

                $pdf->AddPage();
                $pdf->writeHTML($prescription_html, true, false, true, false, '');
                $x++;
            }
        }

        if (!empty($x)) {
            $pdf->lastPage();
            return $pdf->Output('', 'I');
        }
        return false;
    }

    /**
     * Generate html for printing split-side labels
     * these labels have Royal mail label on one side and invoice on the other side
     * If order does not have RM label (DX delivery) only invoice is printed
     *
     * @param int[] $order_ids list of orders to process
     * @param $basket_countries
     * @param Environment $twig
     * @return string generated html for all labels to print
     */
    public static function batchSplitSideLabels($order_ids, $basket_countries, Environment $twig)
    {
        global $email_content;
        $output = '';
        $all_orders = [];

        foreach ($order_ids as $id) {
            $all_orders[] = new Order($id);
        }

        foreach ($all_orders as $order) {

            $options = [
                'order' => $order, // order details
                'email_content' => $email_content,
                'line_items' =>  $order->getLineItems(),
                'cost_subtotal' => $order->getSubtotalCost(),
                'vat_cost' => $order->getVatCost(),
                'order_date' => $order->getOrderDate('Y-m-d H:i:s'),
                'delivery_cost' => $order->getDelivery()->cost,
                'delivery_name' => $order->getDelivery()->name,
                'basket_countries' => $basket_countries,
                'order_status_text' => $order->getOrderStatusText($order->status),
                'site_config' => MultisiteSite::getSiteConfig($order->eloquent()->info->site),
            ];
            $invoice = $twig->render('emails/shop/sales_invoice.twig', $options);

            $output .= minify_html($twig->render('split_side_labels.twig', compact('invoice')));
        }

        return $twig->render('batch_process_print.twig', [
            'html' => $output
        ]);
    }

    /**
     * Order::getPharmacyLabelHTML
     *
     * Creates pharmacy label HTML for one order item.
     *
     * @author Davis Lasis <davis.lasis@mtcmedia.co.uk>
     * @access public
     * @static
     * @param Order $order
     * @param Item $item
     * @param Environment $twig Twig Handler
     * @return string HTML
     */
    public static function getPharmacyLabelHTML($order, $item, Environment $twig, $full_path = false, $page_break = false)
    {
        $image_source = THEME_URL.'/images/logo.png';
        if ($full_path) {
            $image_source = SITE_PATH.$image_source;
        }

        $options = [
            'order' => $order, // order details
            'item' => $item,
            'order_date' => $order->getOrderDate('Y-m-d H:i:s'),
            'image_source' => $image_source,
            'page_break' => $page_break
        ];


        return $twig->render('emails/shop/pharmacy_label.twig', $options);
    }

    /**
     * Order::getPrescriptionHTML
     *
     * Creates prescription HTML for one order.
     *
     * @author Uldis Zvirbulis <uldis.zvirbulis@mtcmedia.co.uk>
     * @access public
     * @static
     * @param Order $order
     * @param array $basket_countries globally defined basket countries
     * @param Environment $twig Twig Handler
     * @return string HTML
     * @throws Exception
     */
    public static function getPrescriptionHTML($order, $item, $basket_countries, Environment $twig)
    {
        $options = [
            'order' => $order, // order details
            'line_item' => $item,
            'cost_subtotal' => $order->getSubtotalCost(),
            'vat_cost' => $order->getVatCost(),
            'order_date' => $order->getOrderDate('Y-m-d H:i:s'),
            'delivery_cost' => $order->getDelivery()->cost,
            'delivery_name' => $order->getDelivery()->name,
            'basket_countries' => $basket_countries,
            'order_status_text' => $order->getOrderStatusText($order->status),
            'site_config' => MultisiteSite::getSiteConfig($order->eloquent()->info->site),
        ];

        return $twig->render('emails/shop/prescription.twig', $options);
    }

    /**
     * Order::createInvoice()
     *
     * Generates invoice for current order
     *
     * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     * @access public
     * @param array $basket_countries globally defined basket countries
     * @param Environment $twig
     * @return string returns invoice filename
     */
    public function createInvoice($basket_countries, Environment $twig)
    {
        $options = [
            'order' => $this, // order details
            'line_items' =>  $this->getLineItems(),
            'cost_subtotal' => $this->getSubtotalCost(),
            'vat_cost' => $this->getVatCost(),
            'order_date' => $this->getOrderDate('Y-m-d H:i:s'),
            'delivery_cost' => $this->getDelivery()->cost,
            'delivery_name' => $this->getDelivery()->name,
            'basket_countries' => $basket_countries,
            'order_status_text' => $this->getOrderStatusText($this->status),
        ];
        $invoice_html = $twig->render('emails/shop/sales_invoice_standalone.twig', $options);
        return $this->generateInvoice($invoice_html);
    }

    /**
     * Order::generateInvoice()
     *
     * generates PDF invoice file based on html provided
     *
     * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     * @access private
     * @param $invoice_html html for the invoice file
     * @return string pdf filename
     */
    private function generateInvoice($invoice_html)
    {
        $pdf = self::generatePDFTemplate();
        $pdf->SetTitle('Sales Invoice ' . $this->id);
        $pdf->SetSubject('Sales Invoice ' . $this->id);

        $pdf->AddPage();
        $pdf->writeHTML($invoice_html, true, false, true, false, '');
        $pdf->lastPage();

        $pdf_name = SITE_PATH . '/uploads/files/invoices/' . md5($this->id) . '.pdf';

        if (file_exists($pdf_name)) {
            rename($pdf_name, str_replace('.pdf', '_' . time() . '.pdf', $pdf_name));
        }

        $pdf->Output($pdf_name, 'F');

        return $pdf_name;
    }

    /**
     * Order::generatePDFTemplate()
     *
     * Creates empty PDF for invoice usage
     * Used to make PDF reusable for both batch and single order exports
     *
     * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     * @access private
     * @param $page_format
     * @return TCPDF $pdf Empty pdf file
     */
    private static function generatePDFTemplate($page_format = null)
    {
        if (empty($page_format)) {
            $page_format = 'A4';
        }
        $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, $page_format, true, 'UTF-8', false);

        $pdf->SetCreator(PDF_CREATOR);
        $pdf->SetAuthor(config('app.name'));
        $pdf->SetHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE.' 006', PDF_HEADER_STRING);
        $pdf->setPrintHeader(false);
        $pdf->setPrintFooter(false);
        $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
        $pdf->SetMargins(PDF_MARGIN_LEFT - 10, PDF_MARGIN_TOP - 20, PDF_MARGIN_RIGHT);
        $pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM - 20);
        $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);

        if (@file_exists(dirname(__FILE__).'/lang/eng.php')) {
            require_once(dirname(__FILE__).'/lang/eng.php');
            $pdf->setLanguageArray($l);
        }

        $pdf->SetFont('dejavusans', '', 10);

        return $pdf;

    }

    /**
     *
     */
    public function getLineItems() {

        $line_items = $this->getItems();

        foreach ($line_items as $order_item_id => $i) {
            $line_item = new Item();
            $line_item->Get_Item($i['item_id']);

            $line_items[$order_item_id]['item_image'] = $line_item->image_basket;
            $line_items[$order_item_id]['epos_code'] = $line_item->epos_code;

            $order_item = OrderItem::find($order_item_id);
            $line_items[$order_item_id]['dosage'] = $order_item->getDosageInfo();

            $patient_details = $order_item->getCustomersForLabels();
            if (count($patient_details) > 0) {
                $patient_details = $patient_details[0];
            }

            $patient_details['address'] = $this->address['billing'];
            $line_items[$order_item_id]['patient_details'] = $patient_details;
            $line_items[$order_item_id]['refund_amount'] = RefundItem::query()
                ->where('order_item_id', $order_item_id)
                ->sum('amount_refunded');
        }

        return $line_items;
    }

    /**
     * public Order->sendOrderConfirmation().
     *
     * Sends out order confirmation email
     */
    public function sendOrderConfirmation()
    {
        global $countries, $twig;
        global $email_content; // instantiated in shop/includes/header.inc.php

        $order_status_text = $this->getOrderStatusText(0);

        $line_items = $this->getLineItems();

        $this->getOrderRef();
        $this->initializePayment();

        $site_config = MultisiteSite::getSiteConfig($this->eloquent()->info->site);

        $options = [
            'order' => $this, // order details
            'order_total' => $this->getTotalCost(), // orders total cost
            'email_content' => $email_content, // email content array contains site information for display within the template
            'line_items' => $line_items, // Order items with additional up to date information pulled from the item class
            'order_subtotal' => $this->getSubtotalCost(),
            'vat_cost' => $this->getVatCost(), // Total VAT cost of the order
            'order_date' => $this->getOrderDate('Y-m-d H:i:s'), // order date sent in full... twig can re-render into any date format from here
            'delivery_cost' => $this->getDelivery()->cost, // Delivery Cost
            'delivery_name' => $this->getDelivery()->name, // Delivery Name
            'order_status_text'=> $order_status_text, // Order status text as defined in admin under shop/order status text
            'countries' => $countries, // Country name
            'state_list' => CountryState::getCountryStateList(), // State name
            'site_config' => $site_config,
        ];

        ob_start();

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

        $mail = ob_get_clean();

        //echo "sending mail now";
        email($this->info['email'], "Your " . $site_config['SITE_NAME'] . " Order " . $this->order_ref, $mail);

        if (DEV_MODE) {
            $mtc = '@mtcmedia.co.uk';
            //send mail to customer if mtc email is used
            //if (substr_compare($this->ifo['email'], $mtc, -strlen($mtc), strlen($mtc)) === 0) {
            if (stristr($this->info['email'], $mtc)) {
                email($this->info['email'], "Your " . $site_config['SITE_NAME'] . " Order (Redirected - Dev Mode)", $mail);
            } else {
                //else send it to dev
                email(DEV_EMAIL, "Your " . $site_config['SITE_NAME'] . " Order (Copy - Dev Mode)", $mail);
            }
        } else {

            email(config('site.shop_email'), "Your " . $site_config['SITE_NAME'] . " Order (Copy)", $mail);

            // Send emails to plugins/modules via hooks
            HooksAdapter::do_action(__CLASS__ . '/' . __FUNCTION__, $site_config['SITE_NAME'] . " Order (Copy)", $mail);

            if (MAIL_DEV_COPY) {
                email(DEV_EMAIL, "Your " . $site_config['SITE_NAME'] . " Order (Copy - Dev)", $mail);
            }

        }
    }

    /**
     * Retrieves and sets order ref
     *
     * @author Janis Lacis <janis.lacis@mtcmedia.co.uk>
     *
     * @return string
     */
    public function getOrderRef(): string
    {
        if (!$order = \Mtc\Shop\Order::query()->find($this->id)) {
            return $this->order_ref;
        }
        return $order->order_ref;
    }

    /**
     * @return array
     */
    public function getCustomerContactHistory(): array
    {
        return Contact::query()
            ->where('order_id', $this->id)
            ->orderByDesc('id')
            ->get()
            ->toArray();
    }

    /**
     * @return array
     */
    public function getOrderNotes(): array
    {
        return Note::query()
            ->where('order_id', $this->id)
            ->orderByDesc('timestamp')
            ->get()
            ->toArray();
    }

    /**
     * Creates a note on order
     *
     * @param $note
     * @return bool
     */
    public function addOrderNote($note): bool
    {
        if (empty($this->id)) {
            return false;
        }
        /** @var Note $orderNote */
        $orderNote = Note::query()->create([
            'order_id' => $this->id,
            'note' => $note,
        ]);
        Event::dispatch(new OrderNoteCreatedEvent($orderNote));
        return true;
    }

    /**
     * Get an order object from a basket ID
     *
     * @param int $basket_id
     * @return Order|bool
     */
    public static function getFromBasketId($basket_id): Order|bool
    {
        if (!$shopOrder = \Mtc\Shop\Order::query()->where('basket_id', $basket_id)->first()) {
            return false;
        }
        $order = new Order();
        $order->build($shopOrder->toArray());
        return $order;
    }

    /**
     * @param $id
     * @return bool|Order
     */
    public static function getById($id): Order|bool
    {
        if (!$shopOrder = \Mtc\Shop\Order::query()->find($id)) {
            return false;
        }
        $order = new Order();
        $order->build($shopOrder->toArray());
        return $order;
    }

    /**
     * Get Discounts
     *
     */
    public function getDiscounts()
    {
        $this->discounts = Discount::query()
            ->where('order_id', $this->id)
            ->get()
            ->toArray();
    }

    /**
     * Set refund amount
     *
     * @return void
     */
    public function setRefundAmount(): void
    {
        $deliveryRefundAmount = Refund::query()
            ->where('order_id', $this->id)
            ->sum('delivery_refund_amount');
        $itemRefundAmount = RefundItem::query()
            ->where('order_id', $this->id)
            ->sum('amount_refunded');

        $this->refundAmount = $deliveryRefundAmount + $itemRefundAmount;
    }

    /**
     * @return void
     */
    public function updateTrustpilotSent(): void
    {
        \Mtc\Shop\Order::query()
            ->where('id', $this->id)
            ->update([
                'trustpilot_sent' => Carbon::now(),
            ]);
    }

    public function updateMessageId($message_id)
    {
        \Mtc\Shop\Order::query()
            ->where('id', $this->id)
            ->update([
                'message_id' => $message_id,
            ]);
    }

    /**
     * Check if this order's total matches an amount. Used for checking paypal payment amount that's rounded differently
     * from the order's total
     * @author Aleksey Lavrinenko
     *
     * @param $amount
     * @return bool
     * @throws Exception
     */
    public function matchesTotal($amount)
    {
        return static::amountsMatch($this->getTotalCost(), $amount);
    }

    public static function batchTrustpilot($orders) {
        foreach($orders as $order => $not_used) {
            $order = new self($order);
            if($order->trustpilot_sent === '0000-00-00 00:00:00') {
                $trustpilot = new \Mtc\Plugins\TrustpilotAPI\Classes\TrustpilotAPI();
                $response = $trustpilot->createReviewInvitation($order);

                if(isset($response->id) || isset($response->reviewLinkId)) {
                    $order->updateTrustpilotSent();
                    \Mtc\Shop\Order\Note::query()
                        ->create([
                            'order_id' => $order->getId(),
                            'note' => 'Trustpilot invitation sent on ' . date('Y-m-d H:i:s')
                        ]);
                    $_SESSION['message'] = 'Invite sent to Trustpilot';
                } else {
                    $_SESSION['error'] = 'Failed to send Trustpilot invite, please try again';
                }
            }
        }
    }

    /**
     * Checks whether the order has items with assessments that need the GP contacted
     */
    public function checkAssessmentForGPNotification()
    {
        $order = (new \Mtc\Shop\Order())->find($this->getId());
        $order_items = $order->items()->where('assessment_id', '>', '0')->get();
        foreach ($order_items as $order_item) {
            $assessment = (new \Mtc\Shop\Assessment\Assessment())->find($order_item->assessment_id);
            foreach ($assessment->answers as $answer) {
                if ($answer->answer === 'Yes' && $answer->question->question_type === 'notify_gp') {
                    $this->sendGPNotificationEmail();
                }
            }

        }
    }

    /**
     * Send an email to admin saying this order needs customers GP contacted
     */
    public function sendGPNotificationEmail()
    {
        $twig = \Illuminate\Support\Facades\App::make('twig');
        $options = [
            'order' => (new \Mtc\Shop\Order())->find($this->getId()),
            'order_date' => $this->getOrderDate('Y-m-d H:i:s'),
            'site_url' => SITE_URL,
        ];
        $mail = $twig->render('/emails/shop/gp_notification.twig', $options);
        $params = [
            'from_email' => GP_NOTIFICATION_EMAIL_FROM,
        ];

        // T100677
        if (
            defined('GP_NOTIFICATION_EMAIL_TO') &&
            !empty(GP_NOTIFICATION_EMAIL_TO) &&
            filter_var(GP_NOTIFICATION_EMAIL_TO, FILTER_VALIDATE_EMAIL)
        ) {
            email(GP_NOTIFICATION_EMAIL_TO, 'GP notification required', $mail, $params);
        }
    }

    public function getWeight()
    {
        $default_weight = (DELIVERY_DEFAULT_ITEM_WEIGHT * 0.001);

        $order_weight = OrderItem::query()
            ->ofOrder($this->id)
            ->with('item')
            ->get()
            ->map(function ($order_item) {
                return $order_item->quantity * ($order_item->item_size->custom->product_weight ?? $order_item->item->custom->product_weight ?? NULL ?: (DELIVERY_DEFAULT_ITEM_WEIGHT * 0.001));
            })
            ->sum();

        return $order_weight > 0 ? $order_weight : $default_weight;
    }

    /**
     * Whether this is a NHS order
     *
     * @return bool
     */
    public function hasNHSItems()
    {
        foreach ($this->getItems() as $item) {
            if ($item['nhs_prescription']) {
                return true;
            }
        }
        return false;
    }


    /**
     * Tells you whether the order has been in a status for more than X days.
     *
     * @param $status
     * @param $number_of_days
     * @return bool
     */
    public function inStatusMoreThanDays($status, $number_of_days)
    {
        if ((int)$this->status !== $status) {
            return false;
        }
        // If this status is changed after the moment of x days ago, return false
        // So if we cannot find this status being changed after moment of x days ago, it means it was changed before.
        return DB::table('order_status')
                ->where('order_id', $this->id)
                ->where('status', $status)
                ->where('timestamp', '>', date('Y-m-d H:i:S', strtotime('-' . $number_of_days . 'days')))
                ->count() === 0;
    }

    /**
     * Return an Eloquent model of this Order.
     * @return \Mtc\Shop\Order
     */
    public function eloquent()
    {
        if ($this->eloquentModel instanceof \Mtc\Shop\Order) {
            return $this->eloquentModel;
        }

        $this->eloquentModel = (new \Mtc\Shop\Order)->find($this->id);

        return $this->eloquentModel;
    }

    /**
     * Creates a batch consent PDF file
     *
     * @author Uldis Zvirbulis <uldis.zvirbulis@mtcmedia.co.uk>
     * @access public
     * @static
     * @param array $order_ids list of NHS member IDs
     * @return mixed
     */
    public static function batchConsentFiles($order_ids)
    {
        if (empty($order_ids)) {
            return false;
        }

        $orders = \Mtc\Shop\Order::query()
            ->whereIn('id', $order_ids)
            ->whereHas('items', function(\Illuminate\Database\Eloquent\Builder $query) {
                $query->where('order_items.nhs_prescription', '=', '1');
            })
            ->get();

        $pdf = new \Mtc\Core\TCPDFWithCustomFonts(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);

        $pdf->SetCreator(PDF_CREATOR);
        $pdf->SetAuthor(FROM_NAME);
        $pdf->setHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE.' 006', PDF_HEADER_STRING);
        $pdf->setPrintHeader(false);
        $pdf->setPrintFooter(false);
        $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
        $pdf->SetMargins(PDF_MARGIN_LEFT - 10, PDF_MARGIN_TOP - 20, PDF_MARGIN_RIGHT);
        $pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM - 20);
        $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);

        $pdf->AddFont('amandasignaturei', '', 'amandasignaturei.php');
        $pdf->SetFont('amandasignaturei', '', 12);

        $x = 0;

        foreach ($orders as $order) {

            $prescription_html = self::getConsentHTML($order);

            $pdf->AddPage();
            $pdf->writeHTML($prescription_html, true, false, true, false, '');
            $x++;
        }

        if (!empty($x)) {
            $pdf->lastPage();
            return $pdf->Output('', 'I');
        }
        return false;
    }

    /**
     * getConsentHTML - creates an HTML template for a single consent
     *
     * @author Uldis Zvirbulis <uldis.zvirbulis@mtcmedia.co.uk>
     * @access public
     * @static
     * @param \Mtc\Shop\Order $order
     * @return string HTML
     */
    public static function getConsentHTML($order)
    {
        $twig = \Illuminate\Support\Facades\App::make('twig');

        $basket_countries = Country::query()->get()->keyBy('code')->toArray();

        $nhs_member = \Mtc\Plugins\NHS\Classes\NHSMember::query()
            ->where('member_id', '=', $order->member)
            ->first();

        $options = [
            'nhs_member' => $nhs_member,
            'order' => $order,
            'doctor_surgery' => $nhs_member->doctor_surgery,
            'basket_countries' => $basket_countries,
        ];
        return $twig->render('NHS/consent/consent.twig', $options);
    }

    /**
     * Check if payment on order is still outstanding
     * Part of deferred payment flow
     *
     * @return bool
     */
    public function hasOutstandingDeferredPayment()
    {
        $has_deffered_payment = false;

        if ($this->payment) {
            $has_deffered_payment = $this->payment['type'] === 'DEFERRED' && empty($this->payment['captured_at']);
        }

        return $has_deffered_payment;
    }

    /**
     * @param $order_item_id
     * @param $admin
     */
    public function approveItem($order_item_id, $admin)
    {
        OrderItem::query()
            ->where('id', $order_item_id)
            ->update([
                'approved' => OrderItem::ORDER_ITEM_APROVED
            ]);

        ApprovalLog::log($admin->id, (int)$order_item_id, OrderItem::$approved_statuses[OrderItem::ORDER_ITEM_APROVED]);

        $orderItem = OrderItem::query()
            ->with('order')
            ->find($order_item_id);

        (new Logger($orderItem->order, User::getAdminUser(), $orderItem->order->member))
            ->log(Logger::ORDER_ITEM_APPROVAL, OrderItem::ORDER_ITEM_APROVED, [
                'order_item_id' => $orderItem->id,
                'item_id' => $orderItem->item_id,
                'item_name' => $orderItem->item_name,
                'variation' => $orderItem->size,
                'quantity' => $orderItem->quantity,
            ]);

        try {
            $this->notifyCustomerOnOrderItemApproval(OrderItem::ORDER_ITEM_APROVED, $order_item_id, $admin);
        } catch (\Exception $exception) {
            // Failed to notify customer
        }
    }

    /**
     * @param $order_item_id
     * @param $admin
     */
    public function rejectItem($order_item_id, $admin)
    {
        OrderItem::query()
            ->where('id', $order_item_id)
            ->update([
                'approved' => OrderItem::ORDER_ITEM_REJECTED
            ]);

        ApprovalLog::log($admin->id, (int)$order_item_id, OrderItem::$approved_statuses[OrderItem::ORDER_ITEM_REJECTED]);

        $orderItem = OrderItem::query()
            ->with('order')
            ->find($order_item_id);

        (new Logger($orderItem->order, User::getAdminUser(), $orderItem->order->member))
            ->log(Logger::ORDER_ITEM_APPROVAL, OrderItem::ORDER_ITEM_REJECTED, [
                'order_item_id' => $orderItem->id,
                'item_id' => $orderItem->item_id,
                'item_name' => $orderItem->item_name,
                'variation' => $orderItem->size,
                'quantity' => $orderItem->quantity,
            ]);
        try {
            $this->notifyCustomerOnOrderItemApproval(OrderItem::ORDER_ITEM_REJECTED, $order_item_id, $admin);
        } catch (\Exception $exception) {
            // Failed to notify customer
        }
    }

    /**
     * Clinically checked by a pharmacist
     *
     * @param $order_item_id
     * @param $admin
     */
    public function clinicallyCheckItem($order_item_id, $admin): void
    {
        OrderItem::query()
            ->where('id', $order_item_id)
            ->update([
                'clinic_checked' => true,
            ]);

        ApprovalLog::log(
            $admin->id,
            (int)$order_item_id,
            OrderItem::$approved_statuses[OrderItem::ORDER_ITEM_CLINICALLY_CHECKED]
        );

        $orderItem = OrderItem::query()
            ->with('order')
            ->find($order_item_id);

        (new Logger($orderItem->order, User::getAdminUser(), $orderItem->order->member))
            ->log(Logger::ORDER_ITEM_APPROVAL, OrderItem::ORDER_ITEM_CLINICALLY_CHECKED, [
                'order_item_id' => $orderItem->id,
                'item_id' => $orderItem->item_id,
                'item_name' => $orderItem->item_name,
                'variation' => $orderItem->size,
                'quantity' => $orderItem->quantity,
            ]);
    }

    /*
     * Notify customer on approval change
     *
     */
    protected function notifyCustomerOnOrderItemApproval($approval_state, $order_item_id, $admin)
    {
        $approval_state = $approval_state == OrderItem::ORDER_ITEM_APROVED ? 'Approved' : 'Rejected';

        $text_message = \Mtc\Plugins\MembersMessaging\Classes\Templates::query()
            ->where('name', "Doctor {$approval_state} Order Item SMS Notification")
            ->firstOrFail();

        $thread_message = \Mtc\Plugins\MembersMessaging\Classes\Templates::query()
            ->where('name', "Doctor {$approval_state} Order Item Messaging Notification")
            ->firstOrFail();

        App::make(DoctorApprovalNotifications::class)
            ->setOrderItem($order_item_id)
            ->setAdmin($admin)
            ->sendMessage($thread_message->withOrderItemReplacements($order_item_id))
            ->sendSMS(strip_tags($text_message->withOrderItemReplacements($order_item_id)));
    }




    /*
     * PDF Labels
     */


    //Get individual label html
    public static function getPharmacyLabelHTMLPDF($order, $item, Environment $twig, $full_path = false, $page_break = false)
    {

        $options = [
            'order' => $order, // order details
            'item' => $item,
            'page_break' => $page_break //Splits dosage and warning into 2 separate labels
        ];

        $html = $twig->render('emails/shop/pharmacy_label_pdf.twig', $options);

        $footer_html = $twig->render('emails/shop/pharmacy_label_pdf_footer.twig', [
            'order' => $order,
        ]);

        return [
            'body_html' => $html,
            'footer_html' => $footer_html,
        ];
    }


    public static function batchPharmacyLabelsPDF2($order_ids, Environment $twig)
    {
        $labels_html = [];

        $labels = request()->input('labels');

        if ($labels) {
            foreach ($labels as $order_item_id => $groups) {

                $order_item = OrderItem::find($order_item_id);
                if (! $order_item) continue;



                foreach ($groups as $group_name => $label_quantities) {
                    foreach ($label_quantities as $qty) {
                        $label_title = $order_item->item_name;
                        if ($order_item->sizeid && trim($order_item->size)) {
                            $label_title = $order_item->size;
                        }

                        $body_data = [
                            'label_title' => $label_title,
                            'dosage' => $order_item->getDosageInfo(),
                            'warnings' => $order_item->item_size->custom->label_warnings ?? $order_item->item->custom->label_warnings,
                            'page_break' => false,
                        ];

                        $footer_data = [
                            'full_name' => $group_name,
                            'order_ref' => "{$order_item->order->id}-{$order_item->id}",
                            'label_date' => date('c'),
                            'label_qty' => $qty,
                        ];


                        /*
                       $body_data = [
                           'label_title' => 'This is a multiline label test to check the size. I\'ll add more text as this didn\'t break onto',
                           'dosage' => 'Take 1-2 tablets every 4-6 hours. More text to fill the space. Take on a full stomach and more text to fill even more space. Take 1-2 tablets every 4-6 hours. More text to fill the space. Take on a full stomach and more text to fill even more space.',
                           'warnings' => 'Keep out of reach of children. Do not exceed the dosage. More text to fill this space too to see what happens. Keep out of reach of children. Do not exceed the dosage. More text to fill this space too to see what happens.',
                           'page_break' => false,
                       ];

                       $footer_data = [
                           'full_name' => 'Christopher McGuckin',
                           'order_ref' => "{$order_item->order->id}-{$order_item->id}",
                           'label_date' => date('c'),
                           'label_qty' => $qty,
                       ];
                        */


                        $labels_html[] = [
                            'body_html' => $twig->render('emails/shop/pharmacy_label_pdf2.twig', $body_data),
                            'footer_html' => $twig->render('emails/shop/pharmacy_label_pdf_footer2.twig', $footer_data),
                        ];
                    }
                }
            }

            self::generateLabelTemplatePDF($labels_html, $order_ids, $twig);
        } else {
            echo 'Sorry, there are no order labels available! <button onclick="window.close();">Go Back</button>';
        }
    }

    //Setup PDF page layout and add the html labels to it
    private static function generateLabelTemplatePDF($html_labels, $order_ids, Environment $twig)
    {

        //Font was looking jagged so tried:
        // 1. Removed font so it uses the default font
        // 2. Doubled the size, spacing and font size of the labels

        //Settings (in mm)
        $dimensions = [72,142];
        $margin_top = 4; //2
        $margin_sides = 4; // 2
        $font_size = 14; // 7 //Body text and heading (Heading uses 1.2em to increase size)
        $footer_font_size = 12; //6
        $bottom_space = 28; // 10

        //Setup for PDF with footer
        $pdf = new Mtc\Core\LabelPDF('L', 'mm', $dimensions, true, 'UTF-8', false);

        //Setup footer
        $pdf->footer_font_size = $footer_font_size;
        $pdf->footer_from_bottom = 0 - 28;

        //Setup page
        $pdf->SetCreator(PDF_CREATOR);
        $pdf->SetAuthor(config('app.name'));
        $pdf->setPrintHeader(false);
        $pdf->setPrintFooter(false);
        $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
        $pdf->SetMargins($margin_sides,$margin_top,$margin_sides);
        $pdf->SetAutoPageBreak(TRUE, $bottom_space);
        $pdf->setCellPaddings(0,0,0,0);

        //Tag spacing
        $tagvs = array(
            'p' => [
                ['h' => 0, 'n' => 0],
                ['h' => 2, 'n' => 2], //['h' => 1, 'n' => 1]
            ]
        );
        $pdf->setHtmlVSpace($tagvs);
        $pdf->setCellHeightRatio(1.2);

        if (@file_exists(dirname(__FILE__).'/lang/eng.php')) {
            require_once(dirname(__FILE__).'/lang/eng.php');
            $pdf->setLanguageArray([]);
        }

        //$pdf->SetFont('dejavusans', '', $font_size);
        $pdf->SetFont('', '', $font_size);
        $pdf->SetTitle('Print Order Labels');
        $pdf->SetSubject('PDF order labels');


        //Build the label pages from html
        foreach ($html_labels as $label) {

            $pdf->AddPage();

            $pdf->setHtmlFooter($label['footer_html']);
            $pdf->setPrintFooter(true);

            //Add the label html
            $pdf->writeHTML($label['body_html'], true, false, true, true, 'C');

        }

        //Reset pointer to last page
        $pdf->lastPage();


        //Build filename
        $order_id_string = '';

        foreach($order_ids as $id) {
            $order_id_string .= $id;
        }

        //Create pdf file
        $pdf_name = SITE_PATH . '/uploads/files/labels/' . md5($order_id_string) . '.pdf';

        if (file_exists($pdf_name) || empty($order_id_string)) {
            rename($pdf_name, str_replace('.pdf', '_' . time() . '.pdf', $pdf_name));
        }

        //Output the file
        return $pdf->Output($pdf_name, 'I');

    }

    /**
     * Whether the order is for a consultation
     *
     * @return bool
     */
    public function isConsultation(): bool
    {
        $orderItems = $this->getItems();
        foreach ($orderItems as $orderItem) {
            $item = ShopItem::query()
                ->find($orderItem['item_id']);
            if (!empty($item) && $item->product_type === ShopItem::TYPE_CONSULTATION) {
                return true;
            }
        }
        return false;
    }


}
