<?php

namespace App\Services;

use App\Models\Account;
use App\Models\JournalEntry;
use App\Models\JournalEntryLine;
use App\Models\Sale;
use App\Models\PosSession;
use App\Models\CashTransaction;
use App\Models\Setting;
use App\Models\PurchaseOrder;
use Illuminate\Support\Facades\DB;

class AccountingService
{
    /**
     * Account codes - these should match the seeded accounts
     */
    const CASH_DRAWER = '1110';
    const CASH_ON_HAND = '1100';
    const BANK_ACCOUNT = '1200';
    const ACCOUNTS_RECEIVABLE = '1300';
    const INVENTORY = '1400';
    const INTERMEDIATE_SALES = '1500';
    const ACCOUNTS_PAYABLE = '2100';
    const SALES_REVENUE = '4100';
    const COST_OF_GOODS = '5100';
    
    const CASH_OVERAGE = '4900';
    const CASH_SHORTAGE = '5900';
    
    /**
     * Get account by code
     */
    protected function getAccountByCode(string $code): ?Account
    {
        return Account::where('code', $code)->first();
    }

    /**
     * Determine which account to debit for a sale
     */
    protected function determinePaymentAccount(Sale $sale, Account $cashAccount, Account $bankAccount, Account $arAccount): Account
    {
        if ($sale->is_credit) {
            return $arAccount;
        }

        // If payment method is set, check if it's bank/card
        if ($sale->payment_method_id) {
            $paymentMethod = \App\Models\PaymentMethod::find($sale->payment_method_id);
            if ($paymentMethod) {
                $name = strtolower($paymentMethod->name);
                if (str_contains($name, 'card') || str_contains($name, 'bank') || str_contains($name, 'visa') || str_contains($name, 'mastercard')) {
                    return $bankAccount;
                }
            }
        }

        return $cashAccount;
    }

