<?php
/**
 * Order Object Eloquent model
 *
 * @version 19/10/16
 * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
 */

namespace Mtc\Shop;

use AdminUser;
use App\Events\OrderCreatedEvent;
use Basket;
use Carbon\Carbon;
use Database\Factories\OrderFactory;
use DeliveryMethod;
use DiscountOffer;
use HooksAdapter;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Event;
use Mtc\Modules\Members\Models\Member;
use Mtc\Modules\Members\Models\MemberNote;
use Mtc\Plugins\MembersMessaging\Classes\Thread;
use Mtc\Shop\Assessment\AnswerMagento;
use Mtc\Shop\Assessment\Assessment;
use Mtc\Shop\Basket\BelongsToBasket;
use Mtc\Shop\Events\MemberNoteCreatedEvent;
use Mtc\Shop\Item\Size;
use MtcPharmacy\Subscriptions\Classes\Subscription;

/**
 * Order Object Eloquent model.
 * Implements Shop order basic functionality
 *
 * @version 19/10/16
 * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
 *
 */
class Order extends Model
{
    // Define relation to basket
    use BelongsToBasket;

    use HasFactory;

    const STATUS_ORDER_RECEIVED = 0;
    const STATUS_PROCESSING = 1;
    const STATUS_SHIPPED = 4;
    const STATUS_PART_REFUNDED = 5;
    const STATUS_REFUNDED = 6;
    const STATUS_WAITING_FOR_RESPONSE = 8;
    const STATUS_CANCELLED = 9;
    const STATUS_AWAITING_GP_CONFIRMATION = 10;
    const STATUS_AWAITING_PRESCRIPTION_STATUS = 12;
    const STATUS_RETURNED = 14;
    const STATUS_NO_RESPONSE = 15;
    const STATUS_GROUPED = 16;
    const STATUS_COMPLETE = 17;

    /**
     * @var string[] $statuses List all Order statuses
     */
    public static $statuses = [
        self::STATUS_ORDER_RECEIVED => 'Order Received',
        self::STATUS_PROCESSING => 'Processing',
        self::STATUS_SHIPPED => 'Shipped',
        self::STATUS_PART_REFUNDED => 'Part Refunded',
        self::STATUS_REFUNDED => 'Refunded',
        self::STATUS_WAITING_FOR_RESPONSE => 'Waiting For Response',
        self::STATUS_CANCELLED => 'Cancelled',
        self::STATUS_AWAITING_GP_CONFIRMATION => 'Awaiting GP Confirmation',
        self::STATUS_AWAITING_PRESCRIPTION_STATUS => 'Awaiting Prescription Status',
        self::STATUS_RETURNED => 'Returned',
        self::STATUS_NO_RESPONSE => 'No Response',
        self::STATUS_GROUPED => 'Part of group',
        self::STATUS_COMPLETE => 'Consultation Complete',
    ];

    /**
     * @var string[] $statuses List failure Order statuses
     */
    public static $failure_statuses = [
        9 => 'Cancelled'
    ];

    /**
     * @var array $order_types list of order statuses mapped against user workflow. Used in manage orders
     */
    public static $order_types = [
        'completed' => [
            self::STATUS_SHIPPED
        ],
        'processing' => [
            self::STATUS_ORDER_RECEIVED,
            self::STATUS_PROCESSING,
            self::STATUS_PART_REFUNDED,
            self::STATUS_WAITING_FOR_RESPONSE,
            self::STATUS_AWAITING_GP_CONFIRMATION,
            self::STATUS_AWAITING_PRESCRIPTION_STATUS,
        ],
        'cancelled' => [
            self::STATUS_CANCELLED,
        ],
        'refunded' => [
            self::STATUS_REFUNDED,
        ],
        'awaiting' => [
            self::STATUS_WAITING_FOR_RESPONSE,
        ],
        'returned' => [
            self::STATUS_RETURNED
        ],
        'no_response' => [
            self::STATUS_NO_RESPONSE
        ],
        'unpaid' => [
            self::STATUS_ORDER_RECEIVED
        ],
    ];

    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'order';

    /**
     * @var array The attributes that are mass assignable.
     */
    protected $fillable = [
        'paid',
        'parent_id',
        'basket_id',
        'subscription_id',
        'date',
        'delivery_method_id',
        'delivery_name',
        'delivery_cost',
        'delivery_instructions',
        'coupon',
        'coupon_code',
        'cost_total',
        'cost_total_exvat',
        'status',
        'member',
        'admin_id',
        'type',
        'ref',
        'keywords',
        'failed',
        'notes',
        'failed_id_check',
        'order_ref',
        'ga_ga',
        'flag',
        'worldpay_order_code',
        'worldpay_session_id',
        'has_subscription',
    ];

