<?php
class SalesController {
    public function index(): void {
        Auth::requireLogin();
        Auth::requireActiveTenant();

        $page = (int)($_GET['page'] ?? 1) ?: 1;
        $filters = [
            'search' => $_GET['search'] ?? '',
            'date_from' => $_GET['date_from'] ?? '',
            'date_to' => $_GET['date_to'] ?? '',
            'status' => $_GET['status'] ?? '',
            'location_id' => $_GET['location_id'] ?? '',
            'user_id' => $_GET['user_id'] ?? ''
        ];

        $sales = Sale::listPaginated($page, $filters);
        
        $pageTitle = 'Sales & Invoices';
        ob_start();
        include APP_ROOT . '/views/tenant/sales/index.php';
        $content = ob_get_clean();
        include APP_ROOT . '/views/tenant/layouts/main.php';
    }

    public function show(string $id): void {
        Auth::requireLogin();
        Auth::requireActiveTenant();

        $sale = Sale::withItems((int)$id);
        if (!$sale) { flash('error', 'Sale not found'); redirect('sales'); }

        $pageTitle = 'Invoice ' . e($sale['sale_no']);
        ob_start();
        include APP_ROOT . '/views/tenant/sales/show.php';
        $content = ob_get_clean();
        include APP_ROOT . '/views/tenant/layouts/main.php';
    }

    public function printReceipt(string $id, string $format = 'pos80'): void {
        Auth::requireLogin();
        Auth::requireActiveTenant();

        $sale = Sale::withItems((int)$id);
        if (!$sale) {
            echo 'Sale not found';
            return;
        }

        $tenantId = Auth::tenantId();
        $tenant = Auth::tenant();
        $currencySymbol = $tenant['currency_symbol'] ?? 'GH₵';

        // Get customer if exists
        $customer = null;
        if ($sale['customer_id']) {
            $custStmt = db()->prepare("SELECT * FROM customers WHERE id=? AND tenant_id=?");
            $custStmt->execute([$sale['customer_id'], $tenantId]);
            $customer = $custStmt->fetch();
        }

        // Get location
        $locStmt = db()->prepare("SELECT * FROM locations WHERE id=?");
        $locStmt->execute([$sale['location_id']]);
        $location = $locStmt->fetch();

        ob_start();
        include APP_ROOT . '/views/tenant/sales/receipt.php';
        $content = ob_get_clean();
        echo $content;
    }

    public function edit(string $id): void {
        Auth::requireLogin();
        Auth::requireActiveTenant();

        $sale = Sale::withItems((int)$id);
        if (!$sale) { flash('error', 'Sale not found'); redirect('sales'); }

        $pageTitle = 'Edit Sale ' . e($sale['sale_no']);
        ob_start();
        include APP_ROOT . '/views/tenant/sales/edit.php';
        $content = ob_get_clean();
        include APP_ROOT . '/views/tenant/layouts/main.php';
    }

    public function pay(string $id): void {
        Auth::requireLogin();
        Auth::requireActiveTenant();

        $sale = Sale::find((int)$id);
        if (!$sale) { flash('error', 'Sale not found'); redirect('sales'); }

        $dueAmount = $sale['total'] - $sale['amount_paid'];
        if ($dueAmount <= 0) { flash('error', 'No due payment for this sale'); redirect('sales'); }

        $pageTitle = 'Pay Sale ' . e($sale['sale_no']);
        ob_start();
        include APP_ROOT . '/views/tenant/sales/pay.php';
        $content = ob_get_clean();
        include APP_ROOT . '/views/tenant/layouts/main.php';
    }

    public function delete(string $id): void {
        Auth::requireLogin();
        Auth::requireActiveTenant();

        $sale = Sale::find((int)$id);
        if (!$sale) { flash('error', 'Sale not found'); redirect('sales'); }

        try {
            db()->beginTransaction();

            // Get sale items before deletion to restore stock
            $tid = Auth::tenantId();
            $stmt = db()->prepare("SELECT * FROM sale_items WHERE sale_id = ? AND tenant_id = ?");
            $stmt->execute([$id, $tid]);
            $items = $stmt->fetchAll();

            // Restore stock for each item
            foreach ($items as $item) {
                adjustStock($item['product_id'], $sale['location_id'], $item['quantity'], $item['batch_id'] ?? null, $tid);
                logMovement($item['product_id'], $sale['location_id'], 'return', $item['quantity'], $sale['sale_no'], 'Sale deletion');
            }

            // Delete sale items
            $stmt = db()->prepare("DELETE FROM sale_items WHERE sale_id = ?");
            $stmt->execute([$id]);

            // Delete sale
            $stmt = db()->prepare("DELETE FROM sales WHERE id = ?");
            $stmt->execute([$id]);

            db()->commit();
            flash('success', 'Sale deleted successfully');
            redirect('sales');
        } catch (Exception $e) {
            db()->rollBack();
            flash('error', 'Error deleting sale: ' . $e->getMessage());
            redirect('sales');
        }
    }

