<?php

namespace App\Services\Accounting;

use App\Models\Order;
use App\Models\ServiceRequest;
use App\Models\Reservation;
use App\Models\InventoryOrder;
use App\Models\Expense;
use App\Models\ManufacturingOrder;
use App\Models\InventoryItem;
use App\Models\Payment;
use App\Models\Invoice;
use App\Models\Account;
use App\Models\JournalEntry;
use App\Models\JournalEntryItem;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Auth;

class AccountingSyncService
{
    protected $journalEntryService;

    public function __construct(JournalEntryService $journalEntryService)
    {
        $this->journalEntryService = $journalEntryService;
    }

    /**
     * Resolve GL account by code. Uses withoutGlobalScopes so sync is not blocked by tenant scope edge cases.
     */
    protected function getAccountId($code, $hotelId = null)
    {
        $account = Account::withoutGlobalScopes()
            ->where('code', $code)
            ->where(function ($q) use ($hotelId) {
                $q->whereNull('hotel_id')
                    ->orWhere('hotel_id', $hotelId);
            })
            ->first();

        if (!$account && $code) {
            Log::warning("AccountingSyncService: Account with code {$code} not found for hotel ID {$hotelId}");
        }

        return $account ? $account->id : null;
    }

    /**
     * First matching account among preferred codes (e.g. Service Revenue 4300 vs 4100).
     */
    protected function firstAccountIdAmongCodes(array $codes, $hotelId): ?int
    {
        foreach ($codes as $code) {
            $id = $this->getAccountId($code, $hotelId);
            if ($id) {
                return $id;
            }
        }

        return null;
    }

    /**
     * Ensure minimum posting accounts exist for this hotel (service sync, cash, AR, VAT).
     */
    protected function ensureCoreServiceGlAccountsExist(int $hotelId): void
    {
        $definitions = [
            '11221' => [
                'name' => 'Hotel Guest Receivables',
                'account_type' => 'A',
                'normal_balance' => 'Dr',
                'report_section' => 'BS',
                'category' => 'Assets - Receivables',
            ],
            '11251' => [
                'name' => 'Petty Cash - General',
                'account_type' => 'A',
                'normal_balance' => 'Dr',
                'report_section' => 'BS',
                'category' => 'Assets - Cash',
            ],
            '12251' => [
                'name' => 'VAT Output Tax Payable',
                'account_type' => 'L',
                'normal_balance' => 'Cr',
                'report_section' => 'BS',
                'category' => 'Liabilities - Tax',
            ],
            '4300' => [
                'name' => 'Service Revenue',
                'account_type' => 'P',
                'normal_balance' => 'Cr',
                'report_section' => 'REVENUE',
                'category' => 'Revenue - Services',
            ],
        ];

        foreach ($definitions as $code => $meta) {
            $exists = Account::withoutGlobalScopes()
                ->where('code', $code)
                ->where(function ($q) use ($hotelId) {
                    $q->whereNull('hotel_id')->orWhere('hotel_id', $hotelId);
                })
                ->exists();

            if ($exists) {
                continue;
            }

            Account::withoutGlobalScopes()->create(array_merge([
                'hotel_id' => $hotelId,
                'code' => $code,
                'is_active' => true,
                'notes' => 'Auto-created for accounting sync',
            ], $meta));
        }
    }

    /**
     * Get account based on payment method
     */
    protected function getCashAccountByMethod($paymentMethod, $hotelId = null)
    {
        $map = [
            'cash' => '11251', // Petty Cash-General
            'credit_card' => '11241', // Stanbic-UGX
            'debit_card' => '11241', // Stanbic-UGX
            'mobile_money' => '11254', // MTN Mobile Money
            'bank_transfer' => '11241', // Stanbic-UGX
        ];

        $code = $map[$paymentMethod] ?? '11251';
        return $this->getAccountId($code, $hotelId);
    }

    // ==================== POS MODULE ====================