    /**
     * Indicates if the model should be timestamped.
     *
     * @var bool
     */
    public $timestamps = false;
    public $order_id;
    /**
     * Scope - successful()
     *
     * @param Builder $query Query to extend
     * @return Builder
     */
    public function scopeSuccessful(Builder $query)
    {
        return $query->where("paid", 1)
            ->where("failed", 0)
            ->whereNotIn('status', array_keys(self::$failure_statuses));
    }

    public function statusObj()
    {
        return $this->belongsTo(OrderStatus::class, 'status', 'id');
    }

    /**
     * Relationship with member
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function customer()
    {
        return $this->belongsTo(Member::class, 'member', 'id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function delivery()
    {
        return $this->belongsTo(DeliveryMethod::class, 'delivery_method_id');
    }


    public function subscriptions()
    {
        return $this->belongsToMany(Subscription::class, 'subscriptions_orders');
    }


    public function parent()
    {
        return $this->belongsTo(self::class, 'parent_id');
    }


    public function orders()
    {
        return $this->hasMany(self::class, 'parent_id');
    }


    /**
     * Define the relationship to order items
     * @return HasMany
     */
    public function items()
    {
        return $this->hasMany(Order\Item::class, 'order_id');
    }

    public function pharmacy_label_items()
    {
        return $this->hasMany(Order\Item::class, 'order_id')
            ->whereHas('item', function ($item_query) {
                return $item_query->where('product_type', 'doctor')
                    ->where('approved', '1');
            });
    }

    /**
     * Define the relationship to order addresses
     * @return HasMany
     */
    public function address()
    {
        return $this->hasMany(Order\Address::class, 'order_id');
    }

    /**
     * Define the relationship to order addresses
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function billingAddress()
    {
        return $this->hasOne(Order\Address::class, 'order_id')
            ->where('type', 'billing');
    }
    /**
     * Define the relationship to order addresses
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function shippingAddress()
    {
        return $this->hasOne(Order\Address::class, 'order_id')
            ->where('type', 'shipping');
    }

    /**
     * Define the relationship to order info
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function info()
    {
        return $this->hasOne(Order\Info::class, 'order_id');
    }

    /**
     * Relationship to order notes
     *
     * @return HasMany
     */
    public function notes()
    {
        return $this->hasMany(Order\Note::class, 'order_id');
    }

    /**
     * Relationship to member notes
     *
     * @return HasMany
     */
    public function memberNotes(): HasMany
    {
        return $this->hasMany(MemberNote::class, 'order_id');
    }

    /**
     * Relationship to order shipments
     *
     * @return HasMany
     */
    public function shipments()
    {
        return $this->hasMany(Order\Shipment::class, 'order_id');
    }

    /**
     * Define the relationship to order info
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function coupon()
    {
        return $this->hasOne(Order\Coupon::class, 'order_id');
    }

    /**
     * Define the relationship to order discounts
     * @return HasMany
     */
    public function discounts()
    {
        return $this->hasMany(Order\Discount::class, 'order_id');
    }

    /**
     * Define the relationship to Assessments
     * @return hasMany
     */
    public function assessments()
    {
        return $this->hasMany(Assessment::class, 'form_id');
    }


    public function assessment_magento_answers()
    {
        return $this->hasOne(AnswerMagento::class, 'mtc_order_id');
    }

    public function getOrderStatusTitle()
    {
        return self::$statuses[$this->status] ?? 'Unknown';
    }


