<?php
/**
 * ITRPOS Financial Module - Journal Entry Handler
 * Handles creation of journal entries for different transaction types
 */

require_once 'financial_helpers.php';

/**
 * Create journal entry for sales transaction
 */
function createSalesJournalEntry($conn, $sale_id, $total_amount, $cost_amount, $payment_method, $store_id = 1, $bank_account_id = null) {
    try {
        $journal_entry_id = createJournalEntry($conn, 'sale', $sale_id, "Sale transaction #$sale_id", $store_id);
        
        if (!$journal_entry_id) {
            throw new Exception("Failed to create journal entry for sale #$sale_id");
        }
        
        // Debit: Cash/Bank (based on payment method)
        $cash_account_id = getAccountIdByMapping($conn, 'ast_Cash');
        if ($payment_method === 'bank') {
            // Use individual bank account mapping if bank_account_id is provided
            if ($bank_account_id) {
                $cash_account_id = getBankAccountId($conn, $bank_account_id);
                if (!$cash_account_id) {
                    // Fallback to generic bank mapping if individual mapping not found
                    $cash_account_id = getAccountIdByMapping($conn, 'ast_Bank');
                }
            } else {
                $cash_account_id = getAccountIdByMapping($conn, 'ast_Bank');
            }
        }
        
        if (!$cash_account_id) {
            throw new Exception("Cash/Bank account not found for payment method: $payment_method");
        }
        
        createJournalLine($conn, $journal_entry_id, $cash_account_id, $total_amount, 0, "Cash receipt for sale #$sale_id");
        
        // Credit: Sales Revenue
        $sales_account_id = getAccountIdByMapping($conn, 'rev_Sale');
        if (!$sales_account_id) {
            throw new Exception("Sales revenue account not found");
        }
        createJournalLine($conn, $journal_entry_id, $sales_account_id, 0, $total_amount, "Sales revenue for sale #$sale_id");
        
        // Debit: Cost of Sales (if cost amount is provided)
        if ($cost_amount > 0) {
            $cost_account_id = getAccountIdByMapping($conn, 'exp_CostOfSales');
            if (!$cost_account_id) {
                throw new Exception("Cost of sales account not found");
            }
            createJournalLine($conn, $journal_entry_id, $cost_account_id, $cost_amount, 0, "Cost of sales for sale #$sale_id");
            
            // Credit: Inventory
            $inventory_account_id = getAccountIdByMapping($conn, 'ast_Stock');
            if (!$inventory_account_id) {
                throw new Exception("Inventory account not found");
            }
            createJournalLine($conn, $journal_entry_id, $inventory_account_id, 0, $cost_amount, "Inventory reduction for sale #$sale_id");
        }
        
        // Post the journal entry
        return postJournalEntry($conn, $journal_entry_id);
        
    } catch (Exception $e) {
        error_log("Sales journal entry error: " . $e->getMessage());
        return false;
    }
}

/**
 * Create journal entry for purchase transaction
 */
function createPurchaseJournalEntry($conn, $purchase_id, $total_amount, $payment_method, $store_id = 1) {
    try {
        $journal_entry_id = createJournalEntry($conn, 'purchase', $purchase_id, "Purchase transaction #$purchase_id", $store_id);
        
        if (!$journal_entry_id) {
            throw new Exception("Failed to create journal entry for purchase #$purchase_id");
        }
        
        // Debit: Inventory
        $inventory_account_id = getAccountIdByMapping($conn, 'ast_Stock');
        if (!$inventory_account_id) {
            throw new Exception("Inventory account not found");
        }
        createJournalLine($conn, $journal_entry_id, $inventory_account_id, $total_amount, 0, "Inventory purchase #$purchase_id");
        
        // Credit: Accounts Payable (if not paid immediately) or Cash/Bank (if paid)
        if ($payment_method === 'credit' || $payment_method === 'pending') {
            $payable_account_id = getAccountIdByMapping($conn, 'lib_Supplier');
            if (!$payable_account_id) {
                throw new Exception("Accounts payable account not found");
            }
            createJournalLine($conn, $journal_entry_id, $payable_account_id, 0, $total_amount, "Accounts payable for purchase #$purchase_id");
        } else {
            // Paid immediately
            $cash_account_id = getAccountIdByMapping($conn, 'ast_Cash');
            if ($payment_method === 'bank') {
                $cash_account_id = getAccountIdByMapping($conn, 'ast_Bank');
            }
            
            if (!$cash_account_id) {
                throw new Exception("Cash/Bank account not found for payment method: $payment_method");
            }
            createJournalLine($conn, $journal_entry_id, $cash_account_id, 0, $total_amount, "Cash payment for purchase #$purchase_id");
        }
        
        // Post the journal entry
        return postJournalEntry($conn, $journal_entry_id);
        
    } catch (Exception $e) {
        error_log("Purchase journal entry error: " . $e->getMessage());
        return false;
    }
}

