<?php

namespace App\Modules\Stock;

use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use Illuminate\Support\Arr;

class StockControllerRESTv1 extends BaseController
{
    /**
     * Return all the records as an encoded JSON.
     *
     * @return JSON
     */
    public function all(Request $request)
    {
        $data = Stock::all();
        $total = count($data);

        $response = [
            'success'      => true,
            'data'         => $data,
            'total'        => $total,
            'current_page' => 1,
            'last_page'    => 1,
            'from'         => 1,
            'to'           => $total,
        ];

        return $response;
    }

    /**
     * Display a listing of the resource.
     *
     * @return Response
     */
    public function index(Request $request)
    {
        // Check to see if all the records were requested
        if ($request->input('all') && $request->input('all') != 'false') {
            return $this->all($request);
        }

        // For Products to order (requested=true), include actual task prices
        if ($request->input('requested') && $request->input('requested') != 'false') {
            $query = Stock::select([
                'stock.*',
                DB::raw('COALESCE(
                    (SELECT SUM(parts.price * parts.quantity) / SUM(parts.quantity) 
                     FROM parts 
                     WHERE parts.stock_id = stock.id 
                     AND parts.quantity_on_order > 0), 
                    stock.price_excluding_vat
                ) as effective_price_excluding_vat')
            ])->with('group', 'composite_items');
        } else {
            $query = Stock::select('*')->with('group', 'composite_items');
        }

        // If the request contains a search, use fuzzy search.
        if ($request->input('query')) {
            if (is_numeric($request->input('query')) && (strlen((string) $request->input('query')) == 12 || strlen((string) $request->input('query')) == 13)) {
                $query->where('model_number', $request->input('query'));
            } else {
                $query->where('name', 'like', "%" . $request->input('query') . "%");
            }
        }

        if ($request->input('type')) {
            $query->where('type', $request->input('type'));
        }

        if ($request->input('under_buffer') && $request->input('under_buffer') != 'false') {
            $query->whereRaw('quantity <  buffer')->whereRaw('buffer > 0')->where('unlimited', '!=', '1');
        }

        if ($request->input('ordered') && $request->input('ordered') != 'false') {
            $query->where('ordered_quantity', '>', 0);
        }

        if ($request->has('composite')) {
            $query->where('composite', $request->input('composite'));
        }

        if ($request->input('requested') && $request->input('requested') != 'false') {
            $query->where('quantity_to_order', '>', 0);
        }

        if ($request->input('in_stock') && $request->input('in_stock') != 'false') {
            $query->where(function ($q) {
                return $q->where('quantity', '>', 0)
                    ->orWhere('unlimited', 1);
            });
        }

        // nadeem added this discontinued section 17-1-17
        //By default hide discontinued. If we send the parameter show_discontinued, we show both
        if ($request->has('show_discontinued') && $request->input('show_discontinued') == 1) {
            // not filtering so it will show both continued and discontinued
        } else {
            $query->where('discontinued', 0);
        }

        if ($request->input('category') && $request->input('category') != 'false') {
            $query->where('group_id', $request->input('category'));
        }

        if ($request->input('reported_before', 0) && $request->input('reported_after')) {
            $reported_after = $request->input('reported_after');
            $reported_before = $request->input('reported_before');
            $query->whereHas('reports', function ($q) use ($reported_after, $reported_before) {
                $q->whereBetween('created_at', [$reported_after, $reported_before]);
            });
        } elseif ($request->input('reported_after', 0)) {
            $reported_after = $request->input('reported_after');
            $query->whereHas('reports', function ($q) use ($reported_after) {
                $q->where('created_at', '>', $reported_after);
            });
        } elseif ($request->input('reported_before', 0)) {
            $reported_before = $request->input('reported_before');
            $query->whereHas('reports', function ($q) use ($reported_before) {
                $q->where('created_at', '>', $reported_before);
            }, '<', 1);
        }

        $query = $query->paginate($request->input('limit', 20));

        $data = $query->getCollection()->toArray();
        
        // For Products to order, replace price_excluding_vat with effective_price_excluding_vat
        if ($request->input('requested') && $request->input('requested') != 'false') {
            foreach ($data as &$item) {
                if (isset($item['effective_price_excluding_vat'])) {
                    $item['price_excluding_vat'] = $item['effective_price_excluding_vat'];
                    unset($item['effective_price_excluding_vat']);
                }
            }
        }

        $response = [
            'success'      => true,
            'data'         => $data,
            'total'        => $query->total(),
            'current_page' => $query->currentPage(),
            'last_page'    => $query->lastPage(),
            'from'         => $query->firstItem(),
            'to'           => $query->lastItem(),
        ];

        return response()->json($response);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @return Response
     */
    public function store(\App\Http\Requests\StoreStockRequest $request)
    {
        $data = $request->all();

        if ($data['price_vat_included']) {
            $data['price_excluding_vat'] = number_format($data['price_excluding_vat'] / (1 + Config::get('settings.vat') / 100), 2, '.', '');
        } else {
            $data['price_excluding_vat'] = $data['price_excluding_vat'];
        }

        $stock = new Stock();
        $stock->fill($data);
        $stock->discontinued = $request->input('discontinued', 0);
        $stock->quantity = 0;
        if ($stock->save()) {
            try {
                // Check if the user is trying to add the stock item itself as a composite
                if (array_search($stock->id, Arr::pluck($request->input('composite_items', []), 'id')) !== false) {
                    return response()->json([
                        'error' => 'You cannot add the same stock as a composite item!',
                        'hint' => 'Remove the stock item from its own composite_items list.'
                    ], 400);
                }

                // Save composite items
                $composite_items = $this->getCompositeItemsFromInput($request);
                $stock->composite_items()->sync($composite_items);

                $stock->load('group', 'composite_items');

                return response()->json(['success' => true, 'data' => $stock]);
            } catch (\Exception $e) {
                return response()->json([
                    'error' => $e->getMessage(),
                    'hint' => 'If you are adding composite items, make sure composite_items is a non-empty array and each item has an id and a pivot quantity.'
                ], 400);
            }
        } else {
            return response()->json(['errors' => $stock->errors->all()], 400);
        }
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function show($id)
    {
        $stock = Stock::withTrashed()->find($id);

        if (!$stock) {
            return response()->json(['error' => 'Stock not found'], 400);
        }

        $stock->load('group');

        return response()->json($stock->toArray());
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function update(Request $request, $id)
    {
        $data = $request->all();

        if ($data['price_vat_included']) {
            $data['price_excluding_vat'] = number_format($data['price_excluding_vat'] / (1 + Config::get('settings.vat') / 100), 2, '.', '');
        }

        $stock = Stock::find($id);

        // Do not allow to modify the stock quantity
        if ($request->has('quantity') && $request->input('quantity') != $stock->quantity) {
            throw new NotAcceptableHttpException('The quantity can not be update.');
        }

        $stock->fill($data);
        $stock->discontinued = $request->input('discontinued', 0);

        return DB::transaction(function () use (&$stock, $request) {
            if ($stock->save()) {
                try {
                    // Check if the user is trying to add the stock item itself as a composite
                    $composite_items_input = $request->input('composite_items', []);
                    if (is_array($composite_items_input) && !empty($composite_items_input)) {
                        $item_ids = [];
                        foreach ($composite_items_input as $item) {
                            if (is_array($item) && isset($item['id'])) {
                                $item_ids[] = $item['id'];
                            }
                        }
                        if (in_array($stock->id, $item_ids)) {
                            return response()->json([
                                'error' => 'You cannot add the same stock as a composite item!',
                                'hint' => 'Remove the stock item from its own composite_items list.'
                            ], 400);
                        }
                    }

                    // Save composite items
                    $composite_items = $this->getCompositeItemsFromInput($request);
                    if ($stock->composite && !empty($composite_items) && !Stock::sameItems($stock, $composite_items)) {
                        $old_items = [];
                        foreach ($stock->composite_items as $item) {
                            $old_items[$item->id] = ['quantity' => $item->pivot->quantity];
                        }

                        $stock->composite_items()->sync($composite_items);
                        $stock->load('composite_items');

                        $new_items = [];
                        foreach ($stock->composite_items as $item) {
                            $new_items[$item->id] = ['quantity' => $item->pivot->quantity];
                        }

                        Event::dispatch('StockItemsModified', [$stock, $old_items, $new_items]);
                    } elseif ($stock->composite && empty($composite_items)) {
                        // Handle case where all items are removed
                        $old_items = [];
                        foreach ($stock->composite_items as $item) {
                            $old_items[$item->id] = ['quantity' => $item->pivot->quantity];
                        }
                        
                        $stock->composite_items()->sync([]);
                        $stock->load('composite_items');
                        
                        Event::dispatch('StockItemsModified', [$stock, $old_items, []]);
                    }

                    $stock->load('group', 'composite_items');

                    return response()->json(['success' => true, 'data' => $stock->toArray()]);
                } catch (\Exception $e) {
                    return response()->json([
                        'error' => $e->getMessage(),
                        'hint' => 'If you are adding composite items, make sure composite_items is a non-empty array and each item has an id and a pivot quantity.'
                    ], 400);
                }
            } else {
                return response()->json(['errors' => $stock->errors->all()], 400);
            }
        });
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function destroy($id)
    {
        $stock = Stock::findOrFail($id);
        $stock->delete();

        return $stock;
    }

    /**
     * Plucks all the composite items from the request input.
     *
     * @return array
     */
    protected function getCompositeItemsFromInput(Request $request)
    {
        $composite_items = [];

        // Log the entire request data for debugging
        \Log::info("getCompositeItemsFromInput called", [
            'composite' => $request->input('composite'),
            'composite_items' => $request->input('composite_items'),
            'all_input' => $request->all()
        ]);

        if ($request->input('composite')) {
            $items = $request->input('composite_items', []);
            if (!is_array($items) || empty($items)) {
                throw new \Exception('The composite_items field is required and must be a non-empty array when composite is set.', 400);
            }
            foreach ($items as $key => $value) {
                // Log each iteration for debugging
                \Log::info("Processing composite item", [
                    'key' => $key,
                    'value' => $value,
                    'type' => gettype($value)
                ]);

                // Skip null, empty, or invalid values that might occur when items are removed
                if ($value === null || $value === '' || $value === false) {
                    \Log::info("Skipping null/empty value", ['key' => $key]);
                    continue;
                }
                
                // Handle both indexed arrays and associative arrays
                if (is_numeric($key) && is_array($value) && isset($value['id'])) {
                    // Standard format: [{'id': 123, 'pivot': {'quantity': 5}}]
                    // Check if pivot exists and has quantity, or if quantity is directly available
                    if (isset($value['pivot']['quantity'])) {
                        $composite_items[$value['id']] = ['quantity' => $value['pivot']['quantity']];
                        \Log::info("Added standard format item", ['id' => $value['id'], 'quantity' => $value['pivot']['quantity']]);
                    } elseif (isset($value['quantity'])) {
                        $composite_items[$value['id']] = ['quantity' => $value['quantity']];
                        \Log::info("Added standard format item with direct quantity", ['id' => $value['id'], 'quantity' => $value['quantity']]);
                    } else {
                        \Log::warning("Skipping item with missing quantity", ['item' => $value]);
                        continue;
                    }
                } elseif (!is_numeric($key) && is_array($value) && isset($value['quantity'])) {
                    // Alternative format: {'5600': {'quantity': 5}}
                    $composite_items[$key] = ['quantity' => $value['quantity']];
                    \Log::info("Added alternative format item", ['key' => $key, 'quantity' => $value['quantity']]);
                } elseif (!is_numeric($key) && is_numeric($value) && $value > 0) {
                    // Simple format: {'5600': 5} - only include if quantity is positive
                    $composite_items[$key] = ['quantity' => $value];
                    \Log::info("Added simple format item", ['key' => $key, 'quantity' => $value]);
                } elseif (is_numeric($key) && is_numeric($value) && $value > 0) {
                    // Handle numeric indexed array with numeric values: [123 => 5]
                    $composite_items[$key] = ['quantity' => $value];
                    \Log::info("Added numeric indexed item", ['key' => $key, 'quantity' => $value]);
                } else {
                    // Log the problematic data for debugging but don't throw exception
                    \Log::warning("Skipping invalid composite item data", [
                        'key' => $key,
                        'value' => $value,
                        'type' => gettype($value),
                        'is_array' => is_array($value),
                        'is_numeric_key' => is_numeric($key),
                        'has_id' => is_array($value) ? isset($value['id']) : false,
                        'has_pivot' => is_array($value) && isset($value['pivot']) ? true : false,
                        'has_quantity' => is_array($value) ? isset($value['quantity']) : false
                    ]);
                    continue;
                }
            }
        }

        \Log::info("Final composite_items result", ['composite_items' => $composite_items]);
        return $composite_items;
    }
}