    /**
     * Sync POS orders to accounting
     */
    public function syncOrders($hotelId)
    {
        $syncedCount = 0;
        $totalAmount = 0;
        $errors = [];

        // Select all completed POS orders NOT linked to a room (walk-ins)
        $orders = Order::where('hotel_id', $hotelId)
            ->where('status', 'completed')
            ->whereNull('room_number') // Prevent double-counting as they'll be in room checkout
            ->where('accounting_synced', false)
            ->get();

        foreach ($orders as $order) {
            try {
                DB::beginTransaction();

                // Determine which template to use based on order type
                $templateId = $this->getPosTemplate($order->order_type);
                
                // Get accounts
                $cashAccountId = $this->getCashAccountByMethod($order->payment_method ?? 'cash', $hotelId);
                $revenueAccountId = $this->getRevenueAccountForOrderType($order->order_type, $hotelId);
                $taxAccountId = $this->getAccountId('12251', $hotelId); // VAT Output Tax Payable

                if (!$cashAccountId || !$revenueAccountId) {
                    throw new \Exception("Required accounts not found for POS sync");
                }

                // Create journal entry using template
                $journalEntry = $this->journalEntryService->createFromTemplate(
                    $templateId,
                    [
                        'order_id' => $order->id,
                        'order_number' => $order->order_number,
                        'sale_amount' => $order->subtotal,
                        'tax_amount' => $order->tax_amount,
                        'total_amount' => $order->total_amount,
                        'payment_method' => $order->payment_method ?? 'cash',
                        'customer_name' => $order->customer_name ?? 'Walk-in Customer',
                        'room_number' => $order->room_number ?? null,
                        'table_number' => $order->table_number ?? null,
                    ],
                    $hotelId
                );

                // If template doesn't create the exact entry, create manually
                if (!$journalEntry) {
                    $journalEntry = JournalEntry::create([
                        'hotel_id' => $hotelId,
                        'date' => Carbon::parse($order->created_at)->toDateString(),
                        'entry_number' => JournalEntry::generateNextNumber($hotelId), // Use the new numbering trait method
                        'description' => "POS Walk-in Order #{$order->order_number}",
                        'reference' => $order->order_number,
                        'total_amount' => $order->total_amount,
                        'status' => JournalEntry::STATUS_POSTED,
                        'module_source' => 'pos',
                        'source_id' => $order->id,
                        'created_by' => Auth::id() ?? 1, // System user if not logged in
                        'posted_at' => now(),
                    ]);

                    // Create journal items
                    JournalEntryItem::create([
                        'hotel_id' => $hotelId,
                        'journal_entry_id' => $journalEntry->id,
                        'account_id' => $cashAccountId,
                        'debit' => $order->total_amount,
                        'credit' => 0,
                        'description' => "Payment received for POS order",
                    ]);

                    JournalEntryItem::create([
                        'hotel_id' => $hotelId,
                        'journal_entry_id' => $journalEntry->id,
                        'account_id' => $revenueAccountId,
                        'debit' => 0,
                        'credit' => $order->subtotal,
                        'description' => "Revenue from POS order",
                    ]);

                    if ($order->tax_amount > 0) {
                        JournalEntryItem::create([
                            'hotel_id' => $hotelId,
                            'journal_entry_id' => $journalEntry->id,
                            'account_id' => $taxAccountId,
                            'debit' => 0,
                            'credit' => $order->tax_amount,
                            'description' => "VAT collected",
                        ]);
                    }
                }

                // Mark as synced
                $order->accounting_synced = true;
                $order->accounting_sync_at = now();
                $order->save();

                DB::commit();
                
                $syncedCount++;
                $totalAmount += $order->total_amount;

            } catch (\Exception $e) {
                DB::rollBack();
                Log::error("Failed to sync POS order {$order->id}: " . $e->getMessage());
                $errors[] = "Order #{$order->order_number}: " . $e->getMessage();
            }
        }

        return [
            'success' => empty($errors),
            'module' => 'pos',
            'synced_count' => $syncedCount,
            'total_amount' => $totalAmount,
            'errors' => $errors,
            'message' => empty($errors) 
                ? "Synced {$syncedCount} POS orders totaling UGX " . number_format($totalAmount)
                : "Synced with " . count($errors) . " errors"
        ];
    }

    protected function getPosTemplate($orderType)
    {
        $templates = [
            'room_service' => 'JE-014', // Room Service
            'restaurant' => 'JE-012', // Restaurant Cash Sale
            'takeaway' => 'JE-012', // Restaurant Cash Sale
        ];
        return $templates[$orderType] ?? 'JE-012';
    }

