<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\InventoryOrder;
use App\Models\Contact;
use App\Models\InventoryItem;
use App\Models\Hotel;
use App\Models\InventoryOrderReceipt;
use App\Models\InventoryMovement;
use App\Models\Account;
use App\Models\JournalEntry;
use App\Models\JournalEntryItem;
use App\Http\Controllers\Concerns\ResolvesTenantHotel;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;

class PurchaseOrderController extends Controller
{
    use ResolvesTenantHotel;

    private function getHotelId()
    {
        return $this->tenantHotelIdOrAbort();
    }

    /**
     * Display a listing of purchase orders.
     */
    public function index(Request $request)
    {
        $hotelId = $this->getHotelId();
        $query = InventoryOrder::with(['item', 'vendor']);
        $query->where('hotel_id', $hotelId);
        
        // Search functionality
        if ($request->has('search') && !empty($request->search)) {
            $query->where(function($q) use ($request) {
                $q->whereHas('item', function($q2) use ($request) {
                    $q2->where('name', 'like', '%' . $request->search . '%');
                })
                ->orWhere('po_number', 'like', '%' . $request->search . '%');
            });
        }
        
        // Status filter
        if ($request->has('status') && !empty($request->status)) {
            $query->where('status', $request->status);
        }
        
        // Urgency filter
        if ($request->has('urgency') && !empty($request->urgency)) {
            $query->where('urgency', $request->urgency);
        }
        
        // Date range filter
        if ($request->has('date_from') && !empty($request->date_from)) {
            $query->whereDate('created_at', '>=', $request->date_from);
        }
        
        if ($request->has('date_to') && !empty($request->date_to)) {
            $query->whereDate('created_at', '<=', $request->date_to);
        }
        
        // Sorting
        $sort = $request->get('sort', 'created_at_desc');
        switch ($sort) {
            case 'date_asc':
                $query->orderBy('created_at', 'asc');
                break;
            case 'date_desc':
                $query->orderBy('created_at', 'desc');
                break;
            case 'delivery_asc':
                $query->orderBy('delivery_date', 'asc');
                break;
            case 'delivery_desc':
                $query->orderBy('delivery_date', 'desc');
                break;
            default:
                $query->orderBy('created_at', 'desc');
        }
        
        $orders = $query->paginate(15);
        
        // Statistics
        $stats = [
            'pending' => InventoryOrder::where('hotel_id', $hotelId)->where('status', 'pending')->count(),
            'ordered' => InventoryOrder::where('hotel_id', $hotelId)->where('status', 'ordered')->count(),
            'received' => InventoryOrder::where('hotel_id', $hotelId)->where('status', 'received')->count(),
            'cancelled' => InventoryOrder::where('hotel_id', $hotelId)->where('status', 'cancelled')->count(),
            'total' => InventoryOrder::where('hotel_id', $hotelId)->count(),
            'total_value' => InventoryOrder::where('hotel_id', $hotelId)->sum('total_amount')
        ];
        
        return view('purchase-orders.index', compact('orders', 'stats'));
    }

    /**
     * Show the form for creating a new purchase order.
     */
    public function create()
    {
        $hotelId = $this->getHotelId();
        $items = InventoryItem::where('hotel_id', $hotelId)->where('active', true)->orderBy('name')->get();
        $vendors = Contact::where('hotel_id', $hotelId)->where('type', 'vendor')->where('active', true)->orderBy('company')->get();
        
        // Generate PO number
        $poNumber = 'PO-' . date('Ymd') . '-' . str_pad(
            InventoryOrder::where('hotel_id', $hotelId)->count() + 1,
            4,
            '0',
            STR_PAD_LEFT
        );
        
        return view('purchase-orders.create', compact('items', 'vendors', 'poNumber'));
    }