    public function update(string $id): void {
        Auth::requireLogin();
        Auth::requireActiveTenant();
        Security::checkCsrf();

        $sale = Sale::withItems((int)$id);
        if (!$sale) { flash('error', 'Sale not found'); redirect('sales'); }

        $tid = Auth::tenantId();
        $items = $_POST['items'] ?? [];
        $oldAmountPaid = $sale['amount_paid'] ?? $sale['paid_amount'] ?? 0;
        $newAmountPaid = (float)$_POST['amount_paid'];

        // Check if any invoice changes were made
        $hasChanges = false;

        // Check if items changed
        $oldItems = [];
        foreach ($sale['items'] as $item) {
            $oldItems[$item['id']] = $item;
        }

        $submittedItemIds = [];
        foreach ($items as $itemData) {
            $itemId = (int)($itemData['id'] ?? 0);
            $quantity = (float)($itemData['quantity'] ?? 0);
            $unitPrice = (float)($itemData['unit_price'] ?? 0);

            if ($itemId > 0 && isset($oldItems[$itemId])) {
                if ($quantity != $oldItems[$itemId]['quantity'] || $unitPrice != $oldItems[$itemId]['unit_price']) {
                    $hasChanges = true;
                }
                $submittedItemIds[] = $itemId;
            }
        }

        // Check if items were removed
        foreach ($oldItems as $oldItemId => $oldItem) {
            if (!in_array($oldItemId, $submittedItemIds)) {
                $hasChanges = true;
            }
        }

        // Check if totals changed
        $oldTaxAmount = (float)($sale['tax_amount'] ?? 0);
        $oldDiscountAmount = (float)($sale['discount_amount'] ?? 0);
        $newTaxAmount = (float)($_POST['tax_amount'] ?? 0);
        $newDiscountAmount = (float)($_POST['discount_amount'] ?? 0);

        if ($oldTaxAmount != $newTaxAmount || $oldDiscountAmount != $newDiscountAmount) {
            $hasChanges = true;
        }

        // Only enforce Amount Paid change if invoice was modified
        if ($hasChanges && $oldAmountPaid == $newAmountPaid) {
            flash('error', 'You must change the Amount Paid field before saving since you modified the invoice. Please update the payment amount.');
            redirect('sales/' . $id . '/edit');
            return;
        }

        // Calculate new totals to validate amount paid doesn't exceed total
        $newTaxAmount = (float)($_POST['tax_amount'] ?? 0);
        $newDiscountAmount = (float)($_POST['discount_amount'] ?? 0);
        $newSubtotal = 0;
        foreach ($items as $itemData) {
            $quantity = (float)($itemData['quantity'] ?? 0);
            $unitPrice = (float)($itemData['unit_price'] ?? 0);
            $newSubtotal += $quantity * $unitPrice;
        }
        $newTotal = $newSubtotal - $newDiscountAmount;

        // Prevent overpayment
        if ($newAmountPaid > $newTotal) {
            flash('error', 'Amount Paid cannot exceed the Total. Please adjust the payment amount to be less than or equal to ' . money($newTotal));
            redirect('sales/' . $id . '/edit');
            return;
        }

        try {
            db()->beginTransaction();

            // Build a map of old items for easy comparison
            $oldItems = [];
            foreach ($sale['items'] as $item) {
                $oldItems[$item['id']] = $item;
            }

            // Process submitted items
            $subtotal = 0;
            $submittedItemIds = [];

            foreach ($items as $itemData) {
                $itemId = (int)($itemData['id'] ?? 0);
                $productId = (int)($itemData['product_id'] ?? 0);
                $quantity = (float)($itemData['quantity'] ?? 0);
                $unitPrice = (float)($itemData['unit_price'] ?? 0);
                $batchId = !empty($itemData['batch_id']) ? (int)$itemData['batch_id'] : null;

                if ($itemId > 0 && isset($oldItems[$itemId])) {
                    // Existing item - check if quantity changed
                    $oldItem = $oldItems[$itemId];
                    $qtyDiff = $quantity - $oldItem['quantity'];

                    if ($qtyDiff != 0) {
                        // Adjust stock based on quantity difference
                        if ($qtyDiff > 0) {
                            ensureStockAvailable($productId, $sale['location_id'], -$qtyDiff, $batchId, $tid);
                        }
                        adjustStock($productId, $sale['location_id'], -$qtyDiff, $batchId, $tid);
                        logMovement($productId, $sale['location_id'], 'adjustment', -$qtyDiff, $sale['sale_no'], 'Sale edit - quantity adjustment');
                    }

                    // Update the item
                    $lineTotal = $quantity * $unitPrice;
                    $stmt = db()->prepare(
                        "UPDATE sale_items SET quantity = ?, unit_price = ?, total = ?, batch_id = ? WHERE id = ? AND tenant_id = ?"
                    );
                    $stmt->execute([$quantity, $unitPrice, $lineTotal, $batchId, $itemId, $tid]);

                    $submittedItemIds[] = $itemId;
                }

                $subtotal += $quantity * $unitPrice;
            }

            // Remove items that were deleted from the form
            foreach ($oldItems as $oldItemId => $oldItem) {
                if (!in_array($oldItemId, $submittedItemIds)) {
                    // Restore stock for removed item
                    adjustStock($oldItem['product_id'], $sale['location_id'], $oldItem['quantity'], $oldItem['batch_id'], $tid);
                    logMovement($oldItem['product_id'], $sale['location_id'], 'return', $oldItem['quantity'], $sale['sale_no'], 'Sale edit - item removed');

                    // Delete the item
                    $stmt = db()->prepare("DELETE FROM sale_items WHERE id = ? AND tenant_id = ?");
                    $stmt->execute([$oldItemId, $tid]);
                }
            }

            // Calculate totals
            $taxAmount = (float)($_POST['tax_amount'] ?? 0);
            $discountAmount = (float)($_POST['discount_amount'] ?? 0);
            $total = $subtotal - $discountAmount;

            // Handle customer balance if payment status changed
            $oldPaymentStatus = $sale['payment_status'];
            $newPaymentStatus = Security::sanitize($_POST['payment_status']);
            $oldAmountPaid = $sale['amount_paid'] ?? $sale['paid_amount'] ?? 0;
            $newAmountPaid = (float)$_POST['amount_paid'];
            $paymentMethod = Security::sanitize($_POST['payment_method'] ?? $sale['payment_method'] ?? 'cash');

            if (!empty($sale['customer_id']) && ($oldPaymentStatus !== $newPaymentStatus || $oldAmountPaid != $newAmountPaid)) {
                $oldDue = $sale['total'] - $oldAmountPaid;
                $newDue = $total - $newAmountPaid;
                $balanceDiff = $newDue - $oldDue;

                if ($balanceDiff != 0) {
                    $stmt = db()->prepare("UPDATE customers SET balance = balance + ? WHERE id = ? AND tenant_id = ?");
                    $stmt->execute([$balanceDiff, $sale['customer_id'], $tid]);
                }
            }

            // Update or create payment record if amount paid changed
            if ($newAmountPaid > $oldAmountPaid) {
                $paymentDiff = $newAmountPaid - $oldAmountPaid;
                $stmt = db()->prepare(
                    "INSERT INTO payments (tenant_id, type, reference_id, party_id, party_type, amount, method, note, user_id)
                     VALUES (?, 'sale', ?, ?, 'customer', ?, ?, ?, ?, ?)"
                );
                $stmt->execute([
                    $tid,
                    $id,
                    $sale['customer_id'],
                    $paymentDiff,
                    $paymentMethod,
                    'Additional payment from sale edit',
                    Auth::userId()
                ]);
            }

            // Update the sale
            $stmt = db()->prepare(
                "UPDATE sales SET subtotal = ?, tax_amount = ?, discount_amount = ?, total = ?, amount_paid = ?, payment_status = ?, payment_method = ?, status = ?, note = ? WHERE id = ?"
            );
            $stmt->execute([
                $subtotal,
                $taxAmount,
                $discountAmount,
                $total,
                $newAmountPaid,
                $newPaymentStatus,
                $paymentMethod,
                Security::sanitize($_POST['status']),
                Security::sanitize($_POST['notes'] ?? ''),
                $id
            ]);

            // Log audit trail
            $stmt = db()->prepare(
                "INSERT INTO audit_logs (tenant_id, user_id, user_type, action, model, model_id, old_values, new_values)
                 VALUES (?, ?, 'tenant_user', 'sale_edit', 'sale', ?, ?, ?)"
            );
            $stmt->execute([
                $tid,
                Auth::userId(),
                $id,
                json_encode(['total' => $sale['total'], 'payment_status' => $oldPaymentStatus]),
                json_encode(['total' => $total, 'payment_status' => $newPaymentStatus])
            ]);

            db()->commit();
            flash('success', 'Sale updated successfully');
            redirect('sales');
        } catch (Exception $e) {
            db()->rollBack();
            flash('error', 'Error updating sale: ' . $e->getMessage());
            redirect('sales/' . $id . '/edit');
        }
    }