    protected function getRevenueAccountForOrderType($orderType, $hotelId)
    {
        $accounts = [
            'room_service' => '21221', // Room Service Orders
            'restaurant' => '21211', // Restaurant & Bar Sales
            'takeaway' => '21212', // Takeaway & Delivery
        ];
        return $this->getAccountId($accounts[$orderType] ?? '21211', $hotelId);
    }

    // ==================== SERVICE REQUESTS MODULE ====================

    /**
     * Sync service requests to accounting
     */
    public function syncServiceRequests($hotelId)
    {
        $syncedCount = 0;
        $totalAmount = 0;
        $errors = [];

        $requests = ServiceRequest::with(['service', 'receipt'])
            ->where('hotel_id', $hotelId)
            ->where('status', 'completed')
            ->whereNull('reservation_id') // Folio-backed services post on reservation checkout
            ->where('accounting_synced', false)
            ->get();

        $this->ensureCoreServiceGlAccountsExist((int) $hotelId);

        foreach ($requests as $request) {
            try {
                DB::beginTransaction();

                // Get accounts (prefer 4300; some charts use 4100/4200 for service revenue)
                $receivableAccountId = $this->getAccountId('11221', $hotelId);
                $revenueAccountId = $this->firstAccountIdAmongCodes(['4300', '4100', '4200'], $hotelId);
                $taxAccountId = $this->getAccountId('12251', $hotelId);

                // Paid at desk → debit cash/bank; otherwise receivable (e.g. invoice pending)
                $debitAccountId = $request->receipt_id
                    ? $this->getCashAccountByMethod($request->receipt->payment_method ?? 'cash', $hotelId)
                    : $receivableAccountId;
                if (!$debitAccountId) {
                    $debitAccountId = $receivableAccountId;
                }

                if (!$receivableAccountId || !$revenueAccountId || !$taxAccountId) {
                    throw new \Exception(
                        'Required GL accounts not found after auto-setup. Expected receivables 11221, revenue 4300/4100/4200, VAT 12251. ' .
                        'Run Chart of Accounts setup or open Accounting → Chart of Accounts for this hotel.'
                    );
                }

                // total_price is VAT-inclusive (same as ServiceController::assign)
                $gross = (float) $request->total_price;
                $taxRatePct = (float) setting('tax_rate', 18);
                $vatAmount = round($gross * ($taxRatePct / (100 + $taxRatePct)), 2);
                $netRevenue = round(max(0, $gross - $vatAmount), 2);

                $serviceLabel = $request->service?->name ?? 'Service';

                // Create journal entry
                $journalEntry = JournalEntry::create([
                    'hotel_id' => $hotelId,
                    'date' => Carbon::parse($request->completed_at ?? $request->updated_at)->toDateString(),
                    'entry_number' => JournalEntry::generateNextNumber($hotelId),
                    'description' => "Service: {$serviceLabel} for guest #{$request->guest_id}",
                    'reference' => "SRQ-{$request->id}",
                    'total_amount' => $gross,
                    'status' => JournalEntry::STATUS_POSTED,
                    'module_source' => 'service_request',
                    'source_id' => $request->id,
                    'created_by' => Auth::id() ?? 1,
                    'posted_at' => now(),
                ]);

                // Debit entry (Cash or Receivables)
                JournalEntryItem::create([
                    'hotel_id' => $hotelId,
                    'journal_entry_id' => $journalEntry->id,
                    'account_id' => $debitAccountId,
                    'debit' => $gross,
                    'credit' => 0,
                    'description' => "Service payment received",
                ]);

                // Credit entry (Revenue — net of VAT)
                JournalEntryItem::create([
                    'hotel_id' => $hotelId,
                    'journal_entry_id' => $journalEntry->id,
                    'account_id' => $revenueAccountId,
                    'debit' => 0,
                    'credit' => $netRevenue,
                    'description' => "Service revenue (net)",
                ]);

                // VAT
                if ($vatAmount > 0) {
                    JournalEntryItem::create([
                        'hotel_id' => $hotelId,
                        'journal_entry_id' => $journalEntry->id,
                        'account_id' => $taxAccountId,
                        'debit' => 0,
                        'credit' => $vatAmount,
                        'description' => "VAT on service",
                    ]);
                }

                // Mark as synced
                $request->accounting_synced = true;
                $request->accounting_sync_at = now();
                $request->save();

                DB::commit();

                $syncedCount++;
                $totalAmount += $gross;

            } catch (\Exception $e) {
                DB::rollBack();
                Log::error("Failed to sync service request {$request->id}: " . $e->getMessage());
                $errors[] = "Service #{$request->id}: " . $e->getMessage();
            }
        }

        return [
            'success' => empty($errors),
            'module' => 'services',
            'synced_count' => $syncedCount,
            'total_amount' => $totalAmount,
            'errors' => $errors,
            'message' => empty($errors)
                ? "Synced {$syncedCount} services totaling UGX " . number_format($totalAmount)
                : "Synced with " . count($errors) . " errors"
        ];
    }

