<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Reservation;
use App\Models\Room;
use App\Models\Guest;
use App\Models\InOutLog;
use App\Models\User;
use App\Models\RoomType;
use App\Models\HousekeepingTask;
use App\Models\Invoice;
use App\Models\InvoiceItem;
use App\Models\Payment;
use App\Models\Setting;
use App\Models\GuestLedger;
use App\Models\Floor;
use App\Models\Receipt;
use App\Services\GuestLedgerService;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Session;
use Illuminate\Validation\Rule;
use App\Http\Controllers\Concerns\ResolvesTenantHotel;

class InOutController extends Controller
{
    use ResolvesTenantHotel;

    protected $ledgerService;

    public function __construct(GuestLedgerService $ledgerService)
    {
        $this->ledgerService = $ledgerService;
    }

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

    private function getConfiguredCheckoutTime(int $hotelId): string
    {
        return (string) Setting::getValue('checkout_time', $hotelId, '12:00');
    }

    private function buildCheckoutCutoffDateTime(Reservation $reservation, int $hotelId): Carbon
    {
        $checkoutDate = Carbon::parse($reservation->check_out)->format('Y-m-d');
        return Carbon::parse($checkoutDate . ' ' . $this->getConfiguredCheckoutTime($hotelId));
    }

    /**
     * Display the in/out log management page.
     */
    public function index()
    {
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                return redirect()->route('login')->with('error', 'Hotel ID not found. Please login again.');
            }
            
            // Get reservations that are pending, confirmed or checked_in
            $reservations = Reservation::with([
                'guest', 
                'room.roomType',
                'invoices.payments'
            ])
            ->where('hotel_id', $hotelId)
            ->whereIn('status', ['pending', 'confirmed', 'checked_in'])
            ->orderBy('created_at', 'desc')
            ->get()
            ->map(function ($reservation) use ($hotelId) {
                // Ensure guest data is properly loaded and formatted
                if ($reservation->guest) {
                    $reservation->guest->display_name = trim(
                        ($reservation->guest->first_name ?? '') . ' ' . 
                        ($reservation->guest->last_name ?? '')
                    ) ?: 'Guest #' . $reservation->id;
                    
                    $reservation->guest->name = $reservation->guest->display_name;
                } else {
                    // Create a dummy guest object if none exists
                    $reservation->guest = (object) [
                        'display_name' => 'Guest #' . $reservation->id,
                        'name' => 'Guest #' . $reservation->id,
                        'first_name' => null,
                        'last_name' => null,
                        'email' => null,
                        'phone' => null
                    ];
                }
                
                // CORRECT PAYMENT DATA FROM RESERVATION AND INVOICES
                // 1. Get the invoice first if it exists
                $invoice = $reservation->invoices->first();
                
                // 2. Determine total amount and paid amount accurately
                if ($invoice) {
                    $totalAmount = (float)($invoice->total_amount ?? 0);
                    $paidAmount = (float)($invoice->amount_paid ?? 0);
                    
                    // Fallback to recalculating payments if amount_paid is suspicious (0 but payments exist)
                    $invoicePayments = $invoice->payments()->whereIn('status', ['completed', 'paid'])->get();
                    if ($paidAmount <= 0 && $invoicePayments->count() > 0) {
                        $paidAmount = (float)$invoicePayments->sum('amount');
                    }
                } else {
                    $totalAmount = (float)($reservation->total_amount ?? 0);
                    $paidAmount = (float)($reservation->paid_amount ?? 0);
                    
                    // Final fallback to Payment model sum
                    if ($paidAmount <= 0) {
                        $paidAmount = (float)Payment::getTotalPaidForReservation($reservation->id);
                    }
                }
                
                $paymentHistory = [];
                $installmentCount = 0;
                
                if ($invoice) {
                    $allPayments = $invoice->payments()->orderBy('created_at', 'asc')->get();
                    foreach ($allPayments as $payment) {
                        $paymentHistory[] = [
                            'id' => $payment->id,
                            'amount' => (float)$payment->amount,
                            'method' => $payment->payment_method,
                            'date' => $payment->created_at->format('M j, Y'),
                            'time' => $payment->created_at->format('h:i A'),
                            'reference' => $payment->reference,
                            'notes' => $payment->notes
                        ];
                    }
                } 
                
                // If we still have no history but we HAVE a paid amount, look for payments linked to reservation
                if (empty($paymentHistory) && $paidAmount > 0) {
                    $directPayments = Payment::where('reservation_id', $reservation->id)
                        ->whereIn('status', ['completed', 'paid'])
                        ->orderBy('created_at', 'asc')
                        ->get();
                        
                    foreach ($directPayments as $payment) {
                        $paymentHistory[] = [
                            'id' => $payment->id,
                            'amount' => (float)$payment->amount,
                            'method' => $payment->payment_method,
                            'date' => $payment->created_at->format('M j, Y'),
                            'time' => $payment->created_at->format('h:i A'),
                            'reference' => $payment->reference,
                            'notes' => $payment->notes
                        ];
                    }
                    
                    // Fallback for cases with no explicit payment records but recorded paid_amount
                    if (empty($paymentHistory)) {
                        $paymentHistory[] = [
                            'id' => null,
                            'amount' => $paidAmount,
                            'method' => $reservation->payment_method ?? 'cash',
                            'date' => $reservation->created_at->format('M j, Y'),
                            'time' => $reservation->created_at->format('h:i A'),
                            'reference' => 'INITIAL',
                            'notes' => 'Recorded collection'
                        ];
                    }
                }
                
                $installmentCount = count($paymentHistory);
                
                // Calculate nights
                $checkIn = Carbon::parse($reservation->check_in);
                $checkOut = Carbon::parse($reservation->check_out);
                $nights = max(1, $checkIn->diffInDays($checkOut));
                $roomPrice = $reservation->room->price_per_night ?? 0;
                
                // Calculate room charges and VAT separately for display
                $roomCharges = $roomPrice * $nights;
                $vatRate = (float) Setting::getValue('tax_rate', $hotelId, 18);
                $vatAmount = ceil($roomCharges * $vatRate / 100);
                $subtotal = $roomCharges + $vatAmount;
                
                // Calculate overstay if checked in
                $overstayDays = 0;
                $overstayFees = 0;
                
                if ($reservation->status === 'checked_in') {
                    $today = Carbon::now();
                    $checkOutDateTime = $this->buildCheckoutCutoffDateTime($reservation, $hotelId);

                    if ($today->greaterThan($checkOutDateTime)) {
                        $overstayDays = Carbon::parse($today->toDateString())->diffInDays(Carbon::parse($checkOutDateTime->toDateString()));
                        $overstayFeePerDay = ceil($roomPrice * 1.2); // 20% extra for overstay
                        $overstayFees = $overstayDays * $overstayFeePerDay;
                    }
                }
                
                // Calculate total with overstay 
                // CRITICAL: If invoice exists, its total_amount ALREADY includes overstay from GuestLedgerService
                // only add overstay if we are NOT using the invoice amount or if the invoice doesn't have it yet
                $hasOverstayInInvoice = $invoice && $entry = GuestLedger::where('invoice_id', $invoice->id)->where('reference_type', 'overstay')->exists();
                
                if ($invoice && $hasOverstayInInvoice) {
                    $totalWithOverstay = $totalAmount;
                } else {
                    $totalWithOverstay = $totalAmount + $overstayFees;
                }
                
                $balance = max(0, $totalWithOverstay - $paidAmount);
                
                // Determine payment status
                $isFullyPaid = $balance <= 0 && $totalWithOverstay > 0;
                $isOverpaid = $paidAmount > $totalWithOverstay;
                $overpaidAmount = $isOverpaid ? ($paidAmount - $totalWithOverstay) : 0;
                
                // Attach calculated data to reservation
                $reservation->calculated_total = $totalWithOverstay;
                $reservation->calculated_paid = $paidAmount;
                $reservation->calculated_balance = $balance;
                $reservation->calculated_is_fully_paid = $isFullyPaid;
                $reservation->calculated_is_overpaid = $isOverpaid;
                $reservation->calculated_overpaid_amount = $overpaidAmount;
                $reservation->calculated_overstay_days = $overstayDays;
                $reservation->calculated_overstay_fees = $overstayFees;
                $reservation->calculated_payment_history = $paymentHistory;
                $reservation->calculated_installment_count = $installmentCount;
                $reservation->calculated_nights = $nights;
                $reservation->calculated_room_price = $roomPrice;
                $reservation->calculated_room_charges = $roomCharges;
                $reservation->calculated_vat_amount = $vatAmount;
                $reservation->calculated_subtotal = $subtotal;
                
                return $reservation;

            });

            // Get distinct categories and floors from rooms
            $categories = Room::where('hotel_id', $hotelId)->distinct()->pluck('category')->filter()->values()->toArray();
            $floors = Room::where('hotel_id', $hotelId)->distinct()->pluck('floor')->filter()->sort()->values()->toArray();

            // Get room types for filter dropdown
            $roomTypes = RoomType::where('hotel_id', $hotelId)->get();