/**
 * Create journal entry for purchase return transaction
 */
function createPurchaseReturnJournalEntry($conn, $return_id, $total_amount, $store_id = 1) {
    try {
        $journal_entry_id = createJournalEntry($conn, 'purchase_return', $return_id, "Purchase return #$return_id", $store_id);
        
        if (!$journal_entry_id) {
            throw new Exception("Failed to create journal entry for purchase return #$return_id");
        }
        
        // Debit: Accounts Payable
        $payable_account_id = getAccountIdByMapping($conn, 'lib_Supplier');
        if (!$payable_account_id) {
            throw new Exception("Accounts payable account not found");
        }
        createJournalLine($conn, $journal_entry_id, $payable_account_id, $total_amount, 0, "Accounts payable reduction for return #$return_id");
        
        // Credit: Inventory
        $inventory_account_id = getAccountIdByMapping($conn, 'ast_Stock');
        if (!$inventory_account_id) {
            throw new Exception("Inventory account not found");
        }
        createJournalLine($conn, $journal_entry_id, $inventory_account_id, 0, $total_amount, "Inventory return #$return_id");
        
        // Post the journal entry
        return postJournalEntry($conn, $journal_entry_id);
        
    } catch (Exception $e) {
        error_log("Purchase return journal entry error: " . $e->getMessage());
        return false;
    }
}

/**
 * Create journal entry for expense transaction
 */
function createExpenseJournalEntry($conn, $expense_id, $amount, $expense_category_id, $payment_method, $store_id = 1, $bank_account_id = null) {
    try {
        $journal_entry_id = createJournalEntry($conn, 'expense', $expense_id, "Expense transaction #$expense_id", $store_id);
        
        if (!$journal_entry_id) {
            throw new Exception("Failed to create journal entry for expense #$expense_id");
        }
        
        // Debit: Appropriate Expense Account
        $expense_account_id = getExpenseAccountId($conn, $expense_category_id);
        if (!$expense_account_id) {
            throw new Exception("Expense account not found for category: $expense_category_id");
        }
        createJournalLine($conn, $journal_entry_id, $expense_account_id, $amount, 0, "Expense #$expense_id");
        
        // Credit: Cash/Bank
        $cash_account_id = getAccountIdByMapping($conn, 'ast_Cash');
        if ($payment_method === 'bank') {
            // Use individual bank account mapping if bank_account_id is provided
            if ($bank_account_id) {
                $cash_account_id = getBankAccountId($conn, $bank_account_id);
                if (!$cash_account_id) {
                    // Fallback to generic bank mapping if individual mapping not found
                    $cash_account_id = getAccountIdByMapping($conn, 'ast_Bank');
                }
            } else {
                $cash_account_id = getAccountIdByMapping($conn, 'ast_Bank');
            }
        }
        
        if (!$cash_account_id) {
            throw new Exception("Cash/Bank account not found for payment method: $payment_method");
        }
        createJournalLine($conn, $journal_entry_id, $cash_account_id, 0, $amount, "Cash payment for expense #$expense_id");
        
        // Post the journal entry
        return postJournalEntry($conn, $journal_entry_id);
        
    } catch (Exception $e) {
        error_log("Expense journal entry error: " . $e->getMessage());
        return false;
    }
}

/**
 * Create journal entry for customer payment
 */