    // ==================== RESERVATIONS (FRONT DESK) MODULE ====================

    /**
     * Sync reservations to accounting
     */
    public function syncReservations($hotelId)
    {
        $syncedCount = 0;
        $totalAmount = 0;
        $errors = [];

        $reservations = Reservation::where('hotel_id', $hotelId)
            ->where('status', 'checked_out')
            ->where('accounting_synced', false)
            ->get();

        foreach ($reservations as $reservation) {
            try {
                DB::beginTransaction();

                // Determine revenue account based on source
                $revenueAccountCode = $this->getRoomRevenueAccount($reservation);
                $revenueAccountId = $this->getAccountId($revenueAccountCode, $hotelId);
                
                // Always debit Receivables for the full reservation amount.
                // Actual cash receipts are handled separately by the syncPayments method.
                $paymentAccountId = $this->getAccountId('11221', $hotelId); // Receivables

                $taxAccountId = $this->getAccountId('12251', $hotelId);

                // Prefer consolidated folio invoice (room + spa/services/POS charges) over reservation.room total only.
                $folioInvoice = Invoice::where('hotel_id', $hotelId)
                    ->where('reservation_id', $reservation->id)
                    ->orderByDesc('updated_at')
                    ->first();

                $grossTotal = (float) $reservation->total_amount;
                if ($folioInvoice && (float) $folioInvoice->total_amount > 0) {
                    $grossTotal = (float) $folioInvoice->total_amount;
                    $taxAmount = (float) ($folioInvoice->tax_amount ?? 0);
                    $totalRevenue = (float) ($folioInvoice->subtotal ?? max(0, $grossTotal - $taxAmount));
                } else {
                    $taxRate = (float) setting('tax_rate', 18) / 100;
                    $totalRevenue = $grossTotal / (1 + $taxRate);
                    $taxAmount = $grossTotal - $totalRevenue;
                }

                // Check for existing entry to prevent duplication
                $existingEntry = JournalEntry::where('module_source', 'reservation')
                    ->where('source_id', $reservation->id)
                    ->first();

                if ($existingEntry) {
                    $reservation->accounting_synced = true;
                    $reservation->accounting_sync_at = now();
                    $reservation->save();
                    continue;
                }

                $entryDescription = $folioInvoice && (float) $folioInvoice->total_amount > 0
                    ? "Reservation #{$reservation->reservation_number} - Folio (room + extras)"
                    : "Room reservation #{$reservation->reservation_number} - Consolidated";

                // Create journal entry
                $journalEntry = JournalEntry::create([
                    'hotel_id' => $hotelId,
                    'date' => Carbon::parse($reservation->actual_check_out ?? $reservation->check_out)->toDateString(),
                    'entry_number' => JournalEntry::generateNextNumber($hotelId),
                    'description' => $entryDescription,
                    'reference' => $reservation->reservation_number,
                    'total_amount' => $grossTotal,
                    'status' => JournalEntry::STATUS_POSTED,
                    'module_source' => 'reservation',
                    'source_id' => $reservation->id,
                    'created_by' => Auth::id() ?? 1,
                    'posted_at' => now(),
                ]);

                // Debit Receivables (Money owed by guest)
                JournalEntryItem::create([
                    'hotel_id' => $hotelId,
                    'journal_entry_id' => $journalEntry->id,
                    'account_id' => $paymentAccountId,
                    'debit' => $grossTotal,
                    'credit' => 0,
                    'description' => "Consolidated guest balance at checkout",
                ]);

                // Credit Room Revenue (Net of VAT)
                JournalEntryItem::create([
                    'hotel_id' => $hotelId,
                    'journal_entry_id' => $journalEntry->id,
                    'account_id' => $revenueAccountId,
                    'debit' => 0,
                    'credit' => $totalRevenue,
                    'description' => "Consolidated reservation revenue",
                ]);

                // Credit VAT Payable
                if ($taxAmount > 0) {
                    JournalEntryItem::create([
                        'hotel_id' => $hotelId,
                        'journal_entry_id' => $journalEntry->id,
                        'account_id' => $taxAccountId,
                        'debit' => 0,
                        'credit' => $taxAmount,
                        'description' => "VAT collected at checkout",
                    ]);
                }

                // Mark as synced
                $reservation->accounting_synced = true;
                $reservation->accounting_sync_at = now();
                $reservation->save();

                DB::commit();

                $syncedCount++;
                $totalAmount += $grossTotal;

            } catch (\Exception $e) {
                DB::rollBack();
                Log::error("Failed to sync reservation {$reservation->id}: " . $e->getMessage());
                $errors[] = "Reservation #{$reservation->reservation_number}: " . $e->getMessage();
            }
        }

        return [
            'success' => empty($errors),
            'module' => 'rooms',
            'synced_count' => $syncedCount,
            'total_amount' => $totalAmount,
            'errors' => $errors,
            'message' => empty($errors)
                ? "Synced {$syncedCount} reservations totaling UGX " . number_format($totalAmount)
                : "Synced with " . count($errors) . " errors"
        ];
    }