    public function processPayment(string $id): void {
        Auth::requireLogin();
        Auth::requireActiveTenant();

        $sale = Sale::find((int)$id);
        if (!$sale) { flash('error', 'Sale not found'); redirect('sales'); }

        $paymentAmount = (float)$_POST['payment_amount'];
        $dueAmount = $sale['total'] - $sale['amount_paid'];

        if ($paymentAmount > $dueAmount) {
            flash('error', 'Payment amount cannot exceed due amount');
            redirect('sales/' . $id . '/pay');
        }

        try {
            db()->beginTransaction();

            $newAmountPaid = $sale['amount_paid'] + $paymentAmount;
            $newPaymentStatus = $newAmountPaid >= $sale['total'] ? 'paid' : 'partial';

            // If sale was suspended, change status to completed after payment
            $newStatus = ($sale['status'] === 'suspended') ? 'completed' : $sale['status'];

            $stmt = db()->prepare(
                "UPDATE sales SET amount_paid = ?, payment_status = ?, payment_method = ?, status = ? WHERE id = ?"
            );
            $stmt->execute([
                $newAmountPaid,
                $newPaymentStatus,
                Security::sanitize($_POST['payment_method']),
                $newStatus,
                $id
            ]);

            // Record in customer ledger if customer exists
            if ($sale['customer_id']) {
                CustomerLedger::addEntry([
                    'customer_id' => $sale['customer_id'],
                    'transaction_type' => 'payment',
                    'reference_id' => $id,
                    'reference_no' => $sale['sale_no'],
                    'debit' => 0,
                    'credit' => $paymentAmount,
                    'description' => 'Payment for sale ' . $sale['sale_no'],
                    'transaction_date' => date('Y-m-d')
                ]);
            }

            db()->commit();
            flash('success', 'Payment processed successfully');
            redirect('sales');
        } catch (Exception $e) {
            db()->rollBack();
            flash('error', 'Error processing payment: ' . $e->getMessage());
            redirect('sales/' . $id . '/pay');
        }
    }