    /**
     * Create journal entry for a sale
     * 
     * Debit: Cash/Bank/Accounts Receivable (where money comes from)
     * Credit: Sales Revenue (income)
     * Debit: COGS (expense for cost)
     * Credit: Inventory (reduce asset)
     */
    public function createSaleJournalEntry(Sale $sale): ?JournalEntry
    {
        $sale->load('items.product.category');
        
        $cashAccount = $this->getAccountByCode(Setting::get('account_cash_drawer', self::CASH_DRAWER));
        $bankAccount = $this->getAccountByCode(Setting::get('account_bank', self::BANK_ACCOUNT));
        $revenueAccount = $this->getAccountByCode(Setting::get('account_sales_revenue', self::SALES_REVENUE));
        $inventoryAccount = $this->getAccountByCode(Setting::get('account_inventory', self::INVENTORY));
        $cogsAccount = $this->getAccountByCode(Setting::get('account_cogs', self::COST_OF_GOODS));
        $arAccount = $this->getAccountByCode(Setting::get('account_receivable', self::ACCOUNTS_RECEIVABLE));

        if (!$cashAccount || !$revenueAccount || !$bankAccount || !$arAccount) {
            \Log::error("Missing accounts for Sale #{$sale->sale_number}. Cash: " . ($cashAccount ? 'OK' : 'MISSING') . ", Bank: " . ($bankAccount ? 'OK' : 'MISSING') . ", AR: " . ($arAccount ? 'OK' : 'MISSING'));
            return null;
        }

        return DB::transaction(function () use ($sale, $cashAccount, $bankAccount, $revenueAccount, $inventoryAccount, $cogsAccount, $arAccount) {
            // 1. Create Sale Revenue/Payment journal entry
            $entry = JournalEntry::create([
                'date' => $sale->created_at->toDateString(),
                'reference' => 'SALE-' . $sale->sale_number,
                'reference_type' => Sale::class,
                'reference_id' => $sale->id,
                'description' => __('Sale') . ' #' . $sale->sale_number,
                'partner_id' => $sale->partner_id, // Associate customer
                'branch_id' => $sale->branch_id,
                'created_by' => $sale->user_id,
                'is_auto' => true,
                'status' => 'pending',
            ]);

            // Create debit entries for each payment method + unpaid balance
            // Load sale payments with payment methods
            $sale->load('payments.paymentMethod');
            
            // Process each actual payment received
            foreach ($sale->payments as $payment) {
                // Determine account based on payment method
                $account = $cashAccount; // Default to cash
                
                if ($payment->paymentMethod) {
                    $methodCode = strtolower($payment->paymentMethod->code ?? '');
                    $methodName = strtolower($payment->paymentMethod->name ?? '');
                    
                    // Map to appropriate account
                    if ($methodCode === 'card' || $methodCode === 'bank' || 
                        str_contains($methodName, 'card') || str_contains($methodName, 'bank') || 
                        str_contains($methodName, 'visa') || str_contains($methodName, 'mastercard')) {
                        $account = $bankAccount;
                    } elseif ($methodCode === 'cash') {
                        $account = $cashAccount;
                    }
                }
                
                // Debit: Cash/Bank account (asset increases)
                JournalEntryLine::create([
                    'journal_entry_id' => $entry->id,
                    'account_id' => $account->id,
                    'debit' => $payment->amount,
                    'credit' => 0,
                    'description' => __('Payment received via ') . ($payment->paymentMethod->localized_name ?? __('Cash')),
                ]);
            }
            
            // Handle unpaid balance (credit sale / pay later)
            $unpaidBalance = $sale->total - $sale->paid;
            if ($unpaidBalance > 0.01 && $sale->partner_id) {
                // Debit: Accounts Receivable (customer owes us)
                JournalEntryLine::create([
                    'journal_entry_id' => $entry->id,
                    'account_id' => $arAccount->id,
                    'debit' => round($unpaidBalance, 2),
                    'credit' => 0,
                    'description' => __('Accounts receivable - unpaid balance'),
                ]);
            }

            // Calculate Revenue Split by Category
            $revenueByAccount = [];
            $totalItemRevenue = 0;

            foreach ($sale->items as $item) {
                $accountId = $item->product?->category?->income_account_id ?? $revenueAccount->id;
                
                if (!isset($revenueByAccount[$accountId])) {
                    $revenueByAccount[$accountId] = 0;
                }
                
                $revenueByAccount[$accountId] += $item->total;
                $totalItemRevenue += $item->total;
            }

            // Handle Global Discount Proration
            $globalDiscount = max(0, $totalItemRevenue - $sale->total);
            
            foreach ($revenueByAccount as $accountId => $amount) {
                if ($amount <= 0) continue;

                if ($globalDiscount > 0 && $totalItemRevenue > 0) {
                    $weight = $amount / $totalItemRevenue;
                    $proratedDiscount = $globalDiscount * $weight;
                    $amount -= $proratedDiscount;
                }

                if ($amount > 0) {
                    JournalEntryLine::create([
                        'journal_entry_id' => $entry->id,
                        'account_id' => $accountId,
                        'debit' => 0,
                        'credit' => round($amount, 2),
                        'description' => __('Sales revenue'),
                    ]);
                }
            }
            
            // Balance rounding
            $totalCredited = JournalEntryLine::where('journal_entry_id', $entry->id)
                ->where('credit', '>', 0)
                ->sum('credit');
            
            $diff = $sale->total - $totalCredited;
            if (abs($diff) > 0.001) {
                $firstCreditLine = JournalEntryLine::where('journal_entry_id', $entry->id)
                    ->where('credit', '>', 0)
                    ->first();
                if ($firstCreditLine) {
                    $firstCreditLine->update(['credit' => $firstCreditLine->credit + $diff]);
                }
            }

            // 2. Create SEPARATE COGS journal entry
            // Calculate COGS Split by Category
            $cogsByAccount = [];
            $totalCogs = 0;

            foreach ($sale->items as $item) {
                $cogsAccountId = $item->product?->category?->cogs_account_id ?? $cogsAccount->id;
                
                if (!isset($cogsByAccount[$cogsAccountId])) {
                    $cogsByAccount[$cogsAccountId] = 0;
                }
                
                $itemCogs = $item->quantity * $item->cost_price; // Use cost_price (stored in sale_items)
                $cogsByAccount[$cogsAccountId] += $itemCogs;
                $totalCogs += $itemCogs;
            }

            if ($totalCogs > 0) {
                if ($inventoryAccount) {
                    $cogsEntry = JournalEntry::create([
                        'date' => $sale->created_at->toDateString(),
                        'reference' => 'COGS-' . $sale->sale_number,
                        'reference_type' => Sale::class,
                        'reference_id' => $sale->id,
                        'description' => __('COGS & Inventory') . ' - ' . __('Sale') . ' #' . $sale->sale_number,
                        'branch_id' => $sale->branch_id,
                        'created_by' => $sale->user_id,
                        'is_auto' => true,
                        'status' => 'pending',
                    ]);

                    // Debit: COGS (expense increases) - Split by Category Account
                    foreach ($cogsByAccount as $accountId => $amount) {
                        if ($amount > 0) {
                            JournalEntryLine::create([
                                'journal_entry_id' => $cogsEntry->id,
                                'account_id' => $accountId, // Category specific COGS account
                                'debit' => round($amount, 2),
                                'credit' => 0,
                                'description' => __('Cost of goods sold'),
                            ]);
                        }
                    }

                    // Credit: Inventory (asset decreases) - Total Amount
                    JournalEntryLine::create([
                        'journal_entry_id' => $cogsEntry->id,
                        'account_id' => $inventoryAccount->id,
                        'debit' => 0,
                        'credit' => round($totalCogs, 2),
                        'description' => __('Inventory reduction'),
                    ]);
                    
                     // Balance rounding check for COGS entry
                    $totalDebited = JournalEntryLine::where('journal_entry_id', $cogsEntry->id)
                        ->where('debit', '>', 0)
                        ->sum('debit');
                    
                    $cogsDiff = round($totalCogs, 2) - $totalDebited;
                    if (abs($cogsDiff) > 0.001) {
                         $firstDebitLine = JournalEntryLine::where('journal_entry_id', $cogsEntry->id)
                            ->where('debit', '>', 0)
                            ->first();
                        if ($firstDebitLine) {
                            $firstDebitLine->update(['debit' => $firstDebitLine->debit + $cogsDiff]);
                        }
                    }

                } else {
                    \Log::warning("COGS journal skipped for Sale #{$sale->sale_number}: Inventory account not mapped");
                }
            }

            return $entry;
        });
    }