    protected function getRoomRevenueAccount($reservation)
    {
        if ($reservation->is_walk_in) {
            return '21111'; // Guests-Walk In
        }
        
        switch ($reservation->source) {
            case 'ota':
                return '21112'; // Guests-OTA
            case 'website':
                return '21113'; // Guests-Website
            case 'corporate':
                return '21123'; // Corporate Rates
            default:
                return '21111';
        }
    }

    // ==================== PURCHASE ORDERS MODULE ====================

    /**
     * Sync purchase orders to accounting
     */
    public function syncInventoryOrders($hotelId)
    {
        $syncedCount = 0;
        $totalAmount = 0;
        $errors = [];

        $purchaseOrders = InventoryOrder::where('hotel_id', $hotelId)
            ->where('status', 'received')
            ->where('accounting_synced', false)
            ->get();

        foreach ($purchaseOrders as $po) {
            try {
                DB::beginTransaction();

                // Determine inventory account based on category
                $inventoryAccountCode = $this->getInventoryAccountForItem($po->item);
                $inventoryAccountId = $this->getAccountId($inventoryAccountCode, $hotelId);
                
                $payableAccountId = $this->getAccountId('12211', $hotelId); // Trade Creditors
                $taxRecoverableId = $this->getAccountId('12252', $hotelId); // VAT Input Tax Recoverable

                $taxAmount = $po->total_amount * (setting('tax_rate', 18) / 100);
                $netAmount = $po->total_amount;

                // Create journal entry
                $journalEntry = JournalEntry::create([
                    'hotel_id' => $hotelId,
                    'date' => Carbon::parse($po->received_date ?? now())->toDateString(),
                    'entry_number' => JournalEntry::generateNextNumber($hotelId),
                    'description' => "Purchase order #{$po->po_number} - {$po->item->name}",
                    'reference' => $po->po_number,
                    'total_amount' => $po->total_amount,
                    'status' => JournalEntry::STATUS_POSTED,
                    'module_source' => 'purchase_order',
                    'source_id' => $po->id,
                    'created_by' => Auth::id() ?? 1,
                    'posted_at' => now(),
                ]);

                // Debit inventory
                JournalEntryItem::create([
                    'hotel_id' => $hotelId,
                    'journal_entry_id' => $journalEntry->id,
                    'account_id' => $inventoryAccountId,
                    'debit' => $netAmount,
                    'credit' => 0,
                    'description' => "Inventory purchase",
                ]);

                // Debit VAT recoverable
                if ($taxAmount > 0) {
                    JournalEntryItem::create([
                        'hotel_id' => $hotelId,
                        'journal_entry_id' => $journalEntry->id,
                        'account_id' => $taxRecoverableId,
                        'debit' => $taxAmount,
                        'credit' => 0,
                        'description' => "Input VAT recoverable",
                    ]);
                }

                // Credit accounts payable
                JournalEntryItem::create([
                    'hotel_id' => $hotelId,
                    'journal_entry_id' => $journalEntry->id,
                    'account_id' => $payableAccountId,
                    'debit' => 0,
                    'credit' => $po->total_amount,
                    'description' => "Amount payable to supplier",
                ]);

                // Mark as synced
                $po->accounting_synced = true;
                $po->accounting_sync_at = now();
                $po->save();

                DB::commit();

                $syncedCount++;
                $totalAmount += $po->total_amount;

            } catch (\Exception $e) {
                DB::rollBack();
                Log::error("Failed to sync purchase order {$po->id}: " . $e->getMessage());
                $errors[] = "PO #{$po->po_number}: " . $e->getMessage();
            }
        }

        return [
            'success' => empty($errors),
            'module' => 'purchase_orders',
            'synced_count' => $syncedCount,
            'total_amount' => $totalAmount,
            'errors' => $errors,
            'message' => empty($errors)
                ? "Synced {$syncedCount} purchase orders totaling UGX " . number_format($totalAmount)
                : "Synced with " . count($errors) . " errors"
        ];
    }