    public function export(): void {
        Auth::requireLogin();
        Auth::requireActiveTenant();

        $tid = Auth::tenantId();
        $where = "s.tenant_id = ?";
        $params = [$tid];

        if (!empty($_GET['search'])) {
            $where .= " AND (s.sale_no LIKE ? OR c.name LIKE ?)";
            $searchParam = "%{$_GET['search']}%";
            $params[] = $searchParam;
            $params[] = $searchParam;
        }
        if (!empty($_GET['date_from'])) { $where .= " AND DATE(s.created_at) >= ?"; $params[] = $_GET['date_from']; }
        if (!empty($_GET['date_to'])) { $where .= " AND DATE(s.created_at) <= ?"; $params[] = $_GET['date_to']; }
        if (!empty($_GET['status'])) { $where .= " AND s.status = ?"; $params[] = $_GET['status']; }
        if (!empty($_GET['location_id'])) { $where .= " AND s.location_id = ?"; $params[] = $_GET['location_id']; }
        if (!empty($_GET['user_id'])) { $where .= " AND s.user_id = ?"; $params[] = $_GET['user_id']; }

        $sql = "SELECT s.sale_no, s.created_at, c.name as customer_name, l.name as location_name, u.name as user_name,
                s.total, s.amount_paid, s.payment_status, s.status
                FROM sales s
                LEFT JOIN customers c ON c.id = s.customer_id
                LEFT JOIN locations l ON l.id = s.location_id
                LEFT JOIN users u ON u.id = s.user_id
                WHERE {$where} ORDER BY s.created_at DESC";

        $stmt = db()->prepare($sql);
        $stmt->execute($params);
        $sales = $stmt->fetchAll();

        header('Content-Type: text/csv');
        header('Content-Disposition: attachment; filename="sales_export_' . date('Y-m-d') . '.csv"');

        $output = fopen('php://output', 'w');

        fputcsv($output, ['Ref No', 'Date & Time', 'Customer', 'Location', 'User', 'Total', 'Paid', 'Payment Status', 'Status']);

        foreach ($sales as $sale) {
            fputcsv($output, [
                $sale['sale_no'],
                date('Y-m-d H:i', strtotime($sale['created_at'])),
                $sale['customer_name'] ?? 'Walk-in Customer',
                $sale['location_name'] ?? 'N/A',
                $sale['user_name'] ?? 'N/A',
                $sale['total'],
                $sale['amount_paid'],
                ucfirst($sale['payment_status']),
                ucfirst($sale['status'])
            ]);
        }

        fclose($output);
        exit;
    }
}