    /**
     * Create journal entry for session open (Transfer float from Intermediate to Drawer)
     */
    public function createSessionOpenJournalEntry(PosSession $session): ?JournalEntry
    {
        $cashDrawer = $this->getAccountByCode(Setting::get('account_cash_drawer', self::CASH_DRAWER));
        $intermediateAccount = $this->getAccountByCode(Setting::get('account_intermediate_sales', self::INTERMEDIATE_SALES));

        if (!$cashDrawer || !$intermediateAccount || $session->opening_cash <= 0) {
            return null;
        }
        
        return DB::transaction(function () use ($session, $cashDrawer, $intermediateAccount) {
            $entry = JournalEntry::create([
                'date' => now()->toDateString(),
                'reference' => 'SESSION-OPEN-' . $session->id,
                'reference_type' => PosSession::class,
                'reference_id' => $session->id,
                'description' => __('POS Session Open') . ' - ' . __('Opening Balance'),
                'branch_id' => $session->branch_id,
                'created_by' => $session->user_id,
                'is_auto' => true,
                'status' => 'pending',
            ]);

            // Debit: Cash Drawer (Asset increases)
            JournalEntryLine::create([
                'journal_entry_id' => $entry->id,
                'account_id' => $cashDrawer->id,
                'debit' => $session->opening_cash,
                'credit' => 0,
                'description' => __('Opening float received'),
            ]);

            // Credit: Intermediate Account (Instead of Safe)
            JournalEntryLine::create([
                'journal_entry_id' => $entry->id,
                'account_id' => $intermediateAccount->id,
                'debit' => 0,
                'credit' => $session->opening_cash,
                'description' => __('Opening float source (Pending Review)'),
            ]);

            return $entry;
        });
    }

    /**
     * Create journal entry for session close
     * This records the summary of cash transactions during the session
     */
    public function createSessionCloseJournalEntry(PosSession $session): ?JournalEntry
    {
        $cashDrawer = $this->getAccountByCode(Setting::get('account_cash_drawer', self::CASH_DRAWER));
        $intermediateAccount = $this->getAccountByCode(Setting::get('account_intermediate_sales', self::INTERMEDIATE_SALES));

        if (!$cashDrawer || !$intermediateAccount) {
            \Log::error("Missing default accounts for Session Close. ensure accounts 1110 and 1500 exist.");
            throw new \Exception("Missing default accounts (Cash Drawer/Intermediate). Please check Accounting Settings.");
        }
        
        // Check if there is anything to record
        $closingCash = $session->actual_cash;
        $diff = abs($session->difference);
        
        \Log::info("Attempting to create Session Close Journal. Closing Cash: $closingCash, Diff: $diff");
        
        // REMOVED LOGIC: Even if 0 cash, we might want to record the "event" or there might be difference.
        // But if strictly 0 movement and 0 diff, JournalEntryLine will be empty which is invalid.
        // Let's create the Header but skip lines if 0? 
        // Better: Only return null if REALLY nothing happened.

        if ($closingCash <= 0 && $diff < 0.01) {
             \Log::info("Session Close Journal seems empty (No cash, no diff). proceeding to create header.");
        }

        return DB::transaction(function () use ($session, $cashDrawer, $intermediateAccount, $closingCash) {
            // Create journal entry for session close
            $entry = JournalEntry::create([
                'date' => now()->toDateString(),
                'reference' => 'SESSION-' . $session->id,
                'reference_type' => PosSession::class,
                'reference_id' => $session->id,
                'description' => __('POS Session Close') . ' - ' . $session->created_at->format('Y-m-d'),
                'branch_id' => $session->branch_id,
                'created_by' => $session->user_id,
                'is_auto' => true,
                'status' => 'pending',
            ]);

            // Transfer cash from drawer to Intermediate Account (for review)
            if ($closingCash > 0) {
                // Debit: Intermediate Account - increases (Pending Review)
                JournalEntryLine::create([
                    'journal_entry_id' => $entry->id,
                    'account_id' => $intermediateAccount->id,
                    'debit' => $closingCash,
                    'credit' => 0,
                    'description' => __('Cash transferred from POS session'),
                ]);

                // Credit: Cash Drawer - decreases
                JournalEntryLine::create([
                    'journal_entry_id' => $entry->id,
                    'account_id' => $cashDrawer->id,
                    'debit' => 0,
                    'credit' => $closingCash,
                    'description' => __('Session close - drawer emptied'),
                ]);
            }

            // Record discrepancy if any
            if (abs($session->difference) > 0.01) {
                $overageCode = Setting::get('account_cash_overage', self::CASH_OVERAGE);
                $shortageCode = Setting::get('account_cash_shortage', self::CASH_SHORTAGE);
                
                $differenceAccount = $session->difference > 0 
                    ? $this->getAccountByCode($overageCode) 
                    : $this->getAccountByCode($shortageCode);
                
                if ($differenceAccount) {
                    if ($session->difference > 0) {
                        // Overage - extra money
                        JournalEntryLine::create([
                            'journal_entry_id' => $entry->id,
                            'account_id' => $cashDrawer->id,
                            'debit' => abs($session->difference),
                            'credit' => 0,
                            'description' => __('Cash overage'),
                        ]);
                        JournalEntryLine::create([
                            'journal_entry_id' => $entry->id,
                            'account_id' => $differenceAccount->id,
                            'debit' => 0,
                            'credit' => abs($session->difference),
                            'description' => __('Cash overage income'),
                        ]);
                    } else {
                        // Shortage - missing money
                        JournalEntryLine::create([
                            'journal_entry_id' => $entry->id,
                            'account_id' => $differenceAccount->id,
                            'debit' => abs($session->difference),
                            'credit' => 0,
                            'description' => __('Cash shortage expense'),
                        ]);
                        JournalEntryLine::create([
                            'journal_entry_id' => $entry->id,
                            'account_id' => $cashDrawer->id,
                            'debit' => 0,
                            'credit' => abs($session->difference),
                            'description' => __('Cash shortage'),
                        ]);
                    }
                }
            }

            return $entry;
        });
    }