function createCustomerPaymentJournalEntry($conn, $payment_id, $amount, $payment_method, $store_id = 1, $bank_account_id = null) {
    try {
        $journal_entry_id = createJournalEntry($conn, 'receipt', $payment_id, "Customer payment #$payment_id", $store_id);
        
        if (!$journal_entry_id) {
            throw new Exception("Failed to create journal entry for customer payment #$payment_id");
        }
        
        // Debit: Cash/Bank
        $cash_account_id = getAccountIdByMapping($conn, 'ast_Cash');
        if ($payment_method === 'bank') {
            // Use individual bank account mapping if bank_account_id is provided
            if ($bank_account_id) {
                $cash_account_id = getBankAccountId($conn, $bank_account_id);
                if (!$cash_account_id) {
                    // Fallback to generic bank mapping if individual mapping not found
                    $cash_account_id = getAccountIdByMapping($conn, 'ast_Bank');
                }
            } else {
                $cash_account_id = getAccountIdByMapping($conn, 'ast_Bank');
            }
        }
        
        if (!$cash_account_id) {
            throw new Exception("Cash/Bank account not found for payment method: $payment_method");
        }
        createJournalLine($conn, $journal_entry_id, $cash_account_id, $amount, 0, "Cash receipt for customer payment #$payment_id");
        
        // Credit: Accounts Receivable
        $receivable_account_id = getAccountIdByMapping($conn, 'ast_Customer');
        if (!$receivable_account_id) {
            throw new Exception("Accounts receivable account not found");
        }
        createJournalLine($conn, $journal_entry_id, $receivable_account_id, 0, $amount, "Accounts receivable reduction for payment #$payment_id");
        
        // Post the journal entry
        return postJournalEntry($conn, $journal_entry_id);
        
    } catch (Exception $e) {
        error_log("Customer payment journal entry error: " . $e->getMessage());
        return false;
    }
}

/**
 * Create journal entry for supplier payment
 */
function createSupplierPaymentJournalEntry($conn, $payment_id, $amount, $payment_method, $store_id = 1) {
    try {
        $journal_entry_id = createJournalEntry($conn, 'payment', $payment_id, "Supplier payment #$payment_id", $store_id);
        
        if (!$journal_entry_id) {
            throw new Exception("Failed to create journal entry for supplier payment #$payment_id");
        }
        
        // Debit: Accounts Payable
        $payable_account_id = getAccountIdByMapping($conn, 'lib_Supplier');
        if (!$payable_account_id) {
            throw new Exception("Accounts payable account not found");
        }
        createJournalLine($conn, $journal_entry_id, $payable_account_id, $amount, 0, "Accounts payable reduction for payment #$payment_id");
        
        // Credit: Cash/Bank
        $cash_account_id = getAccountIdByMapping($conn, 'ast_Cash');
        if ($payment_method === 'bank') {
            $cash_account_id = getAccountIdByMapping($conn, 'ast_Bank');
        }
        
        if (!$cash_account_id) {
            throw new Exception("Cash/Bank account not found for payment method: $payment_method");
        }
        createJournalLine($conn, $journal_entry_id, $cash_account_id, 0, $amount, "Cash payment for supplier payment #$payment_id");
        
        // Post the journal entry
        return postJournalEntry($conn, $journal_entry_id);
        
    } catch (Exception $e) {
        error_log("Supplier payment journal entry error: " . $e->getMessage());
        return false;
    }
}

/**
 * Create journal entry for stock adjustment
 */
