<?php

namespace App\Modules\Bookings;

use App\Modules\Stock\StockTransaction;
use App\Modules\Stock\Stock;
use Exception;
use Illuminate\Support\Facades\Event;
use App\Models\Status;

class PartDeleted
{
    /**
     * The part that was updated.
     *
     * @var \App\Modules\Bookings\Part
     */
    protected $part;

    /**
     * Subscribe to events.
     *
     * @param  mixed $events
     *
     * @return null
     */
    public function subscribe($events)
    {
        $events->listen('PartDeleted', 'App\Modules\Bookings\PartDeleted@fire');
    }

    /**
     * Fire the event.
     *
     * @param  \App\Modules\Bookings\Part $part
     *
     * @return bool
     */
    public function fire($part)
    {
        $this->part = $part;

        if ($part->related->type == 'order') {
            $this->updateOrderStatus();
        }

        if ($part->stock->composite) {
            return $this->processCompositePartFlow();
        }

        return $this->processNormalFlow();
    }

    /**
     * Update or delete stock when this part is on order.
     *
     * @return bool
     */
    protected function processNormalFlow()
    {
        if ($this->part->stock->approval_required) {
            if (! $this->part->stock->delete()) {
                throw new Exception('Failed to delete the temporary stock');
            }

            return;
        }

        // Remove from quantity to order the amount required
        if ($this->part->quantity_on_order > 0) {
            Event::dispatch('CancelRequestedStock', [$this->part->stock, $this->part->quantity_on_order]);
        }

        // Creates a transaction to put back the items used in stock
        if ($this->part->quantity > $this->part->quantity_on_order) {
            $this->createTransaction();
        }

        return true;
    }

    /**
     *
     */
    protected function processCompositePartFlow()
    {
        $items = [];
        foreach ($this->part->composite_items as $item) {
            $items[$item->id] = ['quantity' => $item->pivot->quantity];
        }

        // If the composite items in the part are the same than in stock, act as a simple part.
        if (Stock::sameItems($this->part->stock, $items)) {
            return $this->processNormalFlow();
        }

        // Duplicate the composite stock with no items on it
        $stock = Stock::duplicate($this->part->stock, false);

        if (count($items)) {
            $stock->composite_items()->attach($items);
        }

        // Act as a in a simple part but using as stock the one created.
        $this->part->stock_id = $stock->id;

        return $this->processNormalFlow();
    }

    /**
     * Create a stock transaction that will
     * automatically update the stock.
     *
     * @return bool
     */
    protected function createTransaction()
    {
        $transaction = StockTransaction::create([
            'stock_id'     => $this->part->stock_id,
            'related_type' => $this->part->related_type,
            'related_id'   => $this->part->related_id,
            'quantity'     => $this->part->quantity - $this->part->quantity_on_order,
        ]);

        if (! $transaction->exists) {
            throw new Exception('Failed to create stock transaction');
        }

        return true;
    }

    /**
     * Update the order status to delivered if there are no more on order parts and the status is "Not Attempted".
     *
     * @return void
     */
    protected function updateOrderStatus()
    {
        if ($this->part->related->status_id == Status::ORDERS_NOT_ATTEMPTED) {
            if (Part::where('related_id', $this->part->related_id)->where('related_type', 'task')->where('quantity_on_order', '>', 0)->count() == 0) {
                $this->part->related->update(['status_id' => Status::ORDERS_DELIVERED]);
            }
        }
    }
}