    /**
     * Create journal entry for cash in/out transaction
     */
    public function createCashTransactionJournalEntry(CashTransaction $transaction): ?JournalEntry
    {
        $cashDrawer = $this->getAccountByCode(Setting::get('account_cash_drawer', self::CASH_DRAWER));
        $intermediateAccount = $this->getAccountByCode(Setting::get('account_intermediate_sales', self::INTERMEDIATE_SALES));

        if (!$cashDrawer || !$intermediateAccount) {
            return null;
        }

        return DB::transaction(function () use ($transaction, $cashDrawer, $intermediateAccount) {
            $entry = JournalEntry::create([
                'date' => $transaction->created_at->toDateString(),
                'reference' => 'CASH-' . $transaction->id,
                'reference_type' => CashTransaction::class,
                'reference_id' => $transaction->id,
                'description' => ($transaction->type === 'in' ? __('Cash In') : __('Cash Out')) . ': ' . $transaction->reason,
                'branch_id' => $transaction->session->branch_id ?? null,
                'created_by' => $transaction->user_id ?? auth()->id(),
                'is_auto' => true,
                'status' => 'pending',
            ]);

            if ($transaction->type === 'in') {
                // Cash In: Money added to drawer
                // Debit: Cash Drawer (asset increases)
                JournalEntryLine::create([
                    'journal_entry_id' => $entry->id,
                    'account_id' => $cashDrawer->id,
                    'debit' => $transaction->amount,
                    'credit' => 0,
                    'description' => __('Cash added to drawer'),
                ]);
                // Credit: Intermediate (source)
                JournalEntryLine::create([
                    'journal_entry_id' => $entry->id,
                    'account_id' => $intermediateAccount->id,
                    'debit' => 0,
                    'credit' => $transaction->amount,
                    'description' => __('Cash received (Intermediate)'),
                ]);
            } else {
                // Cash Out: Money removed from drawer
                // Debit: Intermediate (destination pending review)
                JournalEntryLine::create([
                    'journal_entry_id' => $entry->id,
                    'account_id' => $intermediateAccount->id,
                    'debit' => $transaction->amount,
                    'credit' => 0,
                    'description' => __('Cash removed from drawer (Intermediate)'),
                ]);
                // Credit: Cash Drawer (asset decreases)
                JournalEntryLine::create([
                    'journal_entry_id' => $entry->id,
                    'account_id' => $cashDrawer->id,
                    'debit' => 0,
                    'credit' => $transaction->amount,
                    'description' => __('Cash removed from drawer'),
                ]);
            }

            return $entry;
        });
    }
    /**
     * Create journal entry for purchase order receiving
     * 
     * Debit: Inventory (asset increases)
     * Credit: Accounts Payable (liability increases)
     */
    public function createPurchaseJournalEntry(PurchaseOrder $order, float $amount): ?JournalEntry
    {
        $inventoryAccount = $this->getAccountByCode(Setting::get('account_inventory', self::INVENTORY));
        $apAccount = $this->getAccountByCode(Setting::get('account_payable', self::ACCOUNTS_PAYABLE));

        if (!$inventoryAccount || !$apAccount) {
            return null;
        }

        return DB::transaction(function () use ($order, $amount, $inventoryAccount, $apAccount) {
            $entry = JournalEntry::create([
                'date' => now()->toDateString(),
                'reference' => 'PO-' . $order->po_number,
                'reference_type' => PurchaseOrder::class,
                'reference_id' => $order->id,
                'description' => __('Purchase Order Receive') . ' #' . $order->po_number,
                'partner_id' => $order->partner_id,
                'branch_id' => $order->branch_id,
                'created_by' => auth()->id(),
                'is_auto' => true,
                'status' => 'pending',
            ]);

            // Debit: Inventory (asset increases)
            JournalEntryLine::create([
                'journal_entry_id' => $entry->id,
                'account_id' => $inventoryAccount->id,
                'debit' => $amount,
                'credit' => 0,
                'description' => __('Inventory received'),
            ]);

            // Credit: Accounts Payable (liability increases)
            JournalEntryLine::create([
                'journal_entry_id' => $entry->id,
                'account_id' => $apAccount->id,
                'debit' => 0,
                'credit' => $amount,
                'description' => __('Purchase payable'),
            ]);

            return $entry;
        });
    }
    public function createPurchasePaymentJournalEntry(\App\Models\PurchasePayment $payment): ?JournalEntry
    {
        $apAccount = $this->getAccountByCode(Setting::get('account_payable', self::ACCOUNTS_PAYABLE));
        
        // Determine payment account (Cash or Bank)
        $paymentAccountCode = $payment->payment_method === 'bank' || $payment->payment_method === 'check' 
            ? Setting::get('account_bank', self::BANK_ACCOUNT)
            : Setting::get('account_cash_on_hand', self::CASH_ON_HAND); // Usually paid from Safe/Cash on Hand, not Drawer
            
        $paymentAccount = $this->getAccountByCode($paymentAccountCode);

        if (!$apAccount || !$paymentAccount) {
            return null;
        }

        return DB::transaction(function () use ($payment, $apAccount, $paymentAccount) {
            $entry = JournalEntry::create([
                'date' => $payment->payment_date->toDateString(),
                'reference' => 'PAY-' . $payment->id,
                'reference_type' => \App\Models\PurchasePayment::class,
                'reference_id' => $payment->id,
                'description' => __('Payment for PO') . ' #' . $payment->purchaseOrder->po_number,
                'branch_id' => $payment->purchaseOrder->branch_id,
                'created_by' => auth()->id(),
                'is_auto' => true,
                'status' => 'pending',
            ]);

            // Debit: Accounts Payable (liability decreases)
            JournalEntryLine::create([
                'journal_entry_id' => $entry->id,
                'account_id' => $apAccount->id,
                'debit' => $payment->amount,
                'credit' => 0,
                'description' => __('Payment to supplier'),
            ]);

            // Credit: Cash/Bank (asset decreases)
            JournalEntryLine::create([
                'journal_entry_id' => $entry->id,
                'account_id' => $paymentAccount->id,
                'debit' => 0,
                'credit' => $payment->amount,
                'description' => __('Payment outflow'),
            ]);

            return $entry;
        });
    }

