<?php
// ============================================================
//  MJPOS – PosController  (POS / Billing)
// ============================================================

class PosController extends Controller
{
    public function index(): void
    {
        Auth::requirePermission('sales.create');

        $customers = Database::fetchAll(
            "SELECT id, customer_name, phone, current_balance, credit_limit
             FROM customers WHERE status='active' ORDER BY customer_name"
        );
        $heldCount = Database::fetchColumn(
            'SELECT COUNT(*) FROM held_bills WHERE created_by = ?', [Auth::id()]
        );

        $pageTitle  = 'POS – New Sale';
        $breadcrumb = [['label'=>'POS']];
        ob_start();
        require APP_PATH . '/views/pos/index.php';
        $content = ob_get_clean();
        require APP_PATH . '/views/layouts/main.php';
    }

    /** POST /pos/hold – hold current bill */
    public function hold(): void
    {
        Auth::requirePermission('sales.create');
        Auth::requireCsrf();

        $cartJson   = $this->post('cart_data','[]');
        $customerId = $this->post('customer_id') ?: null;
        $subtotal   = (float) $this->post('subtotal', 0);
        $note       = trim($this->post('note',''));

        $cartData = json_decode($cartJson, true);
        if (empty($cartData)) {
            $this->json(['success'=>false,'message'=>'Cart is empty.']);
            return;
        }

        $holdRef = 'HOLD-' . date('Ymd') . '-' . randomCode(4);
        Database::insert(
            'INSERT INTO held_bills (hold_ref, customer_id, cart_data, subtotal, note, created_by)
             VALUES (?,?,?,?,?,?)',
            [$holdRef, $customerId, $cartJson, $subtotal, $note, Auth::id()]
        );

        $this->json(['success'=>true,'hold_ref'=>$holdRef,
                     'message'=>"Bill held as {$holdRef}"]);
    }

    /** GET /pos/held – list of held bills */
    public function heldList(): void
    {
        Auth::requirePermission('sales.create');
        $bills = Database::fetchAll(
            'SELECT hb.*, c.customer_name FROM held_bills hb
             LEFT JOIN customers c ON c.id = hb.customer_id
             WHERE hb.created_by = ?
             ORDER BY hb.created_at DESC',
            [Auth::id()]
        );
        $this->json($bills);
    }

    /** GET /pos/recall/:id – recall a held bill and remove it from hold */
    public function recall(string $id): void
    {
        Auth::requirePermission('sales.create');
        $bill = Database::fetchOne(
            'SELECT * FROM held_bills WHERE id=? AND created_by=?',
            [(int)$id, Auth::id()]
        );
        if (!$bill) {
            $this->json(['success'=>false,'message'=>'Held bill not found.']);
            return;
        }
        // Remove from held so it doesn't linger as a duplicate
        Database::execute('DELETE FROM held_bills WHERE id=?', [(int)$id]);
        $this->json(['success'=>true,'bill'=>$bill]);
    }

    /** POST /pos/held/delete/:id – discard a held bill */
    public function deleteHeld(string $id): void
    {
        Auth::requirePermission('sales.create');
        Auth::requireCsrf();
        $deleted = Database::execute(
            'DELETE FROM held_bills WHERE id=? AND created_by=?',
            [(int)$id, Auth::id()]
        );
        $this->json(['success' => true]);
    }

