<?php

namespace App\Modules\Invoices;

use App\Modules\Stock\StockTransaction;
use App\Modules\Bookings\Part;
use Exception;

class InvoiceItemObserver extends AbstractInvoiceObserver
{
    public function created($model)
    {
        // $this->calculateTotalPrice($model->invoice, true);
        $this->createTransaction($model, 'create');
    }

    public function updated($model)
    {
        $this->createTransaction($model, 'update');
    }

    public function deleting($model)
    {
        $this->createTransaction($model, 'delete');
    }

    /**
     *	Creates a Transaction for this purchased item.
     *
     * @return void
     */
    protected function createTransaction($model, $action)
    {
        if (! $model->stock_id) {
            return;
        }

        if ( $model->ignore_stock ) {
            // Delete any transactions that were created for this invoice item
            return $this->deleteTransactions($model);
        }

        //If it is an invoice or sale that does not belong to a task
        if ($model->invoice->task_id == null) {
            return self::manageTransaction($model, $action);
        }

        //If belongs to a task
        $parts = Part::query()->where('related_id', $model->invoice->task_id)->where('stock_id', $model->stock_id)->get();
        $on_order = $parts->filter(function($part) {
            return $part->quantity_on_order > 0;
        });

        if ($on_order->count()) {
            throw new Exception('You cannot generate an invoice for a task that has parts on order.');
        }

        if ($parts->where('quantity_on_order', 0)->isEmpty()) {
            return self::manageTransaction($model, $action);
        }

        switch ($action) {
            case 'create':
                $part = $parts->where('quantity_on_order', 0)->first();

                if ($part->quantity < $model->quantity) {
                    StockTransaction::createOrUpdate('invoice', $model->invoice_id, $model->stock_id, ($model->quantity - $part->quantity) * -1);
                }
                break;

            case 'update':

                $transactions = StockTransaction::query()->where('related_type', $model->invoice->type)->where('related_id', $model->invoice_id)->where('stock_id', $model->stock_id)->get();
                foreach ($transactions as $transaction) {
                    $transaction->delete();
                }

                self::createTransaction($model, 'create');

                break;

            case 'delete':

                $transactions = StockTransaction::query()->where('related_type', $model->invoice->type)->where('related_id', $model->invoice_id)->where('stock_id', $model->stock_id)->get();
                foreach ($transactions as $transaction) {
                    $transaction->delete();
                }
                break;

        }
    }

    protected function manageTransaction($model, $action)
    {
        switch ($action) {
            case 'create':
                // Create transaction for the main stock item
                StockTransaction::createOrUpdate($model->invoice->type, $model->invoice_id, $model->stock_id, $model->quantity * -1);

                // If this is a composite/build item, also reserve component stock
                if ($model->stock && $model->stock->composite) {
                    $this->reserveComponentStock($model);
                }
                break;

            case 'update':
                $this->deleteTransactions($model);
                $this->deleteComponentTransactions($model);
                self::manageTransaction($model, 'create');
                break;

            case 'delete':
                $this->deleteTransactions($model);
                $this->deleteComponentTransactions($model);
                break;
        }
    }

    /**
     * Reserve component stock for composite/build items
     *
     * @param $model InvoiceItem
     * @return void
     */
    protected function reserveComponentStock($model)
    {
        if (!$model->stock || !$model->stock->composite) {
            return;
        }

        foreach ($model->stock->composite_items as $component) {
            $quantityNeeded = $component->pivot->quantity * $model->quantity;

            // Create negative transaction to reserve component stock
            StockTransaction::createOrUpdate(
                $model->invoice->type,
                $model->invoice_id,
                $component->id,
                $quantityNeeded * -1
            );
        }
    }

    /**
     * Delete component stock transactions for composite/build items
     *
     * @param $model InvoiceItem
     * @return void
     */
    protected function deleteComponentTransactions($model)
    {
        if (!$model->stock || !$model->stock->composite) {
            return;
        }

        foreach ($model->stock->composite_items as $component) {
            $transactions = StockTransaction::query()
                ->where('related_type', $model->invoice->type)
                ->where('related_id', $model->invoice_id)
                ->where('stock_id', $component->id)
                ->get();

            $transactions->each(function($transaction) {
                $transaction->delete();
            });
        }
    }

    protected function deleteTransactions($model)
    {
        $transactions = StockTransaction::query()->where('related_type', $model->invoice->type)->where('related_id', $model->invoice_id)->where('stock_id', $model->stock_id)->get();

        return $transactions->each(function($transaction) {
            $transaction->delete();
        });
    }
}