    /**
     * Create new order based on Basket
     * @param \Basket $basket basket instance
     * @return \Basket users basket with Order ID attached
     */
    public static function newOrder(Basket $basket)
    {
        // Refuse to persist an order built from an empty basket. Without this every caller
        // that forgets to validate first (or a UI race where the basket is emptied between
        // page render and submit) silently produces a paid order with zero line items.
        if (empty($basket->items)) {
            throw new \DomainException('Cannot create an order from an empty basket.');
        }

        $member_id = $basket->member;
        $member = (new Member())->find($member_id);

        $coupon = empty($basket->coupon_code) ? 0 : 1;
        $exported = null;

        $delivery_name = '';
        if ($basket->coupon_freedelivery) {
            $delivery_name = 'Free Delivery Coupon';
        } else {
            $delivery_chosen = DeliveryMethod::query()->find($basket->delivery_selected);
            if ($delivery_chosen) {
                $delivery_name = $delivery_chosen->name;
            }
        }

        $orderAttributes = [
            'basket_id' => $basket->id,
            'subscription_id' => $basket->subscription_id,
            'has_subscription' => $basket->has_subscription,
            'failed' => 0,
            'delivery_name' => $delivery_name,
            'delivery_method_id' => $basket->coupon_freedelivery ? 1 : $basket->delivery_selected,
            'delivery_cost' => empty($basket->cost_delivery) ? 0 : $basket->cost_delivery,
            'delivery_instructions' => clean_page($basket->delivery_instructions),
            'cost_total' => $basket->cost_total,
            'cost_total_exvat' => $basket->cost_total_exvat,
            'member' => $basket->member ?: (int) $_SESSION['member_id'],
            'admin_id' => (int) Arr::get($_SESSION, 'adminId', 0),
            'exported' => $exported,
            'coupon' => $coupon,
            'coupon_code' => empty($basket->coupon_code) ? '' : $basket->coupon_code,
            'type' => empty($basket->type) ? '' : $basket->type,
            'ref' => empty($basket->ref) ? '' : $basket->ref,
            'keywords' => empty($basket->keywords) ? '' : $basket->keywords,
            'ga_ga' => empty($basket->ga_ga) ? '' : $basket->ga_ga,
            'flag' => empty($basket->flag_type) ? 0 : $basket->flag_type
        ];

        $existingOrder = self::query()
            ->where('basket_id', $basket->id)
            ->where('paid', 0)
            ->where('failed', 0)
            ->orderByDesc('id')
            ->first();

        $isNewOrder = false;

        if ($existingOrder) {
            $order = $existingOrder;
            $order->fill($orderAttributes);
            $order->save();

            $order->items()->delete();
            $order->info()->delete();
            $order->address()->delete();
            $order->discounts()->delete();
            $order->coupon()->delete();
        } else {
            /** @var Order $order */
            $order = self::query()->create($orderAttributes);
            $isNewOrder = true;
        }

        $basket->order_id = $order->id;
        $_SESSION['order_id'] = $order->id;

        // Add entries for order->items based on basket->items
        foreach ($basket->items as $item) {
            $value = 0;
            if ($basket->coupon_percentoff > 0) {
                $value = round(((($item['item_price'] / 100) * $basket->coupon_percentoff)) * 100);
            } elseif ($basket->coupon_amountof) {
                //find what percentage of the total order value the price of the current item is
                $percentage = ($item['item_price'] / $basket->cost_subtotal) * 100;
                //work out the value of $percentage of the coupon
                $coupon_item_value = ($basket->coupon_deduct / 100) * $percentage;
                //remove coupon value from item value
                $value = round($coupon_item_value * 100);
            }

            $item['value'] = $value / 100;

            $item_eloquent = Item::query()
                ->with('custom')
                ->find($item['item_id']);

            if ($item_eloquent) {
                // Auto approve items that don't need review by doctor or pharmacist
                $item['approved'] = in_array($item_eloquent->product_type, [Item::TYPE_PHARMACY, Item::TYPE_DOCTOR]) ? 0 : 1;
            }

            if (!empty($item['refill_date'])) {
                $item['reminder_date'] = $item['refill_date'];
            }

            // bundle_id -> bundles and prescription_item_id -> nhs_prescription_items
            // are nullable FK columns. The basket layer stores a legacy 0 sentinel
            // for "no relation" (starter's class.basket.php casts to (int) 0), which
            // violates the constraint when those tables have no id=0 row. Normalise
            // to null, matching how existing order_items rows are stored.
            $item['bundle_id'] = empty($item['bundle_id']) ? null : $item['bundle_id'];
            if (isset($item['prescription_item_id']) && empty($item['prescription_item_id'])) {
                $item['prescription_item_id'] = null;
            }

            $order_item = $order->items()->create($item);

            HooksAdapter::do_action('New_Order/Save_Item', $order_item->id, $item['id']);
        }

        // Save health profile
        $basket->info['member_allergies'] = !empty($member) ?
            ($member->allergies ?? json_encode([])) :
            (
                json_encode(
                    !empty($_SESSION['health_profile']['allergies']) ?
                    $_SESSION['health_profile']['allergies'] :
                    []
                )
            );

        $basket->info['member_medications'] = empty($member) ?
            ($member->medications ?? json_encode([])) :
            (
                json_encode(
                    !empty($_SESSION['health_profile']['medications']) ?
                    $_SESSION['health_profile']['medications'] :
                    []
                )
            );

        $basket->info['member_conditions'] = !empty($member) ?
            ($member->conditions ?? json_encode([])) :
            (
                json_encode(
                    !empty($_SESSION['health_profile']['conditions']) ?
                    $_SESSION['health_profile']['conditions'] :
                    []
                )
            );

        // Add information about order->info based on basket->info
        $orderInfoData = $basket->info;
        if (is_array($orderInfoData)) {
            unset($orderInfoData['id'], $orderInfoData['order_id']);
            $order->info()->create($orderInfoData);
        }


        // Add information about order->address based on basket->address
        if (sizeof($basket->address) > 0) {
            foreach ($basket->address as $data) {
                if (!is_array($data)) {
                    continue;
                }
                $addressData = $data;
                unset($addressData['id'], $addressData['order_id']);
                $order->address()->create($addressData);
            }
        }

        // create information about coupon for order
        if (!empty($basket->coupon_code)) {
            $order->coupon()->create([
                'code' => $basket->coupon_code,
                'freedelivery' => empty($basket->coupon_freedelivery) ? 0 : $basket->coupon_freedelivery,
                'amountoff' => empty($basket->coupon_amountof) ? 0 : $basket->coupon_amountof,
                'percentoff' => empty($basket->coupon_percentoff) ? 0 : $basket->coupon_percentoff,
                'deduct' => empty($basket->coupon_deduct) ? 0 : $basket->coupon_deduct
            ]);
        }

        // create information about discounts for order
        foreach ($basket->discounts_found as $discount) {

            $order->discounts()->create([
                'discount_amount' => $discount['discount_amount'],
                'discount_type' => $discount['discount_type'],
                'discount_id' => $discount['discount_id'],
                'discount_name' => $discount['discount_name'],
                'payment_type' => empty($basket->payment_type) ? '' : $basket->payment_type
            ]);

            if ($isNewOrder) {
                $tmp_discount = new DiscountOffer();
                $tmp_discount->Get($discount['discount_id']);
                $tmp_discount->AddRedemption();
            }
        }

        if ($isNewOrder) {
            Event::dispatch(__CLASS__ . '@' . __FUNCTION__, $order, $basket);
            Event::dispatch(OrderCreatedEvent::class, new OrderCreatedEvent($order, $basket));
        }

        return $basket;
    }