    protected function getInventoryAccountForItem($item)
    {
        if (!$item || !$item->category) {
            return '11212'; // Default F&B Stock
        }

        $categoryMap = [
            'Food' => '11212',
            'Beverage' => '11212',
            'Cleaning' => '11211',
            'Room' => '11211',
            'Office' => '11211',
        ];

        foreach ($categoryMap as $keyword => $account) {
            if (stripos($item->category, $keyword) !== false) {
                return $account;
            }
        }

        return '11212';
    }

    // ==================== EXPENSES MODULE ====================

    /**
     * Sync expenses to accounting
     */
    public function syncExpenses($hotelId)
    {
        $syncedCount = 0;
        $totalAmount = 0;
        $errors = [];

        $expenses = Expense::where('hotel_id', $hotelId)
            ->where('status', 'approved')
            ->where('accounting_synced', false)
            ->get();

        foreach ($expenses as $expense) {
            try {
                DB::beginTransaction();

                // Map expense category to GL account
                $expenseAccountCode = $this->getExpenseAccount($expense->category);
                $expenseAccountId = $this->getAccountId($expenseAccountCode, $hotelId);
                
                $cashAccountId = $this->getCashAccountByMethod($expense->payment_method ?? 'cash', $hotelId);
                $taxRecoverableId = $this->getAccountId('12252', $hotelId); // VAT Input Tax Recoverable

                $taxAmount = $expense->amount * (setting('tax_rate', 18) / 100);
                $netAmount = $expense->amount;

                // Create journal entry
                $journalEntry = JournalEntry::create([
                    'hotel_id' => $hotelId,
                    'date' => Carbon::parse($expense->date)->toDateString(),
                    'entry_number' => JournalEntry::generateNextNumber($hotelId),
                    'description' => $expense->description ?? "Expense payment",
                    'reference' => "EXP-{$expense->id}",
                    'total_amount' => $expense->amount,
                    'status' => JournalEntry::STATUS_POSTED,
                    'module_source' => 'expense',
                    'source_id' => $expense->id,
                    'created_by' => Auth::id() ?? 1,
                    'posted_at' => now(),
                ]);

                // Debit expense
                JournalEntryItem::create([
                    'hotel_id' => $hotelId,
                    'journal_entry_id' => $journalEntry->id,
                    'account_id' => $expenseAccountId,
                    'debit' => $netAmount,
                    'credit' => 0,
                    'description' => "Expense",
                ]);

                // Debit VAT recoverable if applicable
                if ($taxAmount > 0) {
                    JournalEntryItem::create([
                        'hotel_id' => $hotelId,
                        'journal_entry_id' => $journalEntry->id,
                        'account_id' => $taxRecoverableId,
                        'debit' => $taxAmount,
                        'credit' => 0,
                        'description' => "Input VAT recoverable",
                    ]);
                }

                // Credit cash/bank
                JournalEntryItem::create([
                    'hotel_id' => $hotelId,
                    'journal_entry_id' => $journalEntry->id,
                    'account_id' => $cashAccountId,
                    'debit' => 0,
                    'credit' => $expense->amount,
                    'description' => "Payment for expense",
                ]);

                // Mark as synced
                $expense->accounting_synced = true;
                $expense->accounting_synced_at = now();
                $expense->save();

                DB::commit();

                $syncedCount++;
                $totalAmount += $expense->amount;

            } catch (\Exception $e) {
                DB::rollBack();
                Log::error("Failed to sync expense {$expense->id}: " . $e->getMessage());
                $errors[] = "Expense #{$expense->id}: " . $e->getMessage();
            }
        }

        return [
            'success' => empty($errors),
            'module' => 'expenses',
            'synced_count' => $syncedCount,
            'total_amount' => $totalAmount,
            'errors' => $errors,
            'message' => empty($errors)
                ? "Synced {$syncedCount} expenses totaling UGX " . number_format($totalAmount)
                : "Synced with " . count($errors) . " errors"
        ];
    }