    /**
     * Create journal entry for partner payment (customer settlement)
     * Debit: Cash/Bank (asset increases)
     * Credit: Accounts Receivable (asset decreases)
     */
    public function createPartnerPaymentJournalEntry(\App\Models\PartnerPayment $payment): ?JournalEntry
    {
        $arAccount = $this->getAccountByCode(self::ACCOUNTS_RECEIVABLE);

        if (!$arAccount) {
            return null;
        }

        // Determine payment account from payment method's configured account
        $paymentAccount = null;
        if ($payment->paymentMethod && $payment->paymentMethod->account_id) {
            $paymentAccount = Account::find($payment->paymentMethod->account_id);
        }
        
        // Fallback to cash on hand if no account configured
        if (!$paymentAccount) {
            $paymentAccount = $this->getAccountByCode(self::CASH_ON_HAND);
        }

        if (!$paymentAccount) {
            return null;
        }

        return DB::transaction(function () use ($payment, $paymentAccount, $arAccount) {
            $entry = JournalEntry::create([
                'date' => $payment->payment_date->toDateString(),
                'reference' => $payment->payment_number,
                'reference_type' => \App\Models\PartnerPayment::class,
                'reference_id' => $payment->id,
                'description' => __('Payment from') . ' ' . $payment->partner->name,
                'partner_id' => $payment->partner_id,
                'branch_id' => $payment->branch_id,
                'created_by' => $payment->user_id,
                'is_auto' => true,
                'status' => 'pending',
            ]);

            // Debit: Cash/Bank (asset increases)
            JournalEntryLine::create([
                'journal_entry_id' => $entry->id,
                'account_id' => $paymentAccount->id,
                'debit' => $payment->amount,
                'credit' => 0,
                'description' => __('Payment received'),
            ]);

            // Credit: Accounts Receivable (asset decreases)
            JournalEntryLine::create([
                'journal_entry_id' => $entry->id,
                'account_id' => $arAccount->id,
                'debit' => 0,
                'credit' => $payment->amount,
                'description' => __('Customer balance settlement'),
            ]);

            return $entry;
        });
    }
    /**
     * Create journal entry for supplier payment
     * Debit: Accounts Payable (liability decreases)
     * Credit: Cash/Bank (asset decreases)
     */
    public function createSupplierPaymentJournalEntry(\App\Models\PartnerPayment $payment): ?JournalEntry
    {
        $apAccount = $this->getAccountByCode(self::ACCOUNTS_PAYABLE);

        if (!$apAccount) {
            return null;
        }

        // Determine payment account from payment method's configured account
        $paymentAccount = null;
        if ($payment->paymentMethod && $payment->paymentMethod->account_id) {
            $paymentAccount = Account::find($payment->paymentMethod->account_id);
        }
        
        // Fallback to cash on hand if no account configured
        if (!$paymentAccount) {
            $paymentAccount = $this->getAccountByCode(self::CASH_ON_HAND);
        }

        if (!$paymentAccount) {
            return null;
        }

        return DB::transaction(function () use ($payment, $paymentAccount, $apAccount) {
            $entry = JournalEntry::create([
                'date' => $payment->payment_date->toDateString(),
                'reference' => $payment->payment_number,
                'reference_type' => \App\Models\PartnerPayment::class,
                'reference_id' => $payment->id,
                'description' => __('Payment to') . ' ' . $payment->partner->name,
                'partner_id' => $payment->partner_id,
                'branch_id' => $payment->branch_id,
                'created_by' => $payment->user_id,
                'is_auto' => true,
                'status' => 'pending',
            ]);

            // Debit: Accounts Payable (liability decreases)
            JournalEntryLine::create([
                'journal_entry_id' => $entry->id,
                'account_id' => $apAccount->id,
                'debit' => $payment->amount,
                'credit' => 0,
                'description' => __('Supplier payment'),
            ]);

            // Credit: Cash/Bank (asset decreases)
            JournalEntryLine::create([
                'journal_entry_id' => $entry->id,
                'account_id' => $paymentAccount->id,
                'debit' => 0,
                'credit' => $payment->amount,
                'description' => __('Payment outflow'),
            ]);

            return $entry;
        });
    }