function createStockAdjustmentJournalEntry($conn, $adjustment_id, $adjustment_type, $total_value, $store_id = 1) {
    try {
        $journal_entry_id = createJournalEntry($conn, 'adjustment', $adjustment_id, "Stock adjustment #$adjustment_id", $store_id);
        
        if (!$journal_entry_id) {
            throw new Exception("Failed to create journal entry for stock adjustment #$adjustment_id");
        }
        
        // Get inventory account
        $inventory_account_id = getAccountIdByMapping($conn, 'ast_Stock');
        if (!$inventory_account_id) {
            throw new Exception("Inventory account not found");
        }
        
        // Get adjustment account (Stock Loss account for inventory losses)
        $adjustment_account_id = getAccountIdByMapping($conn, 'exp_StockLoss');
        if (!$adjustment_account_id) {
            throw new Exception("Stock Loss account not found - please ensure the Stock Loss account is created and mapped");
        }
        
        if ($adjustment_type === 'addition') {
            // Debit: Inventory
            createJournalLine($conn, $journal_entry_id, $inventory_account_id, $total_value, 0, "Stock addition #$adjustment_id");
            // Credit: Other Income (or specific account)
            $income_account_id = getAccountIdByMapping($conn, 'rev_Other');
            createJournalLine($conn, $journal_entry_id, $income_account_id, 0, $total_value, "Stock addition income #$adjustment_id");
        } else {
            // Debit: Adjustment Expense
            createJournalLine($conn, $journal_entry_id, $adjustment_account_id, $total_value, 0, "Stock adjustment expense #$adjustment_id");
            // Credit: Inventory
            createJournalLine($conn, $journal_entry_id, $inventory_account_id, 0, $total_value, "Stock reduction #$adjustment_id");
        }
        
        // Post the journal entry
        return postJournalEntry($conn, $journal_entry_id);
        
    } catch (Exception $e) {
        error_log("Stock adjustment journal entry error: " . $e->getMessage());
        return false;
    }
}

/**
 * Create manual journal entry
 */
function createManualJournalEntry($conn, $description, $lines, $store_id = 1) {
    try {
        $journal_entry_id = createJournalEntry($conn, 'manual', null, $description, $store_id);
        
        if (!$journal_entry_id) {
            throw new Exception("Failed to create manual journal entry");
        }
        
        // Add journal lines
        foreach ($lines as $line) {
            $account_id = $line['account_id'];
            $debit_amount = $line['debit_amount'] ?? 0;
            $credit_amount = $line['credit_amount'] ?? 0;
            $line_description = $line['description'] ?? '';
            
            createJournalLine($conn, $journal_entry_id, $account_id, $debit_amount, $credit_amount, $line_description);
        }
        
        // Post the journal entry
        return postJournalEntry($conn, $journal_entry_id);
        
    } catch (Exception $e) {
        error_log("Manual journal entry error: " . $e->getMessage());
        return false;
    }
}

/**
 * Void a journal entry
 */
function voidJournalEntry($conn, $journal_entry_id, $reason = '') {
    try {
        // Check if entry exists and is posted
        $stmt = $conn->prepare("SELECT * FROM journal_entries WHERE id = ? AND status = 'posted'");
        $stmt->bind_param("i", $journal_entry_id);
        $stmt->execute();
        $entry = $stmt->get_result()->fetch_assoc();
        
        if (!$entry) {
            throw new Exception("Journal entry not found or not posted");
        }
        
        // Create reversing journal entry
        $reversing_entry_id = createJournalEntry($conn, 'adjustment', $journal_entry_id, "Reversing entry for {$entry['entry_number']} - $reason", $entry['store_id']);
        
        if (!$reversing_entry_id) {
            throw new Exception("Failed to create reversing journal entry");
        }
        
        // Get original lines and reverse them
        $stmt = $conn->prepare("SELECT * FROM journal_entry_lines WHERE journal_entry_id = ?");
        $stmt->bind_param("i", $journal_entry_id);
        $stmt->execute();
        $result = $stmt->get_result();
        
        while ($line = $result->fetch_assoc()) {
            // Reverse the amounts
            $reversed_debit = $line['credit_amount'];
            $reversed_credit = $line['debit_amount'];
            
            createJournalLine($conn, $reversing_entry_id, $line['account_id'], $reversed_debit, $reversed_credit, "Reversal of {$entry['entry_number']}");
        }
        
        // Post the reversing entry
        postJournalEntry($conn, $reversing_entry_id);
        
        // Mark original entry as void
        $stmt = $conn->prepare("UPDATE journal_entries SET status = 'void' WHERE id = ?");
        $stmt->bind_param("i", $journal_entry_id);
        $stmt->execute();
        
        return true;
        
    } catch (Exception $e) {
        error_log("Void journal entry error: " . $e->getMessage());
        return false;
    }
}
?>