    /** POST /pos/sale – process and save a sale */
    public function sale(): void
    {
        Auth::requirePermission('sales.create');
        Auth::requireCsrf();

        $sym         = setting('currency_symbol','Rs.');
        $cartJson    = $this->post('cart_data','[]');
        $customerId  = $this->post('customer_id') ?: null;
        $walkinPhone = trim($this->post('walkin_phone',''));
        $discount    = (float) $this->post('discount', 0);
        $note        = trim($this->post('note',''));
        $holdId      = (int) $this->post('hold_id', 0);
        $payments    = json_decode($this->post('payments','[]'), true);

        $cart = json_decode($cartJson, true);
        if (empty($cart)) {
            $this->json(['success'=>false,'message'=>'Cart is empty.']); return;
        }
        if (empty($payments)) {
            $this->json(['success'=>false,'message'=>'No payment method selected.']); return;
        }

        // Calculate totals
        $subtotal = 0;
        $tax      = 0;
        foreach ($cart as &$item) {
            $item['line_total'] = ($item['selling_price'] * $item['qty'])
                                - ($item['line_discount'] ?? 0);
            $item['line_tax']   = round($item['line_total'] * ($item['tax_percent'] ?? 0) / 100, 2);
            $subtotal += $item['line_total'];
            $tax      += $item['line_tax'];
        }
        unset($item);

        $total = $subtotal - $discount + $tax;

        // ── Payment breakdown ─────────────────────────────────
        // Cash/Card/Bank/Mobile = paid right now
        // Cheque = counted as paid in sale (tracked in cheque_tracking; reversed if bounced)
        // Credit = NOT paid — creates customer balance → goes to Credit Book ONLY
        $totalImmediate = 0; // cash + card + bank + mobile
        $totalCheque    = 0; // cheque payments
        $totalCredit    = 0; // credit — only this hits customer balance

        foreach ($payments as $pmt) {
            $amt = (float)$pmt['amount'];
            match($pmt['method']) {
                'cheque' => $totalCheque    += $amt,
                'credit' => $totalCredit    += $amt,
                default  => $totalImmediate += $amt,
            };
        }

        // What the sale records as "paid" (credit excluded — customer hasn't paid yet)
        $totalPaidNow = $totalImmediate + $totalCheque;

        // Balance = only unpaid credit amount
        $creditBalance = round($totalCredit, 2);
        $balance       = $creditBalance;
        $pmtStatus     = $balance <= 0 ? 'paid' : ($totalPaidNow > 0 ? 'partial' : 'due');

        // Credit sale MUST have a customer selected
        if ($totalCredit > 0 && !$customerId) {
            $this->json(['success'=>false,
                'message'=>'⚠ Please select a customer before using Credit payment.']);
            return;
        }

        // Credit limit check
        if ($creditBalance > 0 && $customerId) {
            $customer = Database::fetchOne(
                'SELECT customer_name, credit_limit, current_balance FROM customers WHERE id = ?',
                [(int)$customerId]
            );
            $newBal = (float)$customer['current_balance'] + $creditBalance;
            if ((float)$customer['credit_limit'] > 0 && $newBal > (float)$customer['credit_limit']) {
                $this->json(['success'=>false,
                    'message'=>"Credit limit exceeded for {$customer['customer_name']}.\n"
                             . "Limit: " . currency((float)$customer['credit_limit'])
                             . " | Used: " . currency((float)$customer['current_balance'])
                             . " | This sale: " . currency($creditBalance)]);
                return;
            }
        }

        // Check stock
        $allowNegative = (int) setting('allow_negative_stock', 0);
        if (!$allowNegative) {
            foreach ($cart as $item) {
                $stock = (float) Database::fetchColumn(
                    'SELECT current_stock FROM products WHERE id=?', [$item['id']]
                );
                if ($stock < $item['qty']) {
                    $this->json(['success'=>false,
                        'message'=>"Insufficient stock for: {$item['product_name']}. Available: {$stock}"]);
                    return;
                }
            }
        }

        // ── Save everything in a transaction ─────────────────
        Database::beginTransaction();
        try {
            // Generate invoice number with retry for concurrent duplicates
            $invoiceNo = null;
            $prefix    = setting('invoice_prefix','INV');
            for ($attempt = 0; $attempt < 5; $attempt++) {
                $candidate = $this->generateInvoiceNo($prefix, 'sales');
                $exists = Database::fetchColumn(
                    'SELECT COUNT(*) FROM sales WHERE invoice_no = ?', [$candidate]
                );
                if (!$exists) { $invoiceNo = $candidate; break; }
            }
            if (!$invoiceNo) {
                // Absolute fallback: prefix + timestamp
                $invoiceNo = $prefix . '-' . date('YmdHis') . '-' . Auth::id();
            }

            // Get cashier branch_id
            $branchId = 1;
            try {
                $cb = Database::fetchColumn(
                    "SELECT COALESCE(branch_id,1) FROM users WHERE id=?", [Auth::id()]
                );
                if ($cb) $branchId = (int)$cb;
            } catch (\Exception $e) {}

            $saleId = Database::insert(
                'INSERT INTO sales
                 (customer_id, customer_phone, branch_id, invoice_no, sale_date, subtotal, discount, tax, total,
                  paid, balance, payment_status, note, status, created_by)
                 VALUES (?, ?, ?, ?, NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
                [$customerId,
                 ($customerId ? null : ($walkinPhone ?: null)),
                 $branchId, $invoiceNo, $subtotal, $discount, $tax,
                 $total, $totalPaidNow, $creditBalance, $pmtStatus,
                 $note, 'completed', Auth::id()]
            );

            foreach ($cart as $item) {
                Database::execute(
                    'INSERT INTO sale_items
                     (sale_id, product_id, quantity, cost_price, selling_price, discount, tax, total)
                     VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
                    [(int)$saleId, (int)$item['id'], $item['qty'],
                     $item['cost_price'], $item['selling_price'],
                     $item['line_discount'] ?? 0, $item['line_tax'],
                     $item['line_total']]
                );

                // Reduce stock
                $newStock = (float) Database::fetchColumn(
                    'SELECT current_stock FROM products WHERE id = ?',
                    [(int)$item['id']]
                ) - (float)$item['qty'];

                Database::execute(
                    'UPDATE products SET current_stock = current_stock - ? WHERE id = ?',
                    [$item['qty'], (int)$item['id']]
                );
                Database::execute(
                    'INSERT INTO stock_movements
                     (product_id, movement_type, quantity_in, quantity_out, stock_after, reference_no, note, created_by)
                     VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
                    [(int)$item['id'], 'sale', 0, $item['qty'], max(0, $newStock),
                     $invoiceNo, 'Sale: ' . $invoiceNo, Auth::id()]
                );
            }

            // Payments
            foreach ($payments as $pmt) {
                $pmtId = (int) Database::insert(
                    'INSERT INTO sale_payments (sale_id, payment_method, amount, reference, note)
                     VALUES (?, ?, ?, ?, ?)',
                    [(int)$saleId, $pmt['method'], $pmt['amount'],
                     $pmt['reference'] ?? null, null]
                );

                // If cheque — save to cheque_tracking
                if ($pmt['method'] === 'cheque' && !empty($pmt['reference'])) {
                    $ref    = $pmt['reference'];
                    $parts  = array_map('trim', explode('|', $ref));
                    $chequeData = [];
                    foreach ($parts as $part) {
                        if (str_starts_with($part,'ChqNo:'))    $chequeData['no']     = substr($part,6);
                        if (str_starts_with($part,'ChqDate:'))  $chequeData['date']   = substr($part,8);
                        if (str_starts_with($part,'Bank:'))     $chequeData['bank']   = substr($part,5);
                        if (str_starts_with($part,'Branch:'))   $chequeData['branch'] = substr($part,7);
                        if (str_starts_with($part,'Drawer:'))   $chequeData['drawer'] = substr($part,7);
                    }
                    // Get customer name
                    $custName  = 'Walk-in';
                    $custPhone = '';
                    if ($customerId) {
                        $cust = Database::fetchOne('SELECT customer_name, phone FROM customers WHERE id=?', [(int)$customerId]);
                        if ($cust) { $custName = $cust['customer_name']; $custPhone = $cust['phone']; }
                    }
                    Database::insert(
                        'INSERT INTO cheque_tracking
                         (transaction_type, sale_payment_id, sale_id, party_name, party_phone,
                          cheque_no, bank_name, branch, cheque_date, amount, status, created_by)
                         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
                        ['sale', $pmtId, (int)$saleId, $custName, $custPhone,
                         $chequeData['no'] ?? '',
                         $chequeData['bank'] ?? null,
                         $chequeData['branch'] ?? null,
                         $chequeData['date'] ?? null,
                         $pmt['amount'], 'pending', Auth::id()]
                    );
                }

                // ── Auto-credit linked finance account ──────────────
                // Cash amount credited = actual sale amount, not change given
                $pmtMethod   = $pmt['method'];
                $creditAmt   = (float)$pmt['amount'];
                // For cash: subtract any change given back
                if ($pmtMethod === 'cash') {
                    $netCash  = min($creditAmt, (float)$total - (float)$totalCredit);
                    $creditAmt = max(0, $netCash);
                }
                $settingKey = 'pmt_account_' . $pmtMethod;
                $acctId     = (int) (setting($settingKey, '') ?: 0);

                if ($acctId > 0 && $creditAmt > 0) {
                    $acct = Database::fetchOne(
                        'SELECT id, current_balance FROM finance_accounts WHERE id=? AND status="active"',
                        [$acctId]
                    );
                    if ($acct) {
                        // ── Card fee deduction ─────────────────────────
                        $feeAmt = 0.0;
                        if ($pmtMethod === 'card') {
                            $feePct = (float)(setting('card_fee_percent','0') ?: 0);
                            if ($feePct > 0) {
                                $feeAmt = round($creditAmt * $feePct / 100, 2);
                            }
                        }
                        $netDeposit = round($creditAmt - $feeAmt, 2);

                        // Single deposit entry — net amount only, fee info in note
                        $newBal = round((float)$acct['current_balance'] + $netDeposit, 2);
                        Database::execute(
                            'UPDATE finance_accounts SET current_balance=? WHERE id=?',
                            [$newBal, $acctId]
                        );
                        if ($feeAmt > 0) {
                            $depositNote = 'POS Sale: ' . $invoiceNo
                                . ' | Card: ' . $sym . number_format($creditAmt, 2)
                                . ' - Fee ' . setting('card_fee_percent','2.5') . '% (' . $sym . number_format($feeAmt, 2) . ')'
                                . ' = Net ' . $sym . number_format($netDeposit, 2);
                        } else {
                            $depositNote = 'POS Sale: ' . $invoiceNo
                                . ' (' . ucfirst(str_replace('_',' ',$pmtMethod)) . ')';
                        }
                        Database::execute(
                            'INSERT INTO finance_transactions
                             (account_id, txn_type, amount, balance_after, reference, note, txn_date, created_by)
                             VALUES (?,?,?,?,?,?,NOW(),?)',
                            [$acctId, 'deposit', $netDeposit, $newBal,
                             $invoiceNo, $depositNote, Auth::id()]
                        );
                    }
                }
            }

            // Update customer credit book balance — ONLY credit method increases this
            if ($customerId && $creditBalance > 0) {
                Database::execute(
                    'UPDATE customers SET current_balance = current_balance + ? WHERE id = ?',
                    [$creditBalance, (int)$customerId]
                );
            }

            // Delete held bill if recalled
            if ($holdId) {
                Database::execute('DELETE FROM held_bills WHERE id=?', [$holdId]);
            }

            Database::commit();

            Auth::logAudit(Auth::id(),'sale_create','sales',
                "Sale {$invoiceNo} – Total: {$total}");

            // Calculate change due (cash overpayment)
            $changeDue = max(0, round($totalImmediate + $totalCheque - ($total - $totalCredit), 2));

            // ── Daily target check ────────────────────────────
            $targetReached = false;
            $targetData    = null;
            if (setting('daily_target_enabled','0') === '1') {
                $target = (float)(setting('daily_sales_target','0') ?: 0);
                if ($target > 0) {
                    $todayTotal = (float)Database::fetchColumn(
                        "SELECT COALESCE(SUM(total),0) FROM sales
                         WHERE DATE(sale_date)=? AND status='completed'
                         AND created_by=?",
                        [date('Y-m-d'), Auth::id()]
                    );
                    $prevTotal = $todayTotal - (float)$total;
                    // Target crossed THIS sale
                    if ($todayTotal >= $target && $prevTotal < $target) {
                        $targetReached = true;
                        $targetData = [
                            'target'      => $target,
                            'today_total' => $todayTotal,
                            'cashier'     => Auth::name(),
                        ];
                    }
                }
            }
            try {
                if (setting('sms_enabled','0') === '1') {
                    $smsPhone = null;
                    $smsCust  = 'Customer';
                    if ($customerId) {
                        $cust2 = Database::fetchOne(
                            'SELECT customer_name, phone FROM customers WHERE id=?',
                            [(int)$customerId]
                        );
                        if (!empty($cust2['phone'])) {
                            $smsPhone = $cust2['phone'];
                            $smsCust  = $cust2['customer_name'];
                        }
                    } elseif (!empty($walkinPhone)) {
                        $smsPhone = $walkinPhone;
                    }

                    if ($smsPhone && function_exists('curl_init')) {
                        // Format phone → +94XXXXXXXXX
                        $p = preg_replace('/[\s\-\(\)]/', '', $smsPhone);
                        if (preg_match('/^0[0-9]{9}$/', $p))     $p = '+94' . substr($p, 1);
                        elseif (preg_match('/^94\d{9}$/', $p))   $p = '+' . $p;
                        elseif (preg_match('/^7[0-9]{8}$/', $p)) $p = '+94' . $p;

                        // Build message
                        $tpl = setting('sms_template',
                            'Hi {customer}, Invoice {invoice_no} | Total: Rs.{total} | Paid: Rs.{paid} | Thank you! - {shop_name}');
                        $msg = str_replace(
                            ['{invoice_no}','{total}','{paid}','{change}','{balance}','{customer}','{shop_name}','{date}'],
                            [$invoiceNo, number_format($total,2),
                             number_format(round($totalImmediate+$totalCheque,2),2),
                             number_format($changeDue,2), number_format($totalCredit,2),
                             $smsCust, setting('shop_name','MJPOS'), date('d M Y')],
                            $tpl
                        );

                        // Send via cURL
                        $ch = curl_init();
                        curl_setopt_array($ch, [
                            CURLOPT_URL            => rtrim(setting('sms_api_url','https://smslenz.lk/api'),'/') . '/send-sms',
                            CURLOPT_POST           => true,
                            CURLOPT_POSTFIELDS     => http_build_query([
                                'user_id'   => setting('sms_user_id',''),
                                'api_key'   => setting('sms_api_key',''),
                                'sender_id' => setting('sms_sender_id','SMSlenzDEMO'),
                                'contact'   => $p,
                                'message'   => $msg,
                            ]),
                            CURLOPT_RETURNTRANSFER => true,
                            CURLOPT_TIMEOUT        => 6,
                            CURLOPT_CONNECTTIMEOUT => 4,
                            CURLOPT_SSL_VERIFYPEER => false,
                        ]);
                        curl_exec($ch);
                        curl_close($ch);
                    }
                }
            } catch (\Throwable $smsErr) {
                error_log('MJPOS SMS: ' . $smsErr->getMessage());
            }

            $this->json([
                'success'        => true,
                'invoice_no'     => $invoiceNo,
                'sale_id'        => $saleId,
                'receipt_url'    => APP_URL . '/pos/receipt/' . $saleId,
                'message'        => "Sale saved: {$invoiceNo}",
                'total'          => $total,
                'paid'           => round($totalImmediate + $totalCheque, 2),
                'balance'        => $totalCredit,
                'change_due'     => $changeDue,
                'target_reached' => $targetReached,
                'target_data'    => $targetData,
            ]);

        } catch (Exception $e) {
            Database::rollback();
            $this->json(['success'=>false,
                'message'=>'Transaction failed: ' . $e->getMessage()]);
        }
    }

    /** GET /pos/receipt/:id – printable receipt */
    public function receipt(string $id): void
    {
        Auth::requireLogin();
        $sale  = Database::fetchOne(
            'SELECT s.*,
                    COALESCE(c.customer_name,"Walk-in") AS customer_name,
                    COALESCE(c.phone, s.customer_phone) AS cust_phone,
                    u.name AS cashier_name
             FROM sales s
             LEFT JOIN customers c ON c.id = s.customer_id
             LEFT JOIN users     u ON u.id = s.created_by
             WHERE s.id = ?',
            [(int)$id]
        );
        if (!$sale) { http_response_code(404); die('Sale not found.'); }

        $items = Database::fetchAll(
            'SELECT si.*, p.product_name, p.product_code
             FROM sale_items si
             JOIN products p ON p.id = si.product_id
             WHERE si.sale_id = ?',
            [(int)$id]
        );
        $payments = Database::fetchAll(
            'SELECT * FROM sale_payments WHERE sale_id=?', [(int)$id]
        );

        require APP_PATH . '/views/pos/receipt.php';
    }
}