    /**
     * Create journal entry for purchase return
     * 
     * Debit: Accounts Payable (liability decreases)
     * Credit: Inventory (asset decreases)
     */
    public function createPurchaseReturnJournalEntry(\App\Models\PurchaseReturn $return): ?JournalEntry
    {
        $inventoryAccount = $this->getAccountByCode(Setting::get('account_inventory', self::INVENTORY));
        $apAccount = $this->getAccountByCode(Setting::get('account_payable', self::ACCOUNTS_PAYABLE));

        if (!$inventoryAccount || !$apAccount) {
            return null;
        }

        return DB::transaction(function () use ($return, $inventoryAccount, $apAccount) {
            $entry = JournalEntry::create([
                'date' => $return->return_date->toDateString(),
                'reference' => 'PR-' . $return->return_number,
                'reference_type' => \App\Models\PurchaseReturn::class,
                'reference_id' => $return->id,
                'description' => __('Purchase Return') . ' #' . $return->return_number . ' (' . __('PO') . ' #' . $return->purchaseOrder->po_number . ')',
                'partner_id' => $return->purchaseOrder->partner_id,
                'branch_id' => $return->purchaseOrder->branch_id,
                'created_by' => $return->user_id,
                'is_auto' => true,
                'status' => 'pending',
            ]);

            // Debit: Accounts Payable (liability decreases)
            JournalEntryLine::create([
                'journal_entry_id' => $entry->id,
                'account_id' => $apAccount->id,
                'debit' => $return->total,
                'credit' => 0,
                'description' => __('Return to supplier'),
            ]);

            // Credit: Inventory (asset decreases)
            JournalEntryLine::create([
                'journal_entry_id' => $entry->id,
                'account_id' => $inventoryAccount->id,
                'debit' => 0,
                'credit' => $return->total,
                'description' => __('Inventory return'),
            ]);

            return $entry;
        });
    }

