<?php

namespace App\Modules\Bookings;

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

class BuildUpdating
{
    /**
     * The build that was updated.
     *
     * @var \App\Modules\Bookings\Build
     */
    protected $build;

    /**
     * The build in the original state.
     *
     * @var \App\Modules\Bookings\Build
     */
    protected $oldBuild;

    /**
     * The quantity that has been updated.
     *
     * @var int
     */
    protected $quantity;

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

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

        if ($this->build->quantity != $this->oldBuild->quantity) {
            foreach ($build->stock->composite_items()->get() as $component) {
                if ($component->unlimited) {
                    $this->createTransaction($component, $component->pivot->quantity * ($oldBuild->quantity - $build->quantity));
                } else {
                    $this->processNormalFlow($component);
                }
            }
        }

        return true;
    }

    /**
     * Update the build quantity.
     *
     * @return bool
     */
    protected function processNormalFlow($component)
    {
        // Get the quantity of the different between the quantity needed before and the quantity needed now for this item
        $quantity_diff = ($this->build->quantity - $this->oldBuild->quantity) * $component->pivot->quantity;

        if ($quantity_diff == 0) {
            return true;
        }

        // If this item has been ordered, get the ordered entry and delete it to update the quantity
        if ($ordered_component = $this->build->ordered_components()->where('stock_id', $component->id)->first()) {
            $this->build->ordered_components()->detach($ordered_component);
            Event::dispatch('CancelRequestedStock', [$component, $ordered_component->pivot->quantity]);
        }

        $quantity_ordered = $ordered_component ? $ordered_component->pivot->quantity : 0;
        $stock_available = $component->quantity_available();
        $quantity_needed = ($quantity_ordered + $quantity_diff) - $stock_available;

        if ($quantity_needed > 0) {
            $this->build->ordered_components()->attach([$component->id => ['quantity' => $quantity_needed]]);
            Event::dispatch('StockRequested', [$component, $quantity_needed]);
        }

        $used_quantity = $quantity_diff + $quantity_ordered - ($quantity_needed > 0 ? $quantity_needed : 0);

        $this->createTransaction($component, -$used_quantity);

        return true;
    }

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

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

        return true;
    }
}