    protected function getExpenseAccount($category)
    {
        $categoryMap = [
            'Salary' => '23111',
            'Wage' => '23111',
            'NSSF' => '23121',
            'PAYE' => '23122',
            'Pension' => '23121',
            'Rent' => '23200',
            'Lease' => '23200',
            'Electricity' => '23221',
            'Power' => '23221',
            'Water' => '23222',
            'Sewage' => '23222',
            'Marketing' => '23300',
            'Advertising' => '23300',
            'OTA' => '23312',
            'Commission' => '23312',
            'Depreciation' => '23511',
            'Repair' => '5400',
            'Maintenance' => '5400',
            'Office' => '5600',
            'Admin' => '5600',
        ];

        foreach ($categoryMap as $keyword => $account) {
            if (stripos($category, $keyword) !== false) {
                return $account;
            }
        }

        return '5600'; // Default administrative
    }

    // ==================== PAYMENTS MODULE ====================

    /**
     * Sync payments to accounting
     */
    public function syncPayments($hotelId)
    {
        $syncedCount = 0;
        $totalAmount = 0;
        $errors = [];

        $payments = Payment::where('hotel_id', $hotelId)
            ->where('status', 'completed')
            ->where('accounting_synced', false)
            ->get();

        foreach ($payments as $payment) {
            try {
                DB::beginTransaction();

                // Get cash/bank account based on payment method
                $cashAccountId = $this->getCashAccountByMethod($payment->payment_method ?? 'cash', $hotelId);
                
                // Credit the Receivables account. If payment is for a Vendor/Expense, we'd credit Payables.
                // Assuming most standalone payments here are Front Desk / Guest payments:
                $creditAccountId = $this->getAccountId('11221', $hotelId); // Hotel Guest Receivables

                if (!$cashAccountId || !$creditAccountId) {
                    throw new \Exception("Required accounts not found for Payment sync");
                }

                $dateToUse = $payment->paid_at ?? $payment->payment_date ?? $payment->date ?? now();

                // Check for existing entry to prevent duplication
                $existingEntry = JournalEntry::where('module_source', 'payment')
                    ->where('source_id', $payment->id)
                    ->first();

                if ($existingEntry) {
                    $payment->accounting_synced = true;
                    $payment->accounting_sync_at = now();
                    $payment->save();
                    continue;
                }

                // Create journal entry
                $journalEntry = JournalEntry::create([
                    'hotel_id' => $hotelId,
                    'date' => Carbon::parse($dateToUse)->toDateString(),
                    'entry_number' => JournalEntry::generateNextNumber($hotelId),
                    'description' => $payment->notes ?? "Payment Receipt #{$payment->payment_number}",
                    'reference' => $payment->payment_number,
                    'total_amount' => $payment->amount,
                    'status' => JournalEntry::STATUS_POSTED,
                    'module_source' => 'payment',
                    'source_id' => $payment->id,
                    'created_by' => Auth::id() ?? 1,
                    'posted_at' => now(),
                ]);

                // Debit Cash
                JournalEntryItem::create([
                    'hotel_id' => $hotelId,
                    'journal_entry_id' => $journalEntry->id,
                    'account_id' => $cashAccountId,
                    'debit' => $payment->amount,
                    'credit' => 0,
                    'description' => "Cash received",
                ]);

                // Credit Receivables
                JournalEntryItem::create([
                    'hotel_id' => $hotelId,
                    'journal_entry_id' => $journalEntry->id,
                    'account_id' => $creditAccountId,
                    'debit' => 0,
                    'credit' => $payment->amount,
                    'description' => "Payment applied to receivables",
                ]);

                // Mark as synced
                $payment->accounting_synced = true;
                $payment->accounting_sync_at = now();
                $payment->save();

                DB::commit();

                $syncedCount++;
                $totalAmount += $payment->amount;

            } catch (\Exception $e) {
                DB::rollBack();
                Log::error("Failed to sync payment {$payment->id}: " . $e->getMessage());
                $errors[] = "Payment #{$payment->payment_number}: " . $e->getMessage();
            }
        }

        return [
            'success' => empty($errors),
            'module' => 'payments',
            'synced_count' => $syncedCount,
            'total_amount' => $totalAmount,
            'errors' => $errors,
            'message' => empty($errors)
                ? "Synced {$syncedCount} payments totaling UGX " . number_format($totalAmount)
                : "Synced with " . count($errors) . " errors"
        ];
    }