    /**
     * Create journal entry for sale return
     * 
     * Journal 1: Revenue Reversal
     * Debit: Sales Return (Contra Revenue)
     * Credit: Cash/Bank/AR (Refund) - Pro-rated based on original sale payments
     * 
     * Journal 2: COGS Reversal
     * Debit: Inventory (Asset Increase)
     * Credit: COGS (Expense Decrease)
     */
    public function createSaleReturnJournalEntry(\App\Models\SaleReturn $return): void
    {
        $return->load(['payments.paymentMethod', 'items', 'sale.items', 'sale']);
        $sale = $return->sale;

        // 4020: Sales Returns (Contra Revenue)
        $salesReturnsAccount = $this->getAccountByCode('4020') ?? Account::where('code', '4020')->first();
        // 1400: Inventory
        $inventoryAccount = $this->getAccountByCode(Setting::get('account_inventory', self::INVENTORY));
        // 5100: COGS
        $cogsAccount = $this->getAccountByCode(Setting::get('account_cogs', self::COST_OF_GOODS));
        // Cash/Bank/AR
        $cashAccount = $this->getAccountByCode(Setting::get('account_cash_drawer', self::CASH_DRAWER));
        $bankAccount = $this->getAccountByCode(Setting::get('account_bank', self::BANK_ACCOUNT));
        $arAccount = $this->getAccountByCode(Setting::get('account_receivable', self::ACCOUNTS_RECEIVABLE));

        if (!$salesReturnsAccount || !$inventoryAccount || !$cogsAccount || !$cashAccount || !$arAccount) {
             \Log::error("Missing accounts for Sale Return #{$return->return_number}");
             return;
        }

        DB::transaction(function () use ($return, $sale, $salesReturnsAccount, $inventoryAccount, $cogsAccount, $cashAccount, $bankAccount, $arAccount) {
            
            // --- JOURNAL 1: Revenue Reversal (Refund) ---
            
            $revenueEntry = JournalEntry::create([
                'date' => $return->created_at->toDateString(),
                'reference' => 'RET-' . $return->return_number,
                'reference_type' => \App\Models\SaleReturn::class,
                'reference_id' => $return->id,
                'description' => __('Sales Return') . ' #' . $return->return_number . ' ' . __('for Sale') . ' #' . $sale->sale_number,
                'branch_id' => $return->branch_id,
                'created_by' => $return->user_id,
                'is_auto' => true,
                'status' => 'draft', // Changed from posted to draft
            ]);

            // Debit: Sales Returns (Reduces Revenue)
            JournalEntryLine::create([
                'journal_entry_id' => $revenueEntry->id,
                'account_id' => $salesReturnsAccount->id,
                'debit' => $return->total,
                'credit' => 0,
                'description' => __('Sales return revenue reversal'),
            ]);

            // Credit: Refund Payment(s)
            // Use the actual refund payment methods chosen by the user
            
            if ($return->payments->isNotEmpty()) {
                // Use explicitly chosen refund payment methods
                foreach ($return->payments as $payment) {
                    if ($payment->amount <= 0.01) continue;

                    $methodCode = strtolower($payment->paymentMethod->code ?? '');
                    $methodName = strtolower($payment->paymentMethod->name ?? '');
                    
                    // Determine refund account based on payment method
                    $refundAccount = $cashAccount;
                    if ($methodCode === 'card' || $methodCode === 'bank' || str_contains($methodName, 'card') || str_contains($methodName, 'bank')) {
                        $refundAccount = $bankAccount;
                    }

                    JournalEntryLine::create([
                        'journal_entry_id' => $revenueEntry->id,
                        'account_id' => $refundAccount->id,
                        'debit' => 0,
                        'credit' => round($payment->amount, 2),
                        'description' => __('Refund') . ' - ' . ($payment->paymentMethod->name ?? 'Cash'),
                    ]);
                }
            } else {
                 // Fallback: Credit cash for the full amount if no payments specified
                 JournalEntryLine::create([
                    'journal_entry_id' => $revenueEntry->id,
                    'account_id' => $cashAccount->id,
                    'debit' => 0,
                    'credit' => $return->total,
                    'description' => __('Refund - Cash'),
                ]);
            }
            
            // Fix rounding total credits vs debits
            $totalCredit = JournalEntryLine::where('journal_entry_id', $revenueEntry->id)->sum('credit');
            $diff = $return->total - $totalCredit;
            
            if (abs($diff) > 0.001) {
                $line = JournalEntryLine::where('journal_entry_id', $revenueEntry->id)->where('credit', '>', 0)->first();
                if ($line) {
                    $line->update(['credit' => $line->credit + $diff]);
                }
            }


            // --- JOURNAL 2: COGS & Inventory Reversal ---
            // We got goods back, so Asset (Inventory) Increases (Debit), Expense (COGS) Decreases (Credit)
            
            $totalCogs = 0;
            $itemsCogs = []; // account_id => amount

            foreach ($return->items as $returnItem) {
                $costPrice = 0;
                // Try to find original sale item to get accurate cost at time of sale
                $saleItem = \App\Models\SaleItem::find($returnItem->sale_item_id);
                
                if ($saleItem) {
                     $costPrice = $saleItem->cost_price ?? 0;
                } else {
                     $product = $returnItem->product; 
                     $costPrice = $product->cost_price ?? 0;
                }
                
                $lineCogs = $returnItem->quantity * $costPrice;
                $totalCogs += $lineCogs;
                
                $product = $saleItem ? $saleItem->product : \App\Models\Product::find($returnItem->product_id);
                
                if ($product && $product->track_inventory) {
                     $cogsId = $product->category?->cogs_account_id ?? $cogsAccount->id;
                     if (!isset($itemsCogs[$cogsId])) $itemsCogs[$cogsId] = 0;
                     $itemsCogs[$cogsId] += $lineCogs;
                }
            }

            if ($totalCogs > 0.01) {
                $cogsEntry = JournalEntry::create([
                    'date' => $return->created_at->toDateString(),
                    'reference' => 'COGS-RET-' . $return->return_number,
                    'reference_type' => \App\Models\SaleReturn::class,
                    'reference_id' => $return->id,
                    'description' => __('Inventory Restoration for Return') . ' #' . $return->return_number,
                    'branch_id' => $return->branch_id,
                    'created_by' => $return->user_id,
                    'is_auto' => true,
                    'status' => 'draft', // Changed from posted to draft
                ]);

                // Debit: Inventory (Asset Increases)
                JournalEntryLine::create([
                    'journal_entry_id' => $cogsEntry->id,
                    'account_id' => $inventoryAccount->id,
                    'debit' => round($totalCogs, 2),
                    'credit' => 0,
                    'description' => __('Inventory restored'),
                ]);

                // Credit: COGS (Expense Decreases)
                foreach ($itemsCogs as $accountId => $amount) {
                    if ($amount > 0) {
                         JournalEntryLine::create([
                            'journal_entry_id' => $cogsEntry->id,
                            'account_id' => $accountId,
                            'debit' => 0,
                            'credit' => round($amount, 2),
                            'description' => __('COGS reversal'),
                        ]);
                    }
                }
            }
        });
    }