            // Get available rooms count
            $occupiedRoomIds = Reservation::where('hotel_id', $hotelId)->where('status', 'checked_in')->pluck('room_id');
            $availableRoomsCount = Room::where('hotel_id', $hotelId)
                ->whereNotIn('id', $occupiedRoomIds)
                ->where('status', 'available')
                ->count();

            // Get recent history - FIXED: Added proper hotel filtering and null checks
            $historyRecords = InOutLog::with(['reservation.guest', 'reservation.room', 'processedBy'])
                ->whereHas('reservation', function($q) use ($hotelId) {
                    $q->where('hotel_id', $hotelId);
                })
                ->orderBy('logged_at', 'desc')
                ->take(50)
                ->get()
                ->map(function ($log) {
                    // Add null check for reservation
                    if (!$log->reservation) {
                        return (object) [
                            'created_at' => $log->logged_at,
                            'type' => $log->type,
                            'guest_name' => 'Guest (Reservation deleted)',
                            'room_number' => 'N/A',
                            'reservation_id' => 'N/A',
                            'is_walkin' => false,
                            'staff' => $log->processedBy->name ?? 'System',
                            'staff_name' => $log->processedBy->name ?? 'System',
                            'notes' => $log->notes,
                            'amount' => null,
                            'amount_paid' => null
                        ];
                    }
                    
                    $guestName = 'Guest';
                    $roomNumber = 'N/A';
                    $isWalkIn = false;
                    $reservationId = 'N/A';
                    $amount = null;
                    
                    if ($log->reservation) {
                        if ($log->reservation->guest) {
                            $firstName = $log->reservation->guest->first_name ?? '';
                            $lastName = $log->reservation->guest->last_name ?? '';
                            $guestName = trim("{$firstName} {$lastName}") ?: 'Guest';
                        }
                        
                        $roomNumber = $log->reservation->room->room_number ?? 'N/A';
                        $isWalkIn = $log->reservation->is_walk_in ?? false;
                        
                        if ($isWalkIn) {
                            $reservationId = $log->reservation->walk_in_number ?? 'WLK' . str_pad($log->reservation->id, 8, '0', STR_PAD_LEFT);
                        } else {
                            $reservationId = $log->reservation->reservation_number ?? 'RES' . str_pad($log->reservation->id, 8, '0', STR_PAD_LEFT);
                        }
                    }
                    
                    if ($log->type === 'payment') {
                        $amount = $log->amount_paid ?? 0;
                    }
                    
                    return (object) [
                        'created_at' => $log->logged_at,
                        'type' => $log->type,
                        'guest_name' => $guestName,
                        'room_number' => $roomNumber,
                        'reservation_id' => $reservationId,
                        'is_walkin' => $isWalkIn,
                        'staff' => $log->processedBy->name ?? 'System',
                        'staff_name' => $log->processedBy->name ?? 'System',
                        'notes' => $log->notes,
                        'amount' => $amount,
                        'amount_paid' => $amount
                    ];
                });

            // Check for pending receipts in session
            $showReceipt = session('show_receipt', false);
            $invoiceNumber = session('invoice_number');
            $reservationId = session('reservation_id');

            // Get hotel settings using Setting::getValue for multi-tenant safety
            $hotelName = \App\Models\Setting::getValue('hotel_name', $hotelId, hotelName());
            $hotelAddress = \App\Models\Setting::getValue('hotel_address', $hotelId, hotelAddress());
            $hotelPhone = \App\Models\Setting::getValue('hotel_phone', $hotelId, hotelPhone());
            $hotelEmail = \App\Models\Setting::getValue('hotel_email', $hotelId, hotelEmail());
            $vatRate = (float) \App\Models\Setting::getValue('tax_rate', $hotelId, 18);
            $currencySymbol = \App\Models\Setting::getValue('currency_symbol', $hotelId, 'UGX');