    // ==================== SYNC ALL MODULES ====================

    /**
     * Sync all modules
     */
    public function syncAll($hotelId)
    {
        $results = [];

        $results['pos'] = $this->syncOrders($hotelId);
        $results['services'] = $this->syncServiceRequests($hotelId);
        $results['rooms'] = $this->syncReservations($hotelId);
        $results['purchase_orders'] = $this->syncInventoryOrders($hotelId);
        $results['expenses'] = $this->syncExpenses($hotelId);
        $results['payments'] = $this->syncPayments($hotelId);

        $totalSynced = collect($results)->sum('synced_count');
        $totalAmount = collect($results)->sum('total_amount');
        $hasErrors = collect($results)->contains('success', false);

        return [
            'success' => !$hasErrors,
            'results' => $results,
            'total_synced' => $totalSynced,
            'total_amount' => $totalAmount,
            'message' => "Synced {$totalSynced} records totaling UGX " . number_format($totalAmount)
        ];
    }

    /**
     * Get sync status for all modules
     */
    public function getSyncStatus($hotelId)
    {
        $status = [];

        // POS Orders
        $status['pos'] = [
            'pending_count' => Order::where('hotel_id', $hotelId)
                ->where('status', 'completed')
                ->where('accounting_synced', false)
                ->count(),
            'last_sync' => Order::where('hotel_id', $hotelId)
                ->where('accounting_synced', true)
                ->max('accounting_sync_at'),
        ];

        // Service Requests
        $status['services'] = [
            'pending_count' => ServiceRequest::whereHas('service', function($q) use ($hotelId) {
                    $q->where('hotel_id', $hotelId);
                })
                ->where('status', 'completed')
                ->where('accounting_synced', false)
                ->count(),
            'last_sync' => ServiceRequest::whereHas('service', function($q) use ($hotelId) {
                    $q->where('hotel_id', $hotelId);
                })
                ->where('accounting_synced', true)
                ->max('accounting_sync_at'),
        ];

        // Reservations
        $status['rooms'] = [
            'pending_count' => Reservation::where('hotel_id', $hotelId)
                ->where('status', 'checked_out')
                ->where('accounting_synced', false)
                ->count(),
            'last_sync' => Reservation::where('hotel_id', $hotelId)
                ->where('accounting_synced', true)
                ->max('accounting_sync_at'),
        ];

        // Purchase Orders
        $status['purchase_orders'] = [
            'pending_count' => InventoryOrder::where('hotel_id', $hotelId)
                ->where('status', 'received')
                ->where('accounting_synced', false)
                ->count(),
            'last_sync' => InventoryOrder::where('hotel_id', $hotelId)
                ->where('accounting_synced', true)
                ->max('accounting_sync_at'),
        ];

        // Expenses
        $status['expenses'] = [
            'pending_count' => Expense::where('hotel_id', $hotelId)
                ->where('status', 'approved')
                ->where('accounting_synced', false)
                ->count(),
            'last_sync' => Expense::where('hotel_id', $hotelId)
                ->where('accounting_synced', true)
                ->max('accounting_synced_at'),
        ];

        // Payments
        $status['payments'] = [
            'pending_count' => Payment::where('hotel_id', $hotelId)
                ->where('status', 'completed')
                ->where('accounting_synced', false)
                ->count(),
            'last_sync' => Payment::where('hotel_id', $hotelId)
                ->where('accounting_synced', true)
                ->max('accounting_sync_at'),
        ];

        return $status;
    }
}