    /**
     * Create journal entry for daily aggregate sale
     */
    public function createDailySaleJournalEntry(\App\Models\DailySale $dailySale): ?JournalEntry
    {
        $revenueAccount = $this->getAccountByCode(Setting::get('account_sales_revenue', self::SALES_REVENUE));
        $cashAccount = $this->getAccountByCode(Setting::get('account_cash_drawer', self::CASH_DRAWER));
        $bankAccount = $this->getAccountByCode(Setting::get('account_bank', self::BANK_ACCOUNT));

        if (!$revenueAccount || !$cashAccount || !$bankAccount) {
            \Log::error("Missing accounts for Daily Sale #{$dailySale->reference}.");
            return null;
        }

        return DB::transaction(function () use ($dailySale, $revenueAccount, $cashAccount, $bankAccount) {
            $entry = JournalEntry::create([
                'date' => $dailySale->sale_date->toDateString(),
                'reference' => $dailySale->reference,
                'reference_type' => \App\Models\DailySale::class,
                'reference_id' => $dailySale->id,
                'description' => __('Daily Sales') . ' - ' . $dailySale->sale_date->format('Y-m-d'),
                'branch_id' => $dailySale->branch_id,
                'created_by' => $dailySale->user_id,
                'is_auto' => true,
                'status' => 'pending',
            ]);

            // Load payments
            $dailySale->load('payments.paymentMethod');

            foreach ($dailySale->payments as $payment) {
                // Determine Debit Account (Cash or Bank)
                $debitAccount = $cashAccount;
                
                if ($payment->paymentMethod) {
                    $methodCode = strtolower($payment->paymentMethod->code ?? '');
                    $methodName = strtolower($payment->paymentMethod->name ?? '');
                    
                    if ($methodCode === 'card' || $methodCode === 'bank' || 
                        str_contains($methodName, 'card') || str_contains($methodName, 'bank') || 
                        str_contains($methodName, 'visa') || str_contains($methodName, 'mastercard')) {
                        $debitAccount = $bankAccount;
                    }
                }

                // Debit: Asset (Cash/Bank)
                JournalEntryLine::create([
                    'journal_entry_id' => $entry->id,
                    'account_id' => $debitAccount->id,
                    'debit' => $payment->amount,
                    'credit' => 0,
                    'description' => __('Daily sales payment') . ' (' . ($payment->paymentMethod->localized_name ?? $payment->paymentMethod->name) . ')',
                ]);

                // Credit: Revenue
                JournalEntryLine::create([
                    'journal_entry_id' => $entry->id,
                    'account_id' => $revenueAccount->id,
                    'debit' => 0,
                    'credit' => $payment->amount,
                    'description' => __('Daily sales revenue'),
                ]);
            }

            return $entry;
        });
    }

}