    /*
     * A method that finds and deletes unpaid orders, the default is 24 hours (1 Day).
     * The time setting is dictated by a setting in the Settings or can also be given manually.
     *
     * @author Jack Donaldson <jack.donaldson@mtcmedia.co.uk>
     *
     * @param int $id
     *
     * @param int $time - Measured in Hours - Expected input would be 24, 168, 672, 2016
     *
     * @return true if any deletion occurs, false otherwise.
     *
     */
    public static function deleteUnpaid($id)
    {
        $time = self::getTimedDeleteUnpaidOrders();

        $order = self::where('paid', 0)
            ->where('id', $id)
            ->whereRaw('date < DATE_SUB(NOW(), INTERVAL ' . clean_value_space($time) . ' HOUR)')
            ->first();

        if (!empty($order)) {
            self::deleteById($id);

            return true;
        } else {
            return false;
        }
    }

    /*
     * A method that finds and deletes old unpaid orders, the default is 12 Weeks (3 Months)
     *
     * @author Jack Donaldson <jack.donaldson@mtcmedia.co.uk>
     *
     * @return true if any deletion occurs, false otherwise.
     *
     */
    public static function deleteAuto()
    {
        $time = self::getAutoDeleteUnpaidOrdersTime();

        $orders = self::where('paid', 0)
            ->whereRaw('date < DATE_SUB(NOW(), INTERVAL ' . clean_value_space($time) . ' WEEK)')
            ->get();
        if (count($orders) > 0) {
            foreach ($orders as $order) {
                self::deleteUnpaid($order->id, $time);
            }
            return true;
        } else {
            return false;
        }
    }

    /*
     * Deletes the Order_item that has the same order_id as the one given in the param.
     * This also will delete the order from other Order related models/tables.
     *
     * @author Jack Donaldson <jack.donaldson@mtcmedia.co.uk>
     *
     * @param int $id
     *
     */
    public static function deleteById($id)
    {
        $order = self::find($id);

        $order->info()->delete();

        $order->address()->delete();

        $order->items()->delete();

        $order->discounts()->delete();

        $order->coupon()->delete();

        $order->delete();
    }