    /**
     * Store a newly created purchase order.
     */
    public function store(Request $request)
    {
        $hotelId = $this->getHotelId();
        DB::beginTransaction();
        try {
            // Multi-line order submission from inventory/order flow
            if (is_array($request->input('items')) && count($request->input('items')) > 0) {
                $validated = $request->validate([
                    'vendor_id' => [
                        'required',
                        Rule::exists('contacts', 'id')->where(function ($query) use ($hotelId) {
                            $query->where('hotel_id', $hotelId);
                        }),
                    ],
                    'urgency' => 'required|in:low,normal,high,urgent',
                    'delivery_date' => 'required|date',
                    'notes' => 'nullable|string',
                    'terms' => 'nullable|string',
                    'items' => 'required|array|min:1',
                    'items.*.quantity' => 'required|numeric|min:0.001',
                    'items.*.unit_price' => 'required|numeric|min:0',
                ]);

                $createdOrders = [];
                $baseNumber = $request->input('po_number') ?: ('PO-' . date('Ymd'));
                $index = 1;
                foreach ($validated['items'] as $itemId => $line) {
                    $item = InventoryItem::where('hotel_id', $hotelId)->find($itemId);
                    if (!$item) {
                        continue;
                    }

                    $conversionFactor = (float) ($item->unit_conversion_factor ?: 1);
                    $purchaseQty = (float) $line['quantity'];
                    $stockQty = max(1, (int) round($purchaseQty * $conversionFactor));
                    $totalAmount = $purchaseQty * (float) $line['unit_price'];

                    $poNumber = $baseNumber . '-' . str_pad((string) $index, 2, '0', STR_PAD_LEFT);
                    while (InventoryOrder::where('hotel_id', $hotelId)->where('po_number', $poNumber)->exists()) {
                        $index++;
                        $poNumber = $baseNumber . '-' . str_pad((string) $index, 2, '0', STR_PAD_LEFT);
                    }

                    $createdOrders[] = InventoryOrder::create([
                        'hotel_id' => $hotelId,
                        'po_number' => $poNumber,
                        'item_id' => $item->id,
                        'vendor_id' => $validated['vendor_id'],
                        'quantity' => $stockQty,
                        'purchase_quantity' => $purchaseQty,
                        'purchase_unit' => $item->purchase_unit ?: $item->unit,
                        'stock_unit' => $item->unit,
                        'unit_conversion_factor' => $conversionFactor,
                        'unit_price' => $line['unit_price'],
                        'total_amount' => $totalAmount,
                        'urgency' => $validated['urgency'],
                        'delivery_date' => $validated['delivery_date'],
                        'notes' => $validated['notes'] ?? null,
                        'terms' => $validated['terms'] ?? null,
                        'status' => 'pending',
                        'ordered_by' => auth()->id(),
                    ]);
                    $this->logMovement(
                        $hotelId,
                        $item,
                        'adjustment',
                        (int) $item->quantity,
                        0,
                        (int) $item->quantity,
                        $poNumber,
                        'PO created',
                        'Purchase order line created (awaiting delivery)'
                    );
                    $index++;
                }

                DB::commit();
                if (count($createdOrders) === 1) {
                    return redirect()->route('purchase-orders.show', $createdOrders[0])
                        ->with('success', 'Purchase order created successfully!');
                }

                return redirect()->route('purchase-orders.index')
                    ->with('success', count($createdOrders) . ' purchase orders created successfully from selected items.')
                    ->with('created_po_numbers', collect($createdOrders)->pluck('po_number')->values()->all());
            }

            // Single-line PO submission from purchase-orders/create
            $validated = $request->validate([
                'po_number' => [
                    'required',
                    Rule::unique('inventory_orders', 'po_number')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
                'item_id' => [
                    'required',
                    Rule::exists('inventory_items', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
                'vendor_id' => [
                    'required',
                    Rule::exists('contacts', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
                'quantity' => 'required|integer|min:1',
                'purchase_quantity' => 'nullable|numeric|min:0.001',
                'purchase_unit' => 'nullable|string|max:50',
                'stock_unit' => 'nullable|string|max:50',
                'unit_conversion_factor' => 'nullable|numeric|min:0.001',
                'unit_price' => 'required|numeric|min:0',
                'urgency' => 'required|in:low,normal,high,urgent',
                'delivery_date' => 'required|date',
                'notes' => 'nullable|string',
                'terms' => 'nullable|string'
            ]);

            $conversionFactor = (float) ($validated['unit_conversion_factor'] ?? 1);
            if ($conversionFactor <= 0) {
                $conversionFactor = 1;
            }
            $purchaseQty = (float) ($validated['purchase_quantity'] ?? $validated['quantity']);
            $stockQty = (int) round($purchaseQty * $conversionFactor);
            if ($stockQty < 1) {
                $stockQty = 1;
            }
            $totalAmount = $purchaseQty * (float) $validated['unit_price'];

            $order = InventoryOrder::create([
                'hotel_id' => $hotelId,
                'po_number' => $validated['po_number'],
                'item_id' => $validated['item_id'],
                'vendor_id' => $validated['vendor_id'],
                'quantity' => $stockQty,
                'purchase_quantity' => $purchaseQty,
                'purchase_unit' => $validated['purchase_unit'] ?? null,
                'stock_unit' => $validated['stock_unit'] ?? null,
                'unit_conversion_factor' => $conversionFactor,
                'unit_price' => $validated['unit_price'],
                'total_amount' => $totalAmount,
                'urgency' => $validated['urgency'],
                'delivery_date' => $validated['delivery_date'],
                'notes' => $validated['notes'],
                'terms' => $validated['terms'],
                'status' => 'pending',
                'ordered_by' => auth()->id()
            ]);
            $item = InventoryItem::where('hotel_id', $hotelId)->findOrFail($validated['item_id']);
            $this->logMovement(
                $hotelId,
                $item,
                'adjustment',
                (int) $item->quantity,
                0,
                (int) $item->quantity,
                $order->po_number,
                'PO created',
                'Purchase order created (awaiting delivery)'
            );

            DB::commit();
            return redirect()->route('purchase-orders.show', $order)
                ->with('success', 'Purchase order created successfully!');
        } catch (\Exception $e) {
            DB::rollBack();
            return redirect()->back()
                ->with('error', 'Failed to create purchase order: ' . $e->getMessage())
                ->withInput();
        }
    }

    /**
     * Display the specified purchase order.
     */
    public function show(InventoryOrder $purchaseOrder)
    {
        abort_unless($purchaseOrder->hotel_id == $this->getHotelId(), 404);
        $purchaseOrder->load(['item', 'vendor', 'orderedBy', 'receipts.receiver']);
        $hotel = Hotel::find($this->getHotelId());
        return view('purchase-orders.show', compact('purchaseOrder', 'hotel'));
    }

    /**
     * Show the form for editing the specified purchase order.
     */
    public function edit(InventoryOrder $purchaseOrder)
    {
        $hotelId = $this->getHotelId();
        abort_unless($purchaseOrder->hotel_id == $hotelId, 404);

        $items = InventoryItem::where('hotel_id', $hotelId)->where('active', true)->orderBy('name')->get();
        $vendors = Contact::where('hotel_id', $hotelId)->where('type', 'vendor')->where('active', true)->orderBy('company')->get();
        
        return view('purchase-orders.edit', compact('purchaseOrder', 'items', 'vendors'));
    }

    /**
     * Update the specified purchase order.
     */
    public function update(Request $request, InventoryOrder $purchaseOrder)
    {
        $hotelId = $this->getHotelId();
        abort_unless($purchaseOrder->hotel_id == $hotelId, 404);

        $validated = $request->validate([
            'item_id' => [
                'required',
                Rule::exists('inventory_items', 'id')->where(function ($query) use ($hotelId) {
                    $query->where('hotel_id', $hotelId);
                }),
            ],
            'vendor_id' => [
                'required',
                Rule::exists('contacts', 'id')->where(function ($query) use ($hotelId) {
                    $query->where('hotel_id', $hotelId);
                }),
            ],
            'quantity' => 'required|integer|min:1',
            'unit_price' => 'required|numeric|min:0',
            'total_amount' => 'required|numeric|min:0',
            'urgency' => 'required|in:low,normal,high,urgent',
            'delivery_date' => 'required|date',
            'notes' => 'nullable|string',
            'terms' => 'nullable|string',
            'status' => 'required|in:pending,ordered,approved,authorized,received,delivered,cancelled'
        ]);
        $validated['status'] = match ($validated['status']) {
            'authorized', 'approved' => 'ordered',
            'delivered' => 'received',
            default => $validated['status'],
        };
        if (
            $validated['status'] === 'received'
            && (int) ($purchaseOrder->received_quantity ?? 0) < (int) $purchaseOrder->quantity
        ) {
            return redirect()->back()->with('error', 'Cannot mark as received before full receipt posting. Use Post Delivery (GRN).');
        }
        
        DB::beginTransaction();
        
        try {
            $oldStatus = $purchaseOrder->status;
            $purchaseOrder->update($validated);

            // Post accounting expense when PO is authorized (ordered)
            if ($purchaseOrder->wasChanged('status') && $validated['status'] === 'ordered') {
                $this->postAuthorizedPurchaseOrderExpense($purchaseOrder, $hotelId);
            }
            
            // Legacy fallback: only increment full quantity if no receipt records exist.
            if (
                $purchaseOrder->wasChanged('status')
                && $validated['status'] == 'received'
                && (int) ($purchaseOrder->received_quantity ?? 0) === 0
            ) {
                $item = $purchaseOrder->item;
                $beforeQty = (int) $item->quantity;
                $item->increment('quantity', $purchaseOrder->quantity);
                $afterQty = $beforeQty + (int) $purchaseOrder->quantity;
                $purchaseOrder->update([
                    'received_quantity' => $purchaseOrder->quantity,
                    'received_date' => now(),
                ]);
                $this->logMovement($hotelId, $item, 'in', $beforeQty, (int) $purchaseOrder->quantity, $afterQty, $purchaseOrder->po_number, 'Legacy full receive', 'Status changed to received');
            }

            if ($purchaseOrder->wasChanged('status')) {
                $item = $purchaseOrder->item;
                $this->logMovement(
                    $hotelId,
                    $item,
                    'adjustment',
                    (int) $item->quantity,
                    0,
                    (int) $item->quantity,
                    $purchaseOrder->po_number,
                    'PO status updated',
                    'Status changed from ' . $oldStatus . ' to ' . $purchaseOrder->status
                );
            }
            
            DB::commit();
            
            return redirect()->route('purchase-orders.show', $purchaseOrder)
                ->with('success', 'Purchase order updated successfully!');
                
        } catch (\Exception $e) {
            DB::rollBack();
            return redirect()->back()
                ->with('error', 'Failed to update purchase order: ' . $e->getMessage())
                ->withInput();
        }
    }

    /**
     * Update purchase order status.
     */
    public function updateStatus(Request $request, InventoryOrder $purchaseOrder)
    {
        $hotelId = $this->getHotelId();
        abort_unless($purchaseOrder->hotel_id == $hotelId, 404);

        $validated = $request->validate([
            'status' => 'required|in:pending,ordered,approved,authorized,received,delivered,cancelled'
        ]);
        $validated['status'] = match ($validated['status']) {
            'authorized', 'approved' => 'ordered',
            'delivered' => 'received',
            default => $validated['status'],
        };
        if (
            $validated['status'] === 'received'
            && (int) ($purchaseOrder->received_quantity ?? 0) < (int) $purchaseOrder->quantity
        ) {
            return redirect()->back()->with('error', 'Cannot mark as received before full receipt posting. Use Post Delivery (GRN).');
        }
        
        DB::beginTransaction();
        
        try {
            $oldStatus = $purchaseOrder->status;
            $newStatus = $validated['status'];
            
            $purchaseOrder->update(['status' => $newStatus]);

            // Post accounting expense when PO is authorized (ordered)
            if ($oldStatus !== 'ordered' && $newStatus === 'ordered') {
                $this->postAuthorizedPurchaseOrderExpense($purchaseOrder, $hotelId);
            }
            
            // Legacy fallback: only increment full quantity if no receipts are tracked.
            if ($oldStatus != 'received' && $newStatus == 'received' && (int) ($purchaseOrder->received_quantity ?? 0) === 0) {
                $item = $purchaseOrder->item;
                $beforeQty = (int) $item->quantity;
                $item->increment('quantity', $purchaseOrder->quantity);
                $afterQty = $beforeQty + (int) $purchaseOrder->quantity;
                $purchaseOrder->update([
                    'received_quantity' => $purchaseOrder->quantity,
                    'received_date' => now(),
                ]);
                $this->logMovement($hotelId, $item, 'in', $beforeQty, (int) $purchaseOrder->quantity, $afterQty, $purchaseOrder->po_number, 'Legacy full receive', 'Status changed to received');
            }
            
            // Reverse legacy fallback stock movement only when it was a full-status receive.
            if ($oldStatus == 'received' && $newStatus != 'received' && (int) ($purchaseOrder->received_quantity ?? 0) >= (int) $purchaseOrder->quantity) {
                $item = $purchaseOrder->item;
                $beforeQty = (int) $item->quantity;
                $item->decrement('quantity', $purchaseOrder->quantity);
                $afterQty = $beforeQty - (int) $purchaseOrder->quantity;
                $this->logMovement($hotelId, $item, 'out', $beforeQty, -1 * (int) $purchaseOrder->quantity, $afterQty, $purchaseOrder->po_number, 'Legacy receive reversal', 'Status moved away from received');
            }
            
            DB::commit();
            
            return redirect()->back()
                ->with('success', 'Purchase order status updated successfully!');
                
        } catch (\Exception $e) {
            DB::rollBack();
            return redirect()->back()
                ->with('error', 'Failed to update status: ' . $e->getMessage());
        }
    }

    /**
     * Generate PDF for purchase order.
     */
    public function generatePdf(InventoryOrder $purchaseOrder)
    {
        abort_unless($purchaseOrder->hotel_id == $this->getHotelId(), 404);
        $purchaseOrder->load(['item', 'vendor', 'orderedBy']);
        $hotel = Hotel::find($this->getHotelId());
        return view('purchase-orders.pdf', compact('purchaseOrder', 'hotel'));
    }

    /**
     * Export purchase orders.
     */
    public function export(Request $request, $format = 'csv')
    {
        $orders = InventoryOrder::with(['item', 'vendor'])->get();
        
        if ($format === 'csv') {
            return $this->exportOrdersToCsv($orders);
        }

        // Graceful fallback until dedicated templates/exporters are added.
        if (in_array($format, ['pdf', 'excel'], true)) {
            return $this->exportOrdersToCsv($orders);
        }
        
        return redirect()->back()->with('error', 'Export format not supported');
    }

    /**
     * Route compatibility wrapper for /purchase-orders/export/csv.
     */
    public function exportCsv(Request $request)
    {
        return $this->export($request, 'csv');
    }

    /**
     * Route compatibility wrapper for /purchase-orders/export/pdf.
     */
    public function exportPdf(Request $request)
    {
        return $this->export($request, 'pdf');
    }

    /**
     * Route compatibility wrapper for /purchase-orders/export/excel.
     */
    public function exportExcel(Request $request)
    {
        return $this->export($request, 'excel');
    }

    /**
     * Route compatibility wrapper for /purchase-orders/{purchaseOrder}/pdf.
     */
    public function pdf(InventoryOrder $purchaseOrder)
    {
        return $this->generatePdf($purchaseOrder);
    }

    /**
     * Route compatibility wrapper for /purchase-orders/{purchaseOrder}/print.
     */
    public function print(InventoryOrder $purchaseOrder)
    {
        return $this->show($purchaseOrder);
    }

    /**
     * Receive delivered goods (supports partial receiving).
     */
    public function receiveDelivery(Request $request, InventoryOrder $purchaseOrder)
    {
        $hotelId = $this->getHotelId();
        abort_unless($purchaseOrder->hotel_id == $hotelId, 404);

        $validated = $request->validate([
            'accepted_quantity' => 'required|integer|min:0',
            'rejected_quantity' => 'nullable|integer|min:0',
            'damaged_quantity' => 'nullable|integer|min:0',
            'delivery_note_number' => 'nullable|string|max:100',
            'invoice_number' => 'nullable|string|max:100',
            'notes' => 'nullable|string',
        ]);

        $accepted = (int) $validated['accepted_quantity'];
        $rejected = (int) ($validated['rejected_quantity'] ?? 0);
        $damaged = (int) ($validated['damaged_quantity'] ?? 0);
        $batchTotal = $accepted + $rejected + $damaged;

        if ($batchTotal <= 0) {
            return redirect()->back()->with('error', 'Enter at least one quantity to receive/reject/damage.');
        }

        DB::beginTransaction();
        try {
            $alreadyReceived = (int) ($purchaseOrder->received_quantity ?? 0);
            $remaining = max(0, (int) $purchaseOrder->quantity - $alreadyReceived);

            if ($batchTotal > $remaining) {
                throw new \Exception('Received batch exceeds remaining quantity. Remaining: ' . $remaining);
            }

            InventoryOrderReceipt::create([
                'hotel_id' => $hotelId,
                'inventory_order_id' => $purchaseOrder->id,
                'inventory_item_id' => $purchaseOrder->item_id,
                'accepted_quantity' => $accepted,
                'rejected_quantity' => $rejected,
                'damaged_quantity' => $damaged,
                'delivery_note_number' => $validated['delivery_note_number'] ?? null,
                'invoice_number' => $validated['invoice_number'] ?? null,
                'notes' => $validated['notes'] ?? null,
                'received_by' => auth()->id(),
                'received_at' => now(),
            ]);

            if ($accepted > 0) {
                $item = $purchaseOrder->item;
                $beforeQty = (int) $item->quantity;
                $item->increment('quantity', $accepted);
                $afterQty = $beforeQty + $accepted;
                $this->logMovement($hotelId, $item, 'in', $beforeQty, $accepted, $afterQty, $purchaseOrder->po_number, 'Delivery receipt', $validated['notes'] ?? null);
            }
            if ($rejected > 0 || $damaged > 0) {
                $item = $purchaseOrder->item;
                $this->logMovement(
                    $hotelId,
                    $item,
                    'return',
                    (int) $item->quantity,
                    0,
                    (int) $item->quantity,
                    $purchaseOrder->po_number,
                    'Delivery exception',
                    'Rejected: ' . $rejected . ', Damaged: ' . $damaged
                );
            }

            $newReceivedTotal = $alreadyReceived + $batchTotal;
            $newStatus = $newReceivedTotal >= (int) $purchaseOrder->quantity ? 'received' : 'ordered';

            $purchaseOrder->update([
                'received_quantity' => $newReceivedTotal,
                'received_date' => now(),
                'invoice_number' => $validated['invoice_number'] ?? $purchaseOrder->invoice_number,
                'status' => $newStatus,
            ]);

            DB::commit();
            return redirect()->back()->with('success', 'Delivery posted successfully. Stock updated for accepted quantity.');
        } catch (\Exception $e) {
            DB::rollBack();
            return redirect()->back()->with('error', 'Failed to post delivery: ' . $e->getMessage())->withInput();
        }
    }

    /**
     * Mark rejected/damaged quantities as returned to vendor.
     */
    public function returnRejected(Request $request, InventoryOrder $purchaseOrder)
    {
        $hotelId = $this->getHotelId();
        abort_unless($purchaseOrder->hotel_id == $hotelId, 404);

        $validated = $request->validate([
            'return_quantity' => 'required|integer|min:1',
            'return_note' => 'nullable|string|max:500',
        ]);

        $totals = $purchaseOrder->receipts()
            ->selectRaw('COALESCE(SUM(rejected_quantity),0) as rejected_total, COALESCE(SUM(damaged_quantity),0) as damaged_total')
            ->first();
        $returnable = ((int) $totals->rejected_total + (int) $totals->damaged_total) - (int) ($purchaseOrder->returned_quantity ?? 0);
        if ($validated['return_quantity'] > $returnable) {
            return redirect()->back()->with('error', 'Return quantity exceeds available rejected/damaged quantity (' . $returnable . ').');
        }

        $newReturned = (int) ($purchaseOrder->returned_quantity ?? 0) + (int) $validated['return_quantity'];
        $note = trim((string) ($validated['return_note'] ?? ''));
        $append = 'Return note: ' . ($note !== '' ? $note : 'Returned to vendor');

        $purchaseOrder->update([
            'returned_quantity' => $newReturned,
            'returned_at' => now(),
            'notes' => trim(((string) $purchaseOrder->notes . "\n" . $append)),
        ]);
        $item = $purchaseOrder->item;
        $this->logMovement(
            $hotelId,
            $item,
            'return',
            (int) $item->quantity,
            0,
            (int) $item->quantity,
            $purchaseOrder->po_number,
            'Return to vendor',
            'Returned quantity: ' . (int) $validated['return_quantity'] . '. ' . ($validated['return_note'] ?? '')
        );

        return redirect()->back()->with('success', 'Return to vendor recorded successfully.');
    }

    private function logMovement(
        int $hotelId,
        InventoryItem $item,
        string $type,
        int $before,
        int $change,
        int $after,
        ?string $reference = null,
        ?string $reason = null,
        ?string $notes = null
    ): void {
        InventoryMovement::create([
            'hotel_id' => $hotelId,
            'inventory_item_id' => $item->id,
            'movement_type' => $type,
            'source_type' => 'purchase_order',
            'source_id' => null,
            'quantity_before' => $before,
            'quantity_change' => $change,
            'quantity_after' => $after,
            'reference_number' => $reference,
            'reason' => $reason,
            'notes' => $notes,
            'performed_by' => auth()->id(),
            'performed_at' => now(),
        ]);
    }
    
    /**
     * Export to CSV
     */
    private function exportOrdersToCsv($orders)
    {
        $fileName = 'purchase_orders_' . date('Y-m-d') . '.csv';
        $headers = [
            'Content-Type' => 'text/csv',
            'Content-Disposition' => 'attachment; filename="' . $fileName . '"',
        ];

        $callback = function() use ($orders) {
            $file = fopen('php://output', 'w');
            
            // Add CSV headers
            fputcsv($file, [
                'PO Number', 
                'Item', 
                'Vendor', 
                'Quantity', 
                'Unit Price (UGX)', 
                'Total Amount (UGX)', 
                'Urgency',
                'Status',
                'Order Date',
                'Delivery Date'
            ]);

            // Add data rows
            foreach ($orders as $order) {
                fputcsv($file, [
                    $order->po_number,
                    $order->item->name,
                    $order->vendor->company,
                    $order->quantity,
                    $order->unit_price,
                    $order->total_amount,
                    ucfirst($order->urgency),
                    ucfirst($order->status),
                    $order->created_at->format('Y-m-d'),
                    $order->delivery_date ? $order->delivery_date->format('Y-m-d') : 'N/A'
                ]);
            }

            fclose($file);
        };

        return response()->stream($callback, 200, $headers);
    }

    /**
     * Post PO authorization as accounting expense (debit expense, credit payable).
     */
    private function postAuthorizedPurchaseOrderExpense(InventoryOrder $purchaseOrder, int $hotelId): void
    {
        if ($purchaseOrder->accounting_synced) {
            return;
        }

        $expenseAccount = $this->resolveOrCreateAccount($hotelId, ['5600'], [
            'code' => '5600',
            'name' => 'Administrative Expenses',
            'l1_statement' => 'Income Statement',
            'l2_department' => 'Administration',
            'l3_group' => 'Operating Expenses',
            'l4_sub_group' => 'Administrative',
            'account_type' => 'P',
            'normal_balance' => 'Dr',
            'category' => 'Expense',
            'pl_line' => 'Operating Expenses',
            'report_section' => 'Expenses',
        ]);

        // Prefer trade creditors; fallback to VAT payable if that's the configured payable bucket.
        $payableAccount = $this->resolveOrCreateAccount($hotelId, ['12211', '12251'], [
            'code' => '12211',
            'name' => 'Trade Creditors',
            'l1_statement' => 'Balance Sheet',
            'l2_department' => 'Liabilities',
            'l3_group' => 'Current Liabilities',
            'l4_sub_group' => 'Payables',
            'account_type' => 'P',
            'normal_balance' => 'Cr',
            'category' => 'Liability',
            'pl_line' => 'Current Liabilities',
            'report_section' => 'Liabilities',
        ]);

        $journalEntry = JournalEntry::create([
            'hotel_id' => $hotelId,
            'date' => now()->toDateString(),
            'entry_number' => JournalEntry::generateEntryNumber($hotelId),
            'description' => "Authorized purchase order #{$purchaseOrder->po_number}",
            'reference' => $purchaseOrder->po_number,
            'total_amount' => $purchaseOrder->total_amount,
            'status' => JournalEntry::STATUS_POSTED,
            'module_source' => 'purchase_order',
            'source_id' => $purchaseOrder->id,
            'created_by' => auth()->id() ?? 1,
            'posted_at' => now(),
        ]);

        JournalEntryItem::create([
            'hotel_id' => $hotelId,
            'journal_entry_id' => $journalEntry->id,
            'account_id' => $expenseAccount->id,
            'debit' => $purchaseOrder->total_amount,
            'credit' => 0,
            'description' => 'PO authorized expense',
        ]);

        JournalEntryItem::create([
            'hotel_id' => $hotelId,
            'journal_entry_id' => $journalEntry->id,
            'account_id' => $payableAccount->id,
            'debit' => 0,
            'credit' => $purchaseOrder->total_amount,
            'description' => 'PO payable',
        ]);

        $purchaseOrder->update([
            'accounting_synced' => true,
            'accounting_sync_at' => now(),
        ]);
    }

    /**
     * Resolve account by preferred codes; create fallback if missing.
     */
    private function resolveOrCreateAccount(int $hotelId, array $preferredCodes, array $fallbackData): Account
    {
        foreach ($preferredCodes as $code) {
            $found = Account::withoutGlobalScopes()
                ->where('hotel_id', $hotelId)
                ->where('code', $code)
                ->first();
            if ($found) {
                return $found;
            }
        }

        return Account::withoutGlobalScopes()->create(array_merge($fallbackData, [
            'hotel_id' => $hotelId,
            'is_active' => true,
        ]));
    }
}