            return view('inout.index', [
                'reservations' => $reservations,
                'categories' => $categories,
                'floors' => $floors,
                'roomTypes' => $roomTypes,
                'availableRoomsCount' => $availableRoomsCount,
                'historyRecords' => $historyRecords,
                'showReceipt' => $showReceipt,
                'invoiceNumber' => $invoiceNumber,
                'reservationId' => $reservationId,
                'hotelName' => $hotelName,
                'hotelAddress' => $hotelAddress,
                'hotelPhone' => $hotelPhone,
                'hotelEmail' => $hotelEmail,
                'vatRate' => $vatRate,
                'currencySymbol' => $currencySymbol
            ]);

        } catch (\Exception $e) {
            Log::error('Error loading in/out page: ' . $e->getMessage(), [
                'hotel_id' => $this->getHotelId(),
                'trace' => $e->getTraceAsString()
            ]);
            
            return view('inout.index', [
                'reservations' => collect(),
                'categories' => [],
                'floors' => [],
                'roomTypes' => collect(),
                'availableRoomsCount' => 0,
                'historyRecords' => collect(),
                'showReceipt' => false,
                'invoiceNumber' => null,
                'reservationId' => null,
                'hotelName' => \App\Models\Setting::getValue('hotel_name', $hotelId, hotelName()),
                'hotelAddress' => \App\Models\Setting::getValue('hotel_address', $hotelId, hotelAddress()),
                'hotelPhone' => \App\Models\Setting::getValue('hotel_phone', $hotelId, hotelPhone()),
                'hotelEmail' => \App\Models\Setting::getValue('hotel_email', $hotelId, hotelEmail()),
                'vatRate' => (float) \App\Models\Setting::getValue('tax_rate', $hotelId, 18),
                'currencySymbol' => \App\Models\Setting::getValue('currency_symbol', $hotelId, 'UGX')
            ]);
        }
    }

    /**
     * Generate invoice for reservation
     */
    private function generateInvoice(Reservation $reservation, $paymentData = [])
    {
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                throw new \Exception('Hotel ID not found');
            }
            
            Log::info('Generating invoice for reservation ID: ' . $reservation->id);
            
            // Check if invoice already exists
            $existingInvoice = Invoice::where('reservation_id', $reservation->id)->first();
            if ($existingInvoice) {
                Log::info('Invoice already exists for reservation ' . $reservation->id);
                return $existingInvoice;
            }
            
            // Calculate nights
            $checkIn = Carbon::parse($reservation->check_in);
            $checkOut = Carbon::parse($reservation->check_out);
            $nights = max(1, $checkIn->diffInDays($checkOut));
            $roomPrice = $reservation->room->roomType->price_per_night ?? $reservation->room->price_per_night ?? 150000;
            
            // Calculate room charges correctly
            $roomCharges = $roomPrice * $nights;
            $vatRate = (float) setting('tax_rate', 18) / 100;
            $taxAmount = ceil($roomCharges * $vatRate);
            $totalWithTax = $roomCharges + $taxAmount;
            
            $paidAmount = $paymentData['amount_paid'] ?? 0;
            
            // Generate unique invoice number
            $invoiceNumber = $this->generateUniqueInvoiceNumber($reservation);
            
            // Create invoice
            $invoice = Invoice::create([
                'hotel_id' => $hotelId,
                'type' => 'room_charge',
                'invoice_number' => $invoiceNumber,
                'date' => now(),
                'due_date' => $reservation->check_out,
                'contact_type' => Guest::class,
                'contact_id' => $reservation->guest_id,
                'reservation_id' => $reservation->id,
                'total_amount' => $totalWithTax,
                'subtotal' => $roomCharges,
                'tax_amount' => $taxAmount,
                'status' => 'sent',
                'payment_status' => $paidAmount >= $totalWithTax ? 'paid' : ($paidAmount > 0 ? 'partially_paid' : 'pending'),
                'module_source' => 'front_desk',
                'source_id' => $reservation->id,
                'currency' => setting('currency_symbol', 'UGX'),
                'exchange_rate' => 1,
                'notes' => ($reservation->is_walk_in ? 'Walk-in guest' : 'Reservation'),
            ]);
            
            Log::info('Invoice generated: ' . $invoice->invoice_number . ' with total: ' . $totalWithTax);
            
            // Register room charge in the guest ledger
            $this->ledgerService->addCharge(
                $reservation->id,
                "Room Charge - Room {$reservation->room->room_number} ({$nights} nights)",
                'room_charge',
                $invoice->id,
                $roomCharges,
                $taxAmount
            );

            // Add payment if any amount was paid
            if ($paidAmount > 0) {
                $paymentMethod = $paymentData['payment_method'] ?? $reservation->payment_method ?? 'cash';
                $payment = Payment::create([
                    'hotel_id' => $hotelId,
                    'invoice_id' => $invoice->id,
                    'amount' => $paidAmount,
                    'payment_method' => $paymentMethod,
                    'reference' => 'PAY-' . time() . '-' . $reservation->id,
                    'notes' => 'Initial payment',
                    'status' => 'completed',
                    'paid_at' => now()
                ]);

                // Create ledger entry for initial payment
                $this->ledgerService->addPayment(
                    $reservation->id,
                    "Initial check-in payment",
                    $paymentMethod,
                    $paidAmount,
                    $payment->id
                );

                // Create receipt record
                $this->createReceiptRecord(
                    $invoice->id,
                    $reservation->guest_id,
                    $paidAmount,
                    $paymentMethod,
                    'Initial payment',
                    Auth::id()
                );
            }
            
            return $invoice;
            
        } catch (\Exception $e) {
            Log::error('Error generating invoice: ' . $e->getMessage());
            throw new \Exception('Failed to generate invoice: ' . $e->getMessage());
        }
    }

    /**
     * Generate a unique invoice number
     */
    private function generateUniqueInvoiceNumber(Reservation $reservation): string
    {
        $prefix = $reservation->is_walk_in ? 'WLK-INV-' : 'INV-';
        $datePrefix = date('Ymd');
        $invoiceNumber = $prefix . $datePrefix . '-' . str_pad($reservation->id, 6, '0', STR_PAD_LEFT);
        
        $counter = 1;
        while (Invoice::where('invoice_number', $invoiceNumber)->exists() && $counter < 10) {
            $invoiceNumber = $prefix . $datePrefix . '-' . str_pad($reservation->id, 4, '0', STR_PAD_LEFT) . '-' . $counter;
            $counter++;
        }
        
        return $invoiceNumber;
    }

    /**
     * Generate unique receipt number
     */
    private function generateUniqueReceiptNumber(): string
    {
        $prefix = 'RCPT';
        $date = date('Ymd');
        $random = rand(1000, 9999);
        $receiptNumber = $prefix . '-' . $date . '-' . $random;
        
        $attempts = 0;
        while (Receipt::where('hotel_id', $this->getHotelId())->where('receipt_number', $receiptNumber)->exists() && $attempts < 10) {
            $random = rand(1000, 9999);
            $receiptNumber = $prefix . '-' . $date . '-' . $random;
            $attempts++;
        }
        
        return $receiptNumber;
    }

    /**
     * Create receipt record
     */
    private function createReceiptRecord($invoiceId, $guestId, $amount, $paymentMethod, $notes = null, $receivedBy = null)
    {
        try {
            $receiptNumber = $this->generateUniqueReceiptNumber();
            
            Receipt::create([
                'hotel_id' => $this->getHotelId(),
                'receipt_number' => $receiptNumber,
                'invoice_id' => $invoiceId,
                'guest_id' => $guestId,
                'amount_paid' => $amount,
                'payment_date' => now(),
                'payment_method' => $paymentMethod,
                'payment_status' => 'paid',
                'notes' => $notes,
                'received_by' => $receivedBy ?? Auth::id(),
            ]);
            
            return $receiptNumber;
            
        } catch (\Exception $e) {
            Log::error('Error creating receipt: ' . $e->getMessage());
            return null;
        }
    }

    /**
     * Generate unique walk-in number
     */
    private function generateUniqueWalkinNumber(): string
    {
        $prefix = 'WLK';
        $year = date('Y');
        $month = date('m');
        $day = date('d');
        $timestamp = time();
        $random = rand(100, 999);
        
        $walkInNumber = $prefix . $year . $month . $day . substr($timestamp, -4) . $random;
        
        $counter = 1;
        while (Reservation::where('walk_in_number', $walkInNumber)->exists() && $counter < 100) {
            $walkInNumber = $prefix . $year . $month . $day . substr($timestamp + $counter, -4) . rand(100, 999);
            $counter++;
        }
        
        return $walkInNumber;
    }

    /**
     * Display the check-in page
     */
    public function checkinPage()
    {
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                return redirect()->route('login')->with('error', 'Hotel ID not found. Please login again.');
            }
            
            $reservations = Reservation::with(['guest', 'room', 'room.roomType'])
                ->where('hotel_id', $hotelId)
                ->whereIn('status', ['pending', 'confirmed'])
                ->whereDate('check_in', today())
                ->orderBy('check_in')
                ->get();

            $availableRooms = Room::where('hotel_id', $hotelId)
                ->where('status', 'available')
                ->with('roomType')
                ->get();

            $roomTypes = RoomType::where('hotel_id', $hotelId)->get();

            return view('inout.checkin', [
                'reservations' => $reservations,
                'availableRooms' => $availableRooms,
                'roomTypes' => $roomTypes
            ]);
            
        } catch (\Exception $e) {
            Log::error('Error in checkinPage: ' . $e->getMessage());
            return view('inout.checkin', [
                'reservations' => collect(),
                'availableRooms' => collect(),
                'roomTypes' => collect()
            ]);
        }
    }

    /**
     * Display walk-in check-in page
     */
    public function walkinPage()
    {
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                return redirect()->route('login')->with('error', 'Hotel ID not found. Please login again.');
            }
            
            $roomTypes = RoomType::where('hotel_id', $hotelId)->get();
            
            return view('inout.checkin', [
                'reservations' => collect(),
                'availableRooms' => collect(),
                'roomTypes' => $roomTypes,
                'isWalkinMode' => true
            ]);
            
        } catch (\Exception $e) {
            Log::error('Error loading walk-in page: ' . $e->getMessage());
            return redirect()->back()->with('error', 'Error loading walk-in check-in page.');
        }
    }

    /**
     * Get reservation details API endpoint
     */
    public function getReservationDetails($id)
    {
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                return response()->json(['success' => false, 'message' => 'Hotel ID not found'], 400);
            }
            
            $reservation = Reservation::with(['guest', 'room', 'room.roomType', 'invoices.payments'])
                ->where('hotel_id', $hotelId)
                ->findOrFail($id);
            
            // Sync ledger with invoice before returning details
            $invoice = $this->ledgerService->updateConsolidatedInvoice($reservation);
            
            $totalAmount = $invoice ? $invoice->total_amount : ($reservation->total_amount ?? 0);
            $paidAmount = $invoice ? ($invoice->amount_paid ?? $invoice->payments->sum('amount')) : ($reservation->paid_amount ?? 0);
            $balance = max(0, $totalAmount - $paidAmount);

            return response()->json([
                'success' => true,
                'id' => $reservation->id,
                'reservation_number' => $reservation->reservation_number ?? 'WLK' . $reservation->id,
                'check_in_date' => $reservation->check_in->format('M j, Y'),
                'check_out_date' => $reservation->check_out->format('M j, Y'),
                'total_nights' => max(1, $reservation->check_in->diffInDays($reservation->check_out)),
                'total_amount' => (float)$totalAmount,
                'paid_amount' => (float)$paidAmount,
                'balance' => (float)$balance,
                'guest_name' => ($reservation->guest->first_name ?? '') . ' ' . ($reservation->guest->last_name ?? ''),
                'guest_email' => $reservation->guest->email ?? 'N/A',
                'guest_phone' => $reservation->guest->phone ?? 'N/A',
                'guest_address' => $reservation->guest->address ?? 'N/A',
                'room_number' => $reservation->room->room_number ?? 'N/A',
                'room_type' => $reservation->room->roomType->name ?? 'N/A',
                'room_floor' => $reservation->room->floor ?? 'N/A',
                'room_capacity' => $reservation->room->capacity ?? 'N/A',
                'status' => $reservation->status,
                'is_walkin' => $reservation->is_walk_in ?? false,
                'has_invoice' => $invoice ? true : false
            ]);

        } catch (\Exception $e) {
            Log::error('Error getting reservation details: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Reservation not found'
            ], 404);
        }
    }

    /**
     * Process check-in
     */
    public function checkin(Request $request)
    {
        DB::beginTransaction();
        
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                throw new \Exception('Hotel ID not found. Please ensure you are logged in.');
            }
            
            $request->validate([
                'reservation_id' => [
                    'required',
                    Rule::exists('reservations', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
                'notes' => 'nullable|string',
                'amount_paid' => 'nullable|numeric|min:0',
                'payment_method' => 'required_with:amount_paid|string'
            ]);

            $reservation = Reservation::with(['guest', 'room', 'invoices.payments'])
                ->where('hotel_id', $hotelId)
                ->findOrFail($request->reservation_id);

            if (!in_array($reservation->status, ['pending', 'confirmed'])) {
                return redirect()->back()->with('error', 'Reservation is not in a valid status to be checked in.');
            }

            $invoice = $reservation->invoices->first();
            $amountPaid = $request->amount_paid ?? 0;
            
            if (!$invoice) {
                // Generate an invoice using the payment amount directly
                $invoice = $this->generateInvoice($reservation, [
                    'amount_paid' => $amountPaid,
                    'payment_method' => $amountPaid > 0 ? $request->payment_method : 'cash'
                ]);
            } else if ($amountPaid > 0) {
                // Determine if we need to use a LedgerService or recordPayment
                // The generateInvoice method will do everything if there is no invoice,
                // But if an invoice already exists and they are paying now, we record it.
                if (method_exists($reservation, 'recordPayment')) {
                    $reservation->recordPayment($amountPaid, $request->payment_method, 'payment', 'Check-in Payment');
                } else if (property_exists($this, 'ledgerService')) {
                    $payment = $invoice->payments()->first();
                    $this->ledgerService->addPayment(
                        $reservation->id,
                        "Check-in payment",
                        $request->payment_method,
                        $amountPaid,
                        $payment->id ?? null
                    );
                }
                
                // Update the reservation's paid amount explicitly
                $reservation->increment('paid_amount', $amountPaid);
                $reservation->update(['payment_status' => $reservation->paid_amount >= $reservation->total_amount ? 'paid' : 'partially_paid']);
            }

            // Update reservation
            $reservation->update([
                'status' => 'checked_in',
                'actual_check_in' => now()
            ]);

            // Update room
            if ($reservation->room) {
                $reservation->room->update([
                    'status' => 'occupied',
                    'housekeeping_status' => 'occupied'
                ]);
            }

            // Create log
            InOutLog::create([
                'hotel_id' => $hotelId,
                'reservation_id' => $reservation->id,
                'guest_id' => $reservation->guest_id,
                'room_id' => $reservation->room_id,
                'type' => 'checkin',
                'logged_at' => now(),
                'notes' => $request->notes,
                'processed_by' => Auth::id(),
                'invoice_id' => $invoice->id,
                'receipt_issued' => true
            ]);

            DB::commit();

            return redirect()->route('inout.index')->with([
                'success' => 'Guest checked in successfully!',
                'invoice_number' => $invoice->invoice_number,
                'reservation_id' => $reservation->id,
                'show_receipt' => true
            ]);

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Check-in error: ' . $e->getMessage());
            return redirect()->back()->with('error', 'Error: ' . $e->getMessage());
        }
    }

    /**
     * Process check-in (API)
     */
    public function processCheckin(Request $request)
    {
        DB::beginTransaction();
        
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                throw new \Exception('Hotel ID not found. Please ensure you are logged in.');
            }
            
            $request->validate([
                'reservation_id' => [
                    'required',
                    Rule::exists('reservations', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
                'notes' => 'nullable|string',
            ]);

            $reservation = Reservation::with(['guest', 'room', 'invoices.payments'])
                ->where('hotel_id', $hotelId)
                ->findOrFail($request->reservation_id);

            if (!in_array($reservation->status, ['pending', 'confirmed'])) {
                throw new \Exception('Reservation is not in a valid state for check-in.');
            }

            $invoice = $reservation->invoices->first();
            
            if (!$invoice) {
                // Generate an invoice if the guest checks in with zero payment
                $invoice = $this->generateInvoice($reservation, [
                    'amount_paid' => 0,
                    'payment_method' => 'cash'
                ]);
            }

            $reservation->update([
                'status' => 'checked_in',
                'actual_check_in' => now()
            ]);

            if ($reservation->room) {
                $reservation->room->update([
                    'status' => 'occupied',
                    'housekeeping_status' => 'occupied'
                ]);
            }

            InOutLog::create([
                'hotel_id' => $hotelId,
                'reservation_id' => $reservation->id,
                'guest_id' => $reservation->guest_id,
                'room_id' => $reservation->room_id,
                'type' => 'checkin',
                'logged_at' => now(),
                'notes' => $request->notes,
                'processed_by' => Auth::id(),
                'invoice_id' => $invoice->id,
                'receipt_issued' => true
            ]);

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Guest checked in successfully!',
                'data' => [
                    'reservation_id' => $reservation->id,
                    'guest_name' => $this->getGuestDisplayName($reservation),
                    'room_number' => $reservation->room->room_number ?? 'N/A',
                    'invoice_number' => $invoice->invoice_number
                ]
            ]);

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Process check-in error: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Process walk-in check-in - FIXED VERSION WITH HOTEL_ID
     */
    public function checkinWalkin(Request $request)
    {
        DB::beginTransaction();
        
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                throw new \Exception('Hotel ID not found. Please ensure you are logged in.');
            }
            
            $request->validate([
                'room_id' => [
                    'required',
                    Rule::exists('rooms', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
                'first_name' => 'required|string|max:255',
                'last_name' => 'required|string|max:255',
                'email' => 'nullable|email',
                'phone' => 'required|string',
                'check_in' => 'required|date',
                'check_out' => 'required|date|after:check_in',
                'adults' => 'required|integer|min:1',
                'payment_method' => 'required|string',
                'amount_paid' => 'nullable|numeric|min:0',
            ]);

            // Calculate nights and total
            $checkInDate = Carbon::parse($request->check_in);
            $checkOutDate = Carbon::parse($request->check_out);
            $nights = max(1, $checkInDate->diffInDays($checkOutDate));
            
            $room = Room::with('roomType')->where('hotel_id', $hotelId)->find($request->room_id);
            if (!$room) {
                throw new \Exception('Room not found for this hotel.');
            }
            
            $pricePerNight = $room->roomType ? $room->roomType->price_per_night : ($room->price_per_night ?? 150000);
            
            // Calculate room charges and VAT correctly
            $roomCharges = $pricePerNight * $nights;
            $vatRate = (float) setting('tax_rate', 18) / 100;
            $taxAmount = ceil($roomCharges * $vatRate);
            $totalAmount = $roomCharges + $taxAmount;

            $amountPaid = $request->amount_paid ?? 0;

            // Handle guest
            $guest = Guest::where('hotel_id', $hotelId)
                ->where(function($q) use ($request) {
                    $q->where('email', $request->email)
                      ->orWhere('phone', $request->phone);
                })
                ->first();

            if ($guest) {
                $guest->update([
                    'first_name' => $request->first_name,
                    'last_name' => $request->last_name,
                    'phone' => $request->phone,
                    'is_walkin_guest' => true,
                    'total_visits' => ($guest->total_visits ?? 0) + 1,
                    'last_visit_date' => now(),
                ]);
            } else {
                $guest = Guest::create([
                    'hotel_id' => $hotelId,
                    'first_name' => $request->first_name,
                    'last_name' => $request->last_name,
                    'email' => $request->email,
                    'phone' => $request->phone,
                    'is_walkin_guest' => true,
                    'guest_type' => 'walkin',
                    'status' => 'active',
                    'total_visits' => 1,
                    'last_visit_date' => now(),
                ]);
            }

            // Generate walk-in number
            $walkInNumber = $this->generateUniqueWalkinNumber();

            // Create reservation WITH HOTEL_ID
            $reservation = Reservation::create([
                'hotel_id' => $hotelId,
                'guest_id' => $guest->id,
                'room_id' => $request->room_id,
                'check_in' => $request->check_in,
                'check_out' => $request->check_out,
                'status' => 'confirmed',
                'source' => 'walk_in',
                'is_walk_in' => true,
                'walk_in_number' => $walkInNumber,
                'reservation_number' => $walkInNumber,
                'total_amount' => $totalAmount,
                'paid_amount' => $amountPaid,
                'payment_method' => $request->payment_method,
                'adults' => $request->adults,
                'children' => $request->children ?? 0,
                'number_of_guests' => ($request->adults ?? 1) + ($request->children ?? 0),
                'created_by' => Auth::id(),
            ]);

            // Generate invoice
            $paymentData = [
                'amount_paid' => $amountPaid,
                'payment_method' => $request->payment_method
            ];
            
            $invoice = $this->generateInvoice($reservation, $paymentData);
            // NOTE: generateInvoice() already posts room charges and initial payment
            // into the guest ledger. Posting again here causes duplicate invoice rows.

            // Check in
            $reservation->update([
                'status' => 'checked_in',
                'actual_check_in' => now()
            ]);

            $room->update([
                'status' => 'occupied',
                'housekeeping_status' => 'occupied'
            ]);

            InOutLog::create([
                'hotel_id' => $hotelId,
                'reservation_id' => $reservation->id,
                'guest_id' => $guest->id,
                'room_id' => $request->room_id,
                'type' => 'checkin',
                'logged_at' => now(),
                'processed_by' => Auth::id(),
                'invoice_id' => $invoice->id,
                'receipt_issued' => true
            ]);

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Walk-in guest checked in successfully!',
                'data' => [
                    'reservation_id' => $reservation->id,
                    'guest_name' => $guest->first_name . ' ' . $guest->last_name,
                    'room_number' => $room->room_number,
                    'total_amount' => $totalAmount,
                    'amount_paid' => $amountPaid,
                    'walk_in_number' => $walkInNumber,
                    'invoice_number' => $invoice->invoice_number,
                    'current_balance' => $totalAmount - $amountPaid,
                    'is_returning_guest' => !$guest->wasRecentlyCreated,
                    'total_visits' => $guest->total_visits
                ]
            ]);

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Walk-in check-in error: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString()
            ]);
            return response()->json([
                'success' => false,
                'message' => 'Error: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Process enhanced walk-in check-in with more fields
     */
    public function checkinWalkinEnhanced(Request $request)
    {
        DB::beginTransaction();
        
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                throw new \Exception('Hotel ID not found. Please ensure you are logged in.');
            }
            
            $validator = Validator::make($request->all(), [
                'room_id' => [
                    'required',
                    Rule::exists('rooms', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
                'first_name' => 'required|string|max:255',
                'last_name' => 'required|string|max:255',
                'email' => 'nullable|email|max:255',
                'phone' => 'required|string|max:20',
                'id_type' => 'nullable|string|max:50',
                'id_number' => 'nullable|string|max:100',
                'check_in_date' => 'required|date',
                'check_out_date' => 'required|date|after:check_in_date',
                'adults' => 'required|integer|min:1|max:10',
                'children' => 'nullable|integer|min:0|max:10',
                'special_requests' => 'nullable|string',
                'payment_method' => 'required|string',
                'total_amount' => 'required|numeric|min:0',
                'paid_amount' => 'required|numeric|min:0',
                'assigned_staff' => 'required|string',
                'user_id' => [
                    'required',
                    Rule::exists('users', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
                'existing_guest_id' => [
                    'nullable',
                    Rule::exists('guests', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
            ]);

            if ($validator->fails()) {
                return response()->json([
                    'success' => false,
                    'message' => 'Validation error',
                    'errors' => $validator->errors()
                ], 422);
            }

            // Check room availability
            $room = Room::where('hotel_id', $hotelId)->findOrFail($request->room_id);
            
            // Handle guest
            $guest = null;
            $isReturningGuest = false;
            
            if ($request->existing_guest_id) {
                $guest = Guest::where('hotel_id', $hotelId)->find($request->existing_guest_id);
                if ($guest) {
                    $isReturningGuest = true;
                }
            }
            
            if (!$guest) {
                $guest = Guest::where('hotel_id', $hotelId)
                    ->where(function($q) use ($request) {
                        $q->where('email', $request->email)
                          ->orWhere('phone', $request->phone);
                    })
                    ->first();
                    
                if ($guest) {
                    $isReturningGuest = true;
                }
            }

            if ($guest) {
                $guest->update([
                    'first_name' => $request->first_name,
                    'last_name' => $request->last_name,
                    'email' => $request->email,
                    'phone' => $request->phone,
                    'id_type' => $request->id_type ?? $guest->id_type,
                    'id_number' => $request->id_number ?? $guest->id_number,
                    'is_walkin_guest' => true,
                    'total_visits' => ($guest->total_visits ?? 0) + 1,
                    'last_visit_date' => now(),
                ]);
            } else {
                $guest = Guest::create([
                    'hotel_id' => $hotelId,
                    'first_name' => $request->first_name,
                    'last_name' => $request->last_name,
                    'email' => $request->email,
                    'phone' => $request->phone,
                    'id_type' => $request->id_type,
                    'id_number' => $request->id_number,
                    'is_walkin_guest' => true,
                    'guest_type' => 'walkin',
                    'status' => 'active',
                    'total_visits' => 1,
                    'last_visit_date' => now(),
                ]);
            }

            // Calculate nights
            $checkInDate = Carbon::parse($request->check_in_date);
            $checkOutDate = Carbon::parse($request->check_out_date);
            $totalNights = max(1, $checkInDate->diffInDays($checkOutDate));

            // Generate walk-in number
            $walkInNumber = $this->generateUniqueWalkinNumber();

            // Create reservation WITH HOTEL_ID
            $reservation = Reservation::create([
                'hotel_id' => $hotelId,
                'guest_id' => $guest->id,
                'room_id' => $request->room_id,
                'check_in' => $checkInDate,
                'check_out' => $checkOutDate,
                'adults' => $request->adults,
                'children' => $request->children ?? 0,
                'total_nights' => $totalNights,
                'total_amount' => $request->total_amount,
                'paid_amount' => $request->paid_amount,
                'balance_due' => max(0, $request->total_amount - $request->paid_amount),
                'payment_method' => $request->payment_method,
                'payment_status' => $request->paid_amount >= $request->total_amount ? 'paid' : 'partially_paid',
                'special_requests' => $request->special_requests,
                'status' => 'confirmed',
                'is_walk_in' => true,
                'walk_in_number' => $walkInNumber,
                'reservation_number' => $walkInNumber,
                'assigned_staff' => $request->assigned_staff,
                'created_by' => $request->user_id,
                'source' => 'walk_in',
            ]);

            // Generate invoice
            $paymentData = [
                'amount_paid' => $request->paid_amount,
                'payment_method' => $request->payment_method
            ];
            
            $invoice = $this->generateInvoice($reservation, $paymentData);
            // NOTE: generateInvoice() already posts room charges and initial payment
            // into the guest ledger. Posting again here causes duplicate invoice rows.

            // Check in the guest
            $reservation->update([
                'status' => 'checked_in',
                'actual_check_in' => Carbon::now()
            ]);

            // Update room status
            $room->update([
                'status' => 'occupied',
                'housekeeping_status' => 'occupied',
                'last_check_in' => Carbon::now(),
            ]);

            // Create log
            InOutLog::create([
                'hotel_id' => $hotelId,
                'reservation_id' => $reservation->id,
                'guest_id' => $guest->id,
                'room_id' => $room->id,
                'type' => 'checkin',
                'logged_at' => Carbon::now(),
                'notes' => 'Walk-in check-in. ' . ($isReturningGuest ? 'Returning guest' : 'New guest') . 
                          '. Total visits: ' . $guest->total_visits,
                'processed_by' => $request->user_id,
                'invoice_id' => $invoice->id,
                'receipt_issued' => true,
            ]);

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Walk-in guest checked in successfully.',
                'data' => [
                    'reservation_id' => $reservation->id,
                    'walk_in_number' => $walkInNumber,
                    'guest_name' => $guest->first_name . ' ' . $guest->last_name,
                    'room_number' => $room->room_number,
                    'invoice_number' => $invoice->invoice_number,
                    'total_amount' => $request->total_amount,
                    'paid_amount' => $request->paid_amount,
                    'balance_due' => max(0, $request->total_amount - $request->paid_amount),
                    'current_balance' => max(0, $request->total_amount - $request->paid_amount),
                    'check_in_date' => $checkInDate->format('Y-m-d H:i:s'),
                    'check_out_date' => $checkOutDate->format('Y-m-d H:i:s'),
                    'is_returning_guest' => $isReturningGuest,
                    'total_visits' => $guest->total_visits
                ]
            ]);

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Enhanced walk-in check-in error: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Error: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Cancel reservation
     */
    public function cancelReservation(Request $request)
    {
        DB::beginTransaction();
        
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                throw new \Exception('Hotel ID not found. Please ensure you are logged in.');
            }
            
            $request->validate([
                'reservation_id' => [
                    'required',
                    Rule::exists('reservations', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
                'reason' => 'required|string',
                'notes' => 'nullable|string',
            ]);

            $reservation = Reservation::with(['guest', 'room', 'invoices.payments'])
                ->where('hotel_id', $hotelId)
                ->findOrFail($request->reservation_id);

            // Check if reservation can be cancelled - ONLY reservations that haven't checked in
            if (!in_array($reservation->status, ['pending', 'confirmed'])) {
                throw new \Exception('Only reservations that have not checked in can be cancelled. Current status: ' . $reservation->status);
            }

            // Process refund if payment was made
            $invoice = $reservation->invoices->first();
            if ($invoice) {
                $paidAmount = $invoice->payments->sum('amount') ?? 0;
                if ($paidAmount > 0) {
                    Log::info('Refund needed for cancelled reservation', [
                        'reservation_id' => $reservation->id,
                        'amount' => $paidAmount,
                        'reason' => $request->reason
                    ]);
                }
            }

            // Update room status for pending reservation
            if ($reservation->room) {
                $reservation->room->update([
                    'status' => 'available',
                    'housekeeping_status' => 'available'
                ]);
            }

            // Update reservation status
            $reservation->update([
                'status' => 'cancelled',
                'cancelled_at' => now(),
                'cancellation_reason' => $request->reason,
                'cancellation_notes' => $request->notes,
                'cancelled_by' => Auth::id()
            ]);

            // Create log
            InOutLog::create([
                'hotel_id' => $hotelId,
                'reservation_id' => $reservation->id,
                'guest_id' => $reservation->guest_id,
                'room_id' => $reservation->room_id,
                'type' => 'cancellation',
                'logged_at' => now(),
                'notes' => 'Reservation cancelled. Reason: ' . $request->reason . '. ' . ($request->notes ?? ''),
                'processed_by' => Auth::id()
            ]);

            DB::commit();

            if ($request->ajax() || $request->wantsJson()) {
                return response()->json([
                    'success' => true,
                    'message' => 'Reservation cancelled successfully',
                    'data' => [
                        'reservation_id' => $reservation->id,
                        'status' => 'cancelled'
                    ]
                ]);
            }

            return redirect()->route('inout.index')->with('success', 'Reservation cancelled successfully');

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Cancel reservation error: ' . $e->getMessage());
            
            if ($request->ajax() || $request->wantsJson()) {
                return response()->json([
                    'success' => false,
                    'message' => 'Error: ' . $e->getMessage()
                ], 500);
            }
            
            return redirect()->back()->with('error', 'Error: ' . $e->getMessage());
        }
    }

    /**
     * Process checkout
     */
    public function checkout(Request $request)
    {
        DB::beginTransaction();
        
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                throw new \Exception('Hotel ID not found. Please ensure you are logged in.');
            }
            
            $request->validate([
                'reservation_id' => [
                    'required',
                    Rule::exists('reservations', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
                'notes' => 'nullable|string',
            ]);

            $reservation = Reservation::with(['guest', 'room', 'invoices.payments'])
                ->where('hotel_id', $hotelId)
                ->findOrFail($request->reservation_id);

            if ($reservation->status !== 'checked_in') {
                return redirect()->back()->with('error', 'Reservation is not checked in.');
            }

            // Check if fully paid
            $invoice = $reservation->invoices->first();
            
            if ($invoice) {
                $totalAmount = $invoice->total_amount ?? 0;
                $paidAmount = $invoice->payments->sum('amount') ?? 0;
                
                // Calculate overstay
                $today = Carbon::now();
                $checkOutDateTime = $this->buildCheckoutCutoffDateTime($reservation, $hotelId);

                if ($today->greaterThan($checkOutDateTime)) {
                    $overstayDays = Carbon::parse($today->toDateString())->diffInDays(Carbon::parse($checkOutDateTime->toDateString()));
                    $roomPrice = $reservation->room->price_per_night ?? 0;
                    $overstayFees = $overstayDays * ceil($roomPrice * 1.2);
                    $totalAmount += $overstayFees;
                }
                
                if ($paidAmount < $totalAmount) {
                    return redirect()->back()->with('error', 'Outstanding balance: UGX ' . number_format($totalAmount - $paidAmount));
                }
            }

            // Update reservation
            $reservation->update([
                'status' => 'checked_out',
                'actual_check_out' => now()
            ]);

            // Flag room for cleaning
            if ($reservation->room) {
                $reservation->room->markAsCheckedOut();
            }

            // Create log
            InOutLog::create([
                'hotel_id' => $hotelId,
                'reservation_id' => $reservation->id,
                'guest_id' => $reservation->guest_id,
                'room_id' => $reservation->room_id,
                'type' => 'checkout',
                'logged_at' => now(),
                'notes' => $request->notes,
                'processed_by' => Auth::id()
            ]);

            DB::commit();

            return redirect()->route('inout.index')->with('success', 'Guest checked out successfully!');

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Checkout error: ' . $e->getMessage());
            return redirect()->back()->with('error', 'Error: ' . $e->getMessage());
        }
    }

    /**
     * Confirm a pending reservation
     */
    public function confirmReservation(Request $request)
    {
        DB::beginTransaction();
        
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                throw new \Exception('Hotel ID not found. Please ensure you are logged in.');
            }
            
            $request->validate([
                'reservation_id' => [
                    'required',
                    Rule::exists('reservations', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
            ]);

            $reservation = Reservation::where('hotel_id', $hotelId)
                ->findOrFail($request->reservation_id);

            if ($reservation->status !== 'pending') {
                throw new \Exception('Only pending reservations can be confirmed.');
            }

            $reservation->update(['status' => 'confirmed']);

            InOutLog::create([
                'hotel_id' => $hotelId,
                'reservation_id' => $reservation->id,
                'guest_id' => $reservation->guest_id,
                'room_id' => $reservation->room_id,
                'type' => 'confirmation',
                'logged_at' => now(),
                'notes' => 'Reservation confirmed from Frontdesk',
                'processed_by' => Auth::id()
            ]);

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Reservation confirmed successfully!'
            ]);

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Process confirmation error: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Process checkout (API)
     */
    public function processCheckout(Request $request)
    {
        DB::beginTransaction();
        
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                throw new \Exception('Hotel ID not found. Please ensure you are logged in.');
            }
            
            $request->validate([
                'reservation_id' => [
                    'required',
                    Rule::exists('reservations', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
                'notes' => 'nullable|string',
            ]);

            $reservation = Reservation::with(['guest', 'room', 'invoices.payments'])
                ->where('hotel_id', $hotelId)
                ->findOrFail($request->reservation_id);

            if ($reservation->status !== 'checked_in') {
                throw new \Exception('Reservation is not checked in.');
            }

            // Check payment
            $invoice = $reservation->invoices->first();
            
            if ($invoice) {
                $totalAmount = (float)($invoice->total_amount ?? 0);
                $paidAmount = (float)($invoice->amount_paid ?? 0);
                
                // Fallback to payments sum if amount_paid is zero or smaller
                $sumPayments = (float)$invoice->payments->sum('amount');
                if ($sumPayments > $paidAmount) {
                    $paidAmount = $sumPayments;
                }
                
                // Calculate overstay
                $today = Carbon::now();
                $checkOutDateTime = $this->buildCheckoutCutoffDateTime($reservation, $hotelId);

                if ($today->greaterThan($checkOutDateTime)) {
                    $overstayDays = Carbon::parse($today->toDateString())->diffInDays(Carbon::parse($checkOutDateTime->toDateString()));
                    $roomPrice = $reservation->room->price_per_night ?? 0;
                    $overstayFees = $overstayDays * ceil($roomPrice * 1.2);
                    $totalAmount += $overstayFees;
                }
                
                if ($paidAmount < $totalAmount) {
                    throw new \Exception('Outstanding balance: UGX ' . number_format($totalAmount - $paidAmount));
                }
            }

            $reservation->update([
                'status' => 'checked_out',
                'actual_check_out' => now()
            ]);

            if ($reservation->room) {
                $reservation->room->markAsCheckedOut();
            }

            InOutLog::create([
                'hotel_id' => $hotelId,
                'reservation_id' => $reservation->id,
                'guest_id' => $reservation->guest_id,
                'room_id' => $reservation->room_id,
                'type' => 'checkout',
                'logged_at' => now(),
                'notes' => $request->notes,
                'processed_by' => Auth::id()
            ]);

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => 'Guest checked out successfully!',
                'data' => [
                    'reservation_id' => $reservation->id,
                    'guest_name' => $this->getGuestDisplayName($reservation),
                    'room_number' => $reservation->room->room_number ?? 'N/A'
                ]
            ]);

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Process checkout error: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Process payment
     */
    public function processPayment(Request $request)
    {
        Log::info('processPayment called', ['payload' => $request->all(), 'route' => 'inout/process-payment']);
        DB::beginTransaction();
        
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                Log::warning('processPayment missing hotel_id');
                return response()->json(['success' => false, 'message' => 'Hotel ID not found. Please ensure you are logged in.', 'balance' => null], 400);
            }
            
            $request->validate([
                'reservation_id' => [
                    'required',
                    Rule::exists('reservations', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
                'amount' => 'required|numeric|min:0.01',
                'payment_method' => 'required|string',
                'notes' => 'nullable|string',
            ]);

            $reservation = Reservation::with(['guest', 'room', 'invoices.payments'])
                ->where('hotel_id', $hotelId)
                ->findOrFail($request->reservation_id);

            $invoice = $reservation->invoices->first();
            
            if (!in_array($reservation->status, ['checked_in', 'confirmed'])) {
                $balance = $invoice ? max(0, (float)$invoice->total_amount - (float)$invoice->payments->sum('amount')) : 0;
                Log::warning('processPayment status invalid', ['status' => $reservation->status, 'reservation_id' => $reservation->id, 'balance' => $balance]);
                return response()->json([
                    'success' => false,
                    'message' => 'Cannot process payment. Current status: ' . $reservation->status,
                    'balance' => $balance
                ], 400);
            }

            // Get or create invoice
            
            if (!$invoice) {
                $paymentData = [
                    'amount_paid' => 0,
                    'payment_method' => $request->payment_method
                ];
                $invoice = $this->generateInvoice($reservation, $paymentData);
            }

            // Calculate total amount with overstay if checked in
            $totalAmount = (float)$invoice->total_amount;
            
            // Calculate overstay if checked in
            if ($reservation->status === 'checked_in') {
                $today = Carbon::now();
                $checkOutDateTime = $this->buildCheckoutCutoffDateTime($reservation, $hotelId);

                if ($today->greaterThan($checkOutDateTime)) {
                    $overstayDays = Carbon::parse($today->toDateString())->diffInDays(Carbon::parse($checkOutDateTime->toDateString()));
                    $roomPrice = $reservation->room->price_per_night ?? 0;
                    $overstayFees = $overstayDays * ceil($roomPrice * 1.2);
                    $totalAmount += $overstayFees;
                }
            }
            
            $currentPaid = (float)$invoice->payments->sum('amount');
            $totalAmount = max(0, (float)$invoice->total_amount);

            if ($currentPaid >= $totalAmount) {
                Log::info('processPayment already paid', ['invoice_id' => $invoice->id, 'current_paid' => $currentPaid, 'total' => $totalAmount]);
                return response()->json([
                    'success' => false,
                    'message' => 'Invoice already fully paid.',
                    'max_allowed' => 0,
                    'balance' => 0
                ], 400);
            }

            $maxAllowed = max(0, $totalAmount - $currentPaid);
            $newTotalPaid = $currentPaid + (float)$request->amount;

            if ($newTotalPaid > $totalAmount) {
                Log::info('processPayment overpay request', ['invoice_id' => $invoice->id, 'current_paid' => $currentPaid, 'requested' => $request->amount, 'total' => $totalAmount, 'max_allowed' => $maxAllowed]);
                return response()->json([
                    'success' => false,
                    'message' => 'Payment exceeds total. Maximum allowed: ' . setting('currency_symbol', 'UGX') . ' ' . number_format($maxAllowed),
                    'max_allowed' => $maxAllowed,
                    'balance' => $maxAllowed
                ], 400);
            }

            // Create payment
            $payment = Payment::create([
                'hotel_id' => $hotelId,
                'invoice_id' => $invoice->id,
                'reservation_id' => $reservation->id, // LINK TO RESERVATION DIRECTLY
                'amount' => $request->amount,
                'payment_method' => $request->payment_method,
                'reference' => 'PAY-' . time() . '-' . $reservation->id,
                'notes' => $request->notes ?? 'Payment processed',
                'status' => 'completed',
                'paid_at' => now()
            ]);

            // Update invoice status
            $invoice->update([
                'payment_status' => $newTotalPaid >= $totalAmount ? 'paid' : 'partially_paid'
            ]);

            $balanceDue = max(0, $totalAmount - $newTotalPaid);
            $invoiceStatus = $newTotalPaid >= $totalAmount ? 'paid' : 'partially_paid';

            // Create receipt
            $receiptNumber = $this->createReceiptRecord(
                $invoice->id,
                $reservation->guest_id,
                $request->amount,
                $request->payment_method,
                $request->notes ?? 'Payment processed',
                Auth::id()
            );

            // Add payment to guest ledger
            $this->ledgerService->addPayment(
                $reservation->id,
                $request->notes ?? 'Payment processed',
                $request->payment_method,
                $request->amount,
                $payment->id
            );

            // Create log
            InOutLog::create([
                'hotel_id' => $hotelId,
                'reservation_id' => $reservation->id,
                'guest_id' => $reservation->guest_id,
                'room_id' => $reservation->room_id,
                'type' => 'payment',
                'logged_at' => now(),
                'notes' => 'Payment of ' . number_format($request->amount) . ' UGX via ' . $request->payment_method,
                'processed_by' => Auth::id(),
                'invoice_id' => $invoice->id,
                'amount_paid' => $request->amount,
                'payment_method' => $request->payment_method
            ]);

            DB::commit();

            // Get updated payment history
            $updatedPayments = $invoice->payments()->orderBy('created_at', 'asc')->get()->map(function($p) {
                return [
                    'amount' => $p->amount,
                    'method' => $p->payment_method,
                    'date' => $p->created_at->format('M j, Y'),
                    'time' => $p->created_at->format('h:i A'),
                    'reference' => $p->reference
                ];
            });

            return response()->json([
                'success' => true,
                'message' => 'Payment processed successfully!',
                'data' => [
                    'reservation_id' => $reservation->id,
                    'invoice_number' => $invoice->invoice_number,
                    'receipt_number' => $receiptNumber,
                    'amount_paid' => $request->amount,
                    'total_paid' => $newTotalPaid,
                    'total_amount' => $totalAmount,
                    'balance' => $balanceDue,
                    'invoice_status' => $invoiceStatus,
                    'is_fully_paid' => $invoiceStatus === 'paid',
                    'payment_history' => $updatedPayments,
                    'installment_count' => $invoice->payments->count(),
                    'guest_name' => $this->getGuestDisplayName($reservation),
                    'room_number' => $reservation->room->room_number ?? 'N/A'
                ]
            ]);

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Payment error: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Error: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get receipt data
     */
    public function getReceipt($reservationId)
    {
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                return response()->json(['success' => false, 'message' => 'Hotel ID not found'], 400);
            }
            
            $reservation = Reservation::with(['guest', 'room', 'room.roomType', 'invoices.payments'])
                ->where('hotel_id', $hotelId)
                ->findOrFail($reservationId);
            
            $guestName = $this->getGuestDisplayName($reservation);
            
            $invoice = $reservation->invoices->first();
            if (!$invoice) {
                return response()->json([
                    'success' => false,
                    'message' => 'No invoice found'
                ], 404);
            }
            
            // Calculate room charges and VAT
            $checkIn = Carbon::parse($reservation->check_in);
            $checkOut = $reservation->actual_check_out ? Carbon::parse($reservation->actual_check_out) : Carbon::parse($reservation->check_out);
            $nights = max(1, $checkIn->diffInDays($checkOut));
            $roomPrice = $reservation->room->price_per_night ?? 0;
            
            // Calculate room charges (price × nights)
            $roomCharges = $roomPrice * $nights;
            
            // Calculate VAT
            $vatRate = (float) setting('tax_rate', 18);
            $vatAmount = ceil($roomCharges * $vatRate / 100);
            
            // Calculate subtotal (room charges + VAT)
            $subtotal = $roomCharges + $vatAmount;
            
            // Calculate overstay
            $overstayDays = 0;
            $overstayFees = 0;
            if ($reservation->actual_check_out) {
                $scheduledCheckOut = Carbon::parse($reservation->check_out);
                $actualCheckOut = Carbon::parse($reservation->actual_check_out);
                if ($actualCheckOut->greaterThan($scheduledCheckOut)) {
                    $overstayDays = $scheduledCheckOut->diffInDays($actualCheckOut);
                    $overstayFeePerDay = ceil($roomPrice * 1.2);
                    $overstayFees = $overstayDays * $overstayFeePerDay;
                }
            }
            
            $folio = $this->ledgerService->getGuestFolio($reservationId);
            $ledgerEntries = $folio['entries'];
            
            // Re-use grouping logic from showConsolidatedInvoice
            $groupedCharges = $ledgerEntries->where('debit', '>', 0)->groupBy('reference_type');
            $breakdown = [];
            $additionalCharges = [];
            $additionalChargesTotal = 0;
            $vatRate = (float) setting('tax_rate', 18);
            
            foreach ($groupedCharges as $type => $entries) {
                $totalDebit = $entries->sum('debit');
                $taxForGroup = ($totalDebit * $vatRate) / (100 + $vatRate);
                $netForGroup = $totalDebit - $taxForGroup;
                
                $label = $type === 'room_charge' ? 'Room Revenue' : 
                         ($type === 'restaurant' ? 'Food and Beverage Revenue' : 
                         ($type === 'service' ? 'Service Revenue' : 'Other Revenue'));

                $breakdown[] = [
                    'label' => $label,
                    'net' => $netForGroup,
                    'tax' => $taxForGroup,
                    'total' => $totalDebit
                ];
                
                if ($type !== 'room_charge') {
                    foreach($entries as $entry) {
                        $additionalCharges[] = [
                            'description' => $entry->transaction_date->format('M j, Y') . ' - ' . $entry->description,
                            'amount' => $entry->debit,
                            'type' => $type
                        ];
                        $additionalChargesTotal += $entry->debit;
                    }
                }
            }

            // Calculate total from folio
            $totalAmount = $folio['summary']['total_charges'];
            
            // Add Service Requests directly
            $rawServiceEntries = \App\Models\ServiceRequest::where('hotel_id', $hotelId)
                ->where('guest_id', $reservation->guest_id)
                ->where('room_id', $reservation->room_id)
                ->where('status', 'completed')
                ->where(function($q) use ($reservationId) {
                    $q->where('reservation_id', $reservationId)
                      ->orWhereNull('reservation_id');
                })
                ->get();

            foreach ($rawServiceEntries as $service) {
                $amount = $service->total_price ?? $service->subtotal ?? 0;
                $additionalCharges[] = [
                    'description' => ($service->completed_at ? $service->completed_at->format('M j, Y') : now()->format('M j, Y')) . ' - ' . ($service->service->name ?? 'Service Request'),
                    'amount' => $amount,
                    'type' => 'service'
                ];
                $additionalChargesTotal += $amount;
                $totalAmount += $amount;
            }

            // Add Food and Beverage Orders directly
            $rawFoodEntries = \App\Models\Order::where('hotel_id', $hotelId)
                ->where('guest_id', $reservation->guest_id)
                ->where('room_id', $reservation->room_id)
                ->where('status', 'completed')
                ->where(function($q) use ($reservationId) {
                    $q->where('reservation_id', $reservationId)
                      ->orWhereNull('reservation_id');
                })
                ->get();

            foreach ($rawFoodEntries as $food) {
                $amount = $food->total_amount ?? 0;
                $additionalCharges[] = [
                    'description' => ($food->created_at ? $food->created_at->format('M j, Y') : now()->format('M j, Y')) . ' - F&B Order ' . $food->order_number,
                    'amount' => $amount,
                    'type' => 'restaurant'
                ];
                $additionalChargesTotal += $amount;
                $totalAmount += $amount;
            }
            
            // Get payments
            $payments = $invoice->payments()->orderBy('created_at', 'asc')->get();
            $paidAmount = $payments->sum('amount') ?? 0;
            
            // Balance should never be negative
            $balance = max(0, $totalAmount - $paidAmount);
            
            // Format payment history
            $paymentHistory = [];
            foreach ($payments as $payment) {
                $paymentHistory[] = [
                    'amount' => $payment->amount,
                    'method' => $payment->payment_method,
                    'date' => $payment->created_at->format('M j, Y'),
                    'time' => $payment->created_at->format('h:i A'),
                    'reference' => $payment->reference
                ];
            }
            
            // Get receipts
            $receipts = Receipt::where('invoice_id', $invoice->id)
                ->orderBy('created_at', 'desc')
                ->get();
            
            $receiptData = [
                'reservation_id' => $reservation->id,
                'receipt_number' => 'RCPT-' . date('Ymd') . '-' . str_pad($reservation->id, 4, '0', STR_PAD_LEFT),
                'invoice_number' => $invoice->invoice_number,
                'date' => now()->format('M j, Y'),
                'time' => now()->format('h:i A'),
                'guest_name' => $guestName,
                'room_number' => $reservation->room->room_number ?? 'N/A',
                'room_type' => $reservation->room->roomType->name ?? 'N/A',
                'check_in' => $reservation->check_in->format('M j, Y'),
                'check_out' => $checkOut->format('M j, Y'),
                'total_nights' => $nights,
                'room_price' => $roomPrice,
                'room_charges' => $roomCharges,
                'vat_rate' => $vatRate,
                'vat_amount' => $vatAmount,
                'subtotal' => $subtotal,
                'overstay_days' => $overstayDays,
                'overstay_fees' => $overstayFees,
                'total_amount' => $totalAmount,
                'paid_amount' => $paidAmount,
                'balance' => $balance,
                'payment_method' => $reservation->payment_method ?? 'Not specified',
                'is_walkin' => $reservation->is_walk_in ?? false,
                'walk_in_number' => $reservation->walk_in_number ?? null,
                'payment_history' => $paymentHistory,
                'installment_count' => count($paymentHistory),
                'receipts' => $receipts,
                'nights' => $nights,
                'additional_charges' => $additionalCharges,
                'additional_charges_total' => $additionalChargesTotal,
                'breakdown' => $breakdown
            ];

            return response()->json([
                'success' => true,
                'data' => $receiptData
            ]);

        } catch (\Exception $e) {
            Log::error('Error getting receipt: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Error: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get available rooms
     */
    public function getAvailableRooms(Request $request)
    {
        try {
            $hotelId = $this->getHotelId();
            
            if (!$hotelId) {
                return response()->json(['success' => false, 'message' => 'Hotel ID not found'], 400);
            }
            
            $validator = Validator::make($request->all(), [
                'check_in_date' => 'required|date',
                'check_out_date' => 'required|date|after:check_in_date',
                'room_type_id' => [
                    'nullable',
                    Rule::exists('room_types', 'id')->where(function ($query) use ($hotelId) {
                        $query->where('hotel_id', $hotelId);
                    }),
                ],
            ]);

            if ($validator->fails()) {
                return response()->json([
                    'success' => false,
                    'errors' => $validator->errors()
                ], 422);
            }

            $checkInDate = Carbon::parse($request->check_in_date);
            $checkOutDate = Carbon::parse($request->check_out_date);
            $nights = max(1, $checkInDate->diffInDays($checkOutDate));

            $query = Room::with(['roomType', 'reservations' => function($q) use ($checkInDate, $checkOutDate) {
                $q->where(function($q2) use ($checkInDate, $checkOutDate) {
                    $q2->where('check_in', '<', $checkOutDate)
                      ->where('check_out', '>', $checkInDate)
                      ->whereIn('status', ['confirmed', 'checked_in']);
                });
            }])
            ->where('hotel_id', $hotelId)
            ->where('status', 'available')
            ->when($request->room_type_id, function($q) use ($request) {
                return $q->where('room_type_id', $request->room_type_id);
            });

            $rooms = $query->get()->filter(function($room) {
                return $room->reservations->isEmpty();
            })->map(function($room) use ($nights) {
                $price = $room->roomType->price_per_night ?? $room->price_per_night ?? 150000;
                $roomCharges = $price * $nights;
                $vatRate = (float) setting('tax_rate', 18) / 100;
                $taxAmount = ceil($roomCharges * $vatRate);
                $totalWithTax = $roomCharges + $taxAmount;
                
                return [
                    'id' => $room->id,
                    'room_number' => $room->room_number,
                    'type' => $room->roomType->name ?? 'Standard',
                    'price_per_night' => $price,
                    'price' => $price,
                    'capacity' => $room->capacity ?? 2,
                    'floor' => $room->floor ?? 'Ground',
                    'total_for_nights' => $roomCharges,
                    'total_with_vat' => $totalWithTax,
                    'vat_amount' => $taxAmount,
                    'nights' => $nights
                ];
            })->values();

            return response()->json([
                'success' => true,
                'data' => $rooms,
                'meta' => [
                    'nights' => $nights,
                    'check_in' => $checkInDate->format('Y-m-d'),
                    'check_out' => $checkOutDate->format('Y-m-d'),
                    'vat_rate' => setting('tax_rate', 18)
                ]
            ]);

        } catch (\Exception $e) {
            Log::error('Error getting available rooms: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Error: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Show consolidated invoice for guest
     */
    public function showConsolidatedInvoice($reservationId)
    {
        try {
            $hotelId = $this->getHotelId();
            if (!$hotelId) {
                return response()->json(['success' => false, 'message' => 'Hotel ID not found'], 400);
            }

            $reservation = Reservation::with(['guest', 'room'])
                ->where('hotel_id', $hotelId)
                ->findOrFail($reservationId);

            $folio = $this->ledgerService->getGuestFolio($reservationId);
            $invoice = Invoice::where('reservation_id', $reservationId)->first();

            $hotelName = hotelName();
            $hotelEmail = hotelEmail();
            $hotelAddress = hotelAddress();
            $hotelPhone = hotelPhone();
            $nights = $reservation->check_in->diffInDays($reservation->check_out);

            $totalCharges = $folio['summary']['total_charges'];
            $totalPayments = $folio['summary']['total_payments'];
            $balance = $folio['summary']['balance'];
            $ledgerEntries = $folio['entries'] ?? [];

            // Fetch service charges for this guest and reservation
            $rawServiceEntries = \App\Models\ServiceRequest::where('hotel_id', $hotelId)
                ->where('guest_id', $reservation->guest_id)
                ->where('room_id', $reservation->room_id)
                ->where('status', 'completed')
                ->where(function($q) use ($reservationId) {
                    $q->where('reservation_id', $reservationId)
                      ->orWhereNull('reservation_id'); // fallback for legacy
                })
                ->orderBy('completed_at', 'desc')
                ->get();

            // Fetch food/beverage orders for this guest and reservation
            $rawFoodEntries = \App\Models\Order::where('hotel_id', $hotelId)
                ->where('guest_id', $reservation->guest_id)
                ->where('room_id', $reservation->room_id)
                ->where('status', 'completed')
                ->where(function($q) use ($reservationId) {
                    $q->where('reservation_id', $reservationId)
                      ->orWhereNull('reservation_id'); // fallback for legacy
                })
                ->with('orderItems')
                ->orderBy('created_at', 'desc')
                ->get();

            $serviceEntries = [];
            foreach ($rawServiceEntries as $service) {
                $amount = $service->total_price ?? $service->subtotal ?? 0;
                $serviceEntries[] = [
                    'date' => $service->completed_at ? $service->completed_at->format('M j, Y') : now()->format('M j, Y'),
                    'description' => $service->service->name ?? 'Hotel Service',
                    'amount' => $amount
                ];
                $totalCharges += $amount;
            }

            $foodEntries = [];
            foreach ($rawFoodEntries as $food) {
                $amount = $food->total_amount ?? 0;
                $foodEntries[] = [
                    'date' => $food->created_at ? $food->created_at->format('M j, Y') : now()->format('M j, Y'),
                    'description' => 'F&B Order ' . $food->order_number,
                    'amount' => $amount
                ];
                $totalCharges += $amount;
            }
            
            $balance = max(0, $totalCharges - $totalPayments);
            
            // Group ledger entries by type for breakdown
            $groupedCharges = $ledgerEntries->where('debit', '>', 0)->groupBy('reference_type');
            $breakdown = [];
            $vatRate = (float) setting('tax_rate', 18);
            
            foreach ($groupedCharges as $type => $entries) {
                $entriesCollection = collect($entries);
                $totalDebit = $entriesCollection->sum('debit');
                $taxForGroup = ($totalDebit * $vatRate) / (100 + $vatRate);
                $netForGroup = $totalDebit - $taxForGroup;
                
                $label = $type === 'room_charge' ? 'Room Revenue' : 
                         ($type === 'restaurant' ? 'Food and Beverage Revenue' : 
                         ($type === 'service' ? 'Service Revenue' : 'Other Revenue'));

                $breakdown[] = [
                    'label' => $label,
                    'net' => $netForGroup,
                    'tax' => $taxForGroup,
                    'total' => $totalDebit
                ];
            }

            $html = view('inout.consolidated-receipt', compact(
                'reservation',
                'invoice',
                'folio',
                'hotelName',
                'hotelAddress',
                'hotelPhone',
                'nights',
                'totalCharges',
                'totalPayments',
                'balance',
                'ledgerEntries',
                'serviceEntries',
                'foodEntries',
                'breakdown'
            ))->render();

            if (request()->wantsJson()) {
                return response()->json([
                    'success' => true,
                    'html' => $html
                ]);
            }
            return $html;
        } catch (\Exception $e) {
            Log::error('Error showing consolidated invoice: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Error: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Flag room for cleaning (Legacy - now uses Room model)
     */
    private function flagRoomForCleaning($roomId, $reservationId = null)
    {
        $room = Room::where('hotel_id', $this->getHotelId())->find($roomId);
        return $room ? $room->markAsCheckedOut() : false;
    }

    /**
     * Get guest display name
     */
    private function getGuestDisplayName(Reservation $reservation): string
    {
        if ($reservation->guest) {
            $firstName = $reservation->guest->first_name ?? '';
            $lastName = $reservation->guest->last_name ?? '';
            return trim($firstName . ' ' . $lastName) ?: 'Guest #' . $reservation->id;
        }
        return 'Guest #' . $reservation->id;
    }
}