    /*
     * A method that finds the time in the Settings for the manual deletion of orders.
     * Time is dictated by a setting in the Settings and measured in hours.
     * the default is 24 hours (1 Day).
     *
     * @author Jack Donaldson <jack.donaldson@mtcmedia.co.uk>
     *
     * @return $time, the time is determined by the TIMED_DELETE_UNPAID_ORDERS setting.
     *
     */
    public static function getTimedDeleteUnpaidOrders()
    {
        if (!TIMED_DELETE_UNPAID_ORDERS) {
            $time = 24;
        } else {
            $time = TIMED_DELETE_UNPAID_ORDERS;
        }

        return $time;
    }

    /*
     * A method that finds the time in the Settings for the autodeletion of orders.
     * The default is 12 Weeks (3 Months)
     *
     * @author Jack Donaldson <jack.donaldson@mtcmedia.co.uk>
     *
     * @return $time, the time is determined by the AUTO_DELETE_UNPAID_ORDER_TIME setting.
     *
     */

    public static function getAutoDeleteUnpaidOrdersTime()
    {
        if (!AUTO_DELETE_UNPAID_ORDER_TIME) {
            $time = 12;
        } elseif (AUTO_DELETE_UNPAID_ORDER_TIME < 4) {
            $time = 4;
        } else {
            $time = AUTO_DELETE_UNPAID_ORDER_TIME;
        }

        return $time;
    }

    /**
     * Finds the orders of the member. Merges with orders that match member's address
     *
     * @param $member_id
     * @param $address
     * @return Collection
     */
    public static function findPersonOrders($member_id, $info, $address)
    {
        $orders = new Collection();
        // If user not logged in, search by email address
        if (empty($member_id)) {
            if (isset($info['email'])) {
                if ($member = Member::query()->where('email', $info['email'])->first()) {
                    $member_id = $member->id;
                }
            }
        }
        if (!empty($member_id)) {
            $orders = self::query()
                ->where('member', $member_id)
                ->where('paid', '1')
                ->where('status', '<>', '9')
                ->where('status', '<>', '6')
                ->get()
                ->keyBy('id');
        }
        if (
            !empty($address['address1']) &&
            !empty($address['country']) &&
            !empty($address['postcode']) &&
            !empty($address['firstname'])
        ) {
            $orders_by_address = self::query()
                ->select('order.*')
                ->join('order_address', function ($join) use ($address) {
                    $join->on('order_address.order_id', '=', 'order.id')
                        ->where('order_address.type', 'shipping')
                        ->where('order_address.address1', $address['address1'])
                        ->where('order_address.address2', $address['address2'])
                        ->where('order_address.country', $address['country'])
                        ->where('order_address.postcode', $address['postcode'])
                        ->where('order_address.firstname', $address['firstname']);
                })
                ->where('paid', '1')
                ->where('status', '<>', '9')
                ->where('status', '<>', '6')
                ->get()
                ->keyBy('id');
            $orders = $orders->merge($orders_by_address);
        }

        return $orders;
    }


    /**
     * Builds an array of ingredient purchase history. Result contains quantity and date. Grouped by ingredient ID
     *
     * @param Collection $orders
     * @return array
     */
    public static function getIngredientPurchaseHistory(Collection $orders)
    {
        $orders->loadMissing(['items.item_size', 'items.item.ingredients']);

        $ingredient_history = [];
        foreach ($orders as $order) {
            foreach ($order->items as $order_item) {
                $tablets_quantity = 0;
                if ($order_item->item_size) {
                    $tablets_quantity = (int) $order_item->item_size->quantity * $order_item->quantity;
                }
                $ingredients = $order_item->item->ingredients ?? null;
                if ($ingredients) {
                    foreach ($ingredients as $ingredient) {
                        $ingredient_history[$ingredient->id][] = [
                            'quantity' => $order_item->quantity,
                            'tablets_quantity' => $tablets_quantity,
                            'date' => $order->date,
                        ];
                    }
                }
            }
        }
        return $ingredient_history;
    }

    /**
     * Builds an array of item purchase history. Result contains quantity and date. Grouped by item ID
     *
     * @param Collection $orders
     * @return array
     */
    public static function getItemPurchaseHistory(Collection $orders)
    {
        $orders->loadMissing('items.item_size');

        $item_history = [];
        foreach ($orders as $order) {
            foreach ($order->items as $order_item) {
                $tablets_quantity = 0;
                if ($order_item->item_size) {
                    $tablets_quantity = (int) $order_item->item_size->quantity * $order_item->quantity;
                }
                $item_history[$order_item->item_id][] = [
                    'quantity' => $order_item->quantity,
                    'tablets_quantity' => $tablets_quantity,
                    'date' => $order->date,
                ];
            }
        }
        return $item_history;
    }

    public function scopeOfMember(Builder $query, $member_id)
    {
        return $query->where('member', $member_id);
    }

    /**
     * get the order weight
     *
     * @return int
     */
    public function weight()
    {
        return (new \Order($this->id))->getWeight();
    }

    /**
     * Updates order totals taking into account the following:
     * - individual order item price and price ex VAT
     * - delivery cost
     * - order coupon (if used)
     *
     * @param $order_id
     */
    public static function updateTotals($order_id)
    {
        if (!$order = (new self())->find($order_id)) {
            return;
        }
        $cost_total = 0;
        $cost_total_exvat = 0;
        foreach ($order->items as $order_item) {
            $cost_total += $order_item->item_price * $order_item->quantity;
            $cost_total_exvat += $order_item->item_price_exvat * $order_item->quantity;
        }
        $order->cost_total = $cost_total + $order->delivery_cost;
        $order->cost_total_exvat = $cost_total_exvat + $order->delivery_cost;
        if ($order_coupon = \Mtc\Shop\Order\Coupon::query()->where('order_id', $order_id)->first()) {
            $order->cost_total -= $order_coupon->deduct;
            $order->cost_total_exvat -= $order_coupon->deduct;
        }
        $order->save();
    }

    /**
     * Changes order delivery method and updates order totals
     *
     * @param $order_id
     * @param $new_delivery_method_id
     * @return bool
     */
    public static function changeDeliveryMethod($order_id, $new_delivery_method_id): bool
    {
        if (!($order = (new self())->find($order_id))) {
            return false;
        }

        if (!($new_delivery_method = DeliveryMethod::query()->find($new_delivery_method_id))) {
            return false;
        }

        if ($order->delivery_method_id === $new_delivery_method->id) {
            return false;
        }

        $new_delivery_cost = $new_delivery_method->cost;

        // Subtract the old price
        $order->cost_total = round($order->cost_total - $order->delivery_cost, 2);
        $order->cost_total_exvat = round($order->cost_total_exvat - $order->delivery_cost, 2);

        // Append the new price
        $order->cost_total = round($order->cost_total + $new_delivery_cost, 2);
        $order->cost_total_exvat = round($order->cost_total_exvat + $new_delivery_cost, 2);

        // Set the new price
        $order->delivery_cost = $new_delivery_cost;

        $order->delivery_method_id = (int) $_REQUEST['delivery_method_id'];
        $order->delivery_name = $new_delivery_method->name;
        $order->save();

        $_SESSION['message'] = 'Delivery method changed successfully!';

        return true;
    }


    public function hasMagentoAssessment()
    {
        return (bool) ($this->magento_entity_id * AnswerMagento::query()->where('mtc_order_id', '=', $this->id)->count());
    }


    public function getAssessmentMagentoAnswers()
    {
        $answers = [];

        if ($this->hasMagentoAssessment()) {
            $answers_obj = AnswerMagento::query()->where('mtc_order_id', '=', $this->id)->first();
            $answers = json_decode($answers_obj->magento_answer, true);
        }

        return $answers;

    }


    public function getMessageThreadsData()
    {
        $data = [
            'has_unread_messages' => false,
            'thread' => '',
        ];

        $threads = Thread::query()->where('order_id', $this->id)->get();
        foreach ($threads as $thread) {
            $data['thread'] = $thread;
            if ($thread->hasUnreadMessagesFromCustomer()) {
                $data['has_unread_messages'] = true;
                break;
            }
        }

        return $data;
    }


    public function legacy()
    {
        return new \Order($this->id);
    }


    public function getAdminUrl()
    {
        $url = "/shop/admin/orders/manage.order.php?id={$this->id}";

        return $url;
    }

    public function isFailedSubscriptionPayment()
    {
        if ($this->paid != 0) {
            return false;
        }
        return $this->subscription_id !== null || $this->subscriptions()->exists();
    }

    public static function newFactory()
    {
        return new OrderFactory();
    }
}
