<?php

// Public JSON API for the CurryPot customer-facing ordering PWA.
// No login required — this powers the mobile web app that customers install on their phones.
class ApiController extends Controller
{
    private function jsonInput(): array
    {
        $raw = file_get_contents('php://input');
        $data = json_decode($raw, true);
        return is_array($data) ? $data : [];
    }

    public function getStoreStatus(): void
    {
        $status = $this->checkStoreOpen();
        $this->json(['success' => true] + $status);
    }

    private function checkStoreOpen(): array
    {
        if (setting('orders_manually_paused', '0') === '1') {
            return ['is_open' => false, 'message' => "We're temporarily not accepting orders right now. Please check back soon."];
        }

        $openTime = setting('order_open_time', '09:00');
        $closeTime = setting('order_close_time', '22:00');
        $now = date('H:i');

        $isOpen = ($closeTime > $openTime)
            ? ($now >= $openTime && $now < $closeTime)
            : ($now >= $openTime || $now < $closeTime); // handles overnight hours e.g. 18:00–02:00

        if (!$isOpen) {
            return [
                'is_open' => false,
                'message' => "We're currently closed. Order hours: " . date('h:i A', strtotime($openTime)) . " – " . date('h:i A', strtotime($closeTime)) . ".",
            ];
        }
        return ['is_open' => true, 'message' => null];
    }

    public function getMenu(): void
    {
        $menuItemModel = new MenuItem();
        $grouped = $menuItemModel->getMenuForPos();
        $todaysCounts = $menuItemModel->todaysOrderCounts();

        $categories = [];
        foreach ($grouped as $categoryName => $items) {
            $categories[] = [
                'category_name' => $categoryName,
                'items' => array_map(function ($item) use ($todaysCounts) {
                    return [
                        'id'            => (int) $item['id'],
                        'name'          => $item['item_name'],
                        'description'   => $item['description'],
                        'base_price'    => (float) $item['base_price'],
                        'image_url'     => $item['image_path'] ? BASE_URL . '/' . $item['image_path'] : null,
                        'orders_today'  => $todaysCounts[(int) $item['id']] ?? 0,
                        'variants'      => array_map(fn($v) => [
                            'id'                => (int) $v['id'],
                            'name'              => $v['variant_name'],
                            'price_adjustment'  => (float) $v['price_adjustment'],
                            'final_price'       => (float) $item['base_price'] + (float) $v['price_adjustment'],
                        ], $item['variants']),
                    ];
                }, $items),
            ];
        }

        $this->json(['success' => true, 'categories' => $categories]);
    }

    public function getZones(): void
    {
        $zoneModel = new DeliveryZone();
        $zones = array_map(fn($z) => [
            'id'               => (int) $z['id'],
            'name'             => $z['zone_name'],
            'is_free_delivery' => (bool) $z['is_free_delivery'],
            'delivery_fee'     => (float) $z['delivery_fee'],
        ], $zoneModel->activeZones());

        $this->json(['success' => true, 'zones' => $zones]);
    }

    public function getPromotions(): void
    {
        $promotionModel = new Promotion();
        $promotions = array_map(fn($p) => [
            'id'          => (int) $p['id'],
            'eyebrow'     => $p['title'],
            'subtitle'    => $p['subtitle'],
            'button_text' => $p['button_text'],
            'image_url'   => $p['image_path'] ? BASE_URL . '/' . $p['image_path'] : null,
        ], $promotionModel->activeOrdered());

        $this->json(['success' => true, 'promotions' => $promotions]);
    }

    public function getBankDetails(): void
    {
        $this->json([
            'success' => true,
            'bank' => [
                'account_name'   => setting('bank_account_name', ''),
                'bank_name'      => setting('bank_name', ''),
                'account_number' => setting('bank_account_number', ''),
            ],
        ]);
    }

    private function saveDepositSlip(int $orderId, string $base64Data): ?string
    {
        // Expected format: "data:image/jpeg;base64,....."
        if (!preg_match('/^data:image\/(png|jpe?g|webp);base64,(.+)$/', $base64Data, $matches)) {
            return null;
        }
        $ext = $matches[1] === 'jpeg' ? 'jpg' : $matches[1];
        $binary = base64_decode($matches[2]);
        if ($binary === false || strlen($binary) > 5 * 1024 * 1024) {
            return null; // reject invalid or oversized (>5MB) uploads
        }

        $uploadDir = ROOT_PATH . '/public/uploads/deposit_slips/';
        if (!is_dir($uploadDir)) {
            mkdir($uploadDir, 0755, true);
        }

        $filename = 'slip_order' . $orderId . '_' . time() . '.' . $ext;
        file_put_contents($uploadDir . $filename, $binary);

        return 'uploads/deposit_slips/' . $filename;
    }

    public function trackOrder(): void
    {
        $orderNumber = trim($this->input('order', ''));
        if ($orderNumber === '') {
            $this->json(['success' => false, 'message' => 'Order number is required.'], 422);
            return;
        }

        $db = Database::getConnection();
        $stmt = $db->prepare("SELECT id FROM orders WHERE order_number = :num LIMIT 1");
        $stmt->execute(['num' => $orderNumber]);
        $row = $stmt->fetch();
        if (!$row) {
            $this->json(['success' => false, 'message' => 'Order not found.'], 404);
            return;
        }

        $orderModel = new Order();
        $order = $orderModel->findWithDetails((int) $row['id']);

        $statusLabels = ['PENDING' => 'Order Received', 'CONFIRMED' => 'Confirmed', 'PREPARING' => 'Preparing',
            'READY' => 'Ready', 'OUT_FOR_DELIVERY' => 'On the Way', 'COMPLETED' => 'Delivered', 'CANCELLED' => 'Cancelled'];
        $flow = ['PENDING', 'CONFIRMED', 'PREPARING', 'READY', 'OUT_FOR_DELIVERY', 'COMPLETED'];
        $currentIndex = array_search($order['status'], $flow, true);

        $history = [];
        foreach ($order['history'] as $h) {
            $history[$h['status']] = date('d M, h:i A', strtotime($h['changed_at']));
        }

        $totalPaid = 0;
        foreach ($order['payments'] as $p) {
            if ($p['status'] === 'PAID') {
                $totalPaid += (float) $p['amount'];
            }
        }

        $this->json([
            'success'       => true,
            'order_number'  => $order['order_number'],
            'status'        => $order['status'],
            'status_label'  => $statusLabels[$order['status']] ?? $order['status'],
            'current_index' => $currentIndex,
            'flow'          => array_map(fn($s) => ['key' => $s, 'label' => $statusLabels[$s]], $flow),
            'history'       => $history,
            'created_at'    => date('d M Y, h:i A', strtotime($order['created_at'])),
            'order_type'    => ucwords(strtolower(str_replace('_', ' ', $order['order_type']))),
            'zone_name'     => $order['zone_name'] ?? null,
            'delivery_address' => $order['delivery_address'] ?? null,
            'rider_name'    => $order['delivery']['rider_name'] ?? null,
            'items'         => array_map(fn($it) => [
                'name'     => $it['item_name_snapshot'],
                'quantity' => (int) $it['quantity'],
                'line_total' => (float) $it['line_total'],
            ], $order['items']),
            'subtotal'      => (float) $order['subtotal'],
            'delivery_fee'  => (float) $order['delivery_fee'],
            'discount'      => (float) $order['discount'],
            'total_amount'  => (float) $order['total_amount'],
            'total_paid'    => $totalPaid,
            'balance_due'   => max(0, (float) $order['total_amount'] - $totalPaid),
        ]);
    }

    public function myPayments(): void
    {
        if (empty($_SESSION['customer_id'])) {
            $this->json(['success' => false, 'message' => 'Please log in to view your payment history.'], 401);
            return;
        }

        $db = Database::getConnection();
        $stmt = $db->prepare(
            "SELECT p.*, o.order_number, pm.method_name
             FROM payments p
             JOIN orders o ON o.id = p.order_id
             JOIN payment_methods pm ON pm.id = p.payment_method_id
             WHERE o.customer_id = :customer_id
             ORDER BY p.id DESC LIMIT 50"
        );
        $stmt->execute(['customer_id' => $_SESSION['customer_id']]);
        $rows = $stmt->fetchAll();

        $methodLabels = ['PAYHERE' => 'Card (PayHere)', 'CASH' => 'Cash', 'CARD' => 'Card', 'ONLINE_TRANSFER' => 'Bank Transfer'];

        $payments = array_map(fn($p) => [
            'order_number' => $p['order_number'],
            'method'       => $methodLabels[$p['method_name']] ?? ucwords(strtolower(str_replace('_', ' ', $p['method_name']))),
            'amount'       => (float) $p['amount'],
            'fee_amount'   => (float) $p['fee_amount'],
            'status'       => $p['status'],
            'paid_at'      => date('d M Y, h:i A', strtotime($p['paid_at'])),
        ], $rows);

        $totalPaid = array_sum(array_column($payments, 'amount'));

        $this->json(['success' => true, 'payments' => $payments, 'total_paid' => $totalPaid]);
    }

    public function myOrders(): void
    {
        if (empty($_SESSION['customer_id'])) {
            $this->json(['success' => false, 'message' => 'Please log in to view your orders.'], 401);
            return;
        }

        $statusLabels = ['PENDING' => 'Order Received', 'CONFIRMED' => 'Confirmed', 'PREPARING' => 'Preparing',
            'READY' => 'Ready', 'OUT_FOR_DELIVERY' => 'On the Way', 'COMPLETED' => 'Delivered', 'CANCELLED' => 'Cancelled'];

        $orderModel = new Order();
        $orders = $orderModel->findByCustomer((int) $_SESSION['customer_id'], 30);

        $result = array_map(fn($o) => [
            'id'            => (int) $o['id'],
            'order_number'  => $o['order_number'],
            'status'        => $o['status'],
            'status_label'  => $statusLabels[$o['status']] ?? $o['status'],
            'total_amount'  => (float) $o['total_amount'],
            'item_count'    => (int) $o['item_count'],
            'created_at'    => $o['created_at'],
            'track_url'     => (setting('public_base_url') ?: BASE_URL) . '/track?order=' . urlencode($o['order_number']),
        ], $orders);

        $this->json(['success' => true, 'orders' => $result]);
    }

    public function reorder(): void
    {
        if (empty($_SESSION['customer_id'])) {
            $this->json(['success' => false, 'message' => 'Please log in to reorder.'], 401);
            return;
        }
        $orderId = (int) ($this->jsonInput()['order_id'] ?? 0);

        $db = Database::getConnection();
        $stmt = $db->prepare(
            "SELECT oi.menu_item_id, oi.variant_id, oi.quantity, mi.item_name, mi.is_available
             FROM order_items oi
             JOIN orders o ON o.id = oi.order_id
             JOIN menu_items mi ON mi.id = oi.menu_item_id
             WHERE oi.order_id = :order_id AND o.customer_id = :customer_id"
        );
        $stmt->execute(['order_id' => $orderId, 'customer_id' => $_SESSION['customer_id']]);
        $rows = $stmt->fetchAll();

        $items = array_values(array_filter(array_map(fn($r) => $r['is_available'] ? [
            'menu_item_id' => (int) $r['menu_item_id'],
            'variant_id'   => $r['variant_id'] ? (int) $r['variant_id'] : null,
            'quantity'     => (int) $r['quantity'],
            'name'         => $r['item_name'],
        ] : null, $rows)));

        $this->json(['success' => true, 'items' => $items]);
    }

    public function placeOrder(): void
    {
        if (empty($_SESSION['customer_id'])) {
            $this->json(['success' => false, 'message' => 'Please log in or create an account to place an order.'], 401);
            return;
        }

        $storeStatus = $this->checkStoreOpen();
        if (!$storeStatus['is_open']) {
            $this->json(['success' => false, 'message' => $storeStatus['message']], 422);
            return;
        }

        $body = $this->jsonInput();

        $customerName  = $_SESSION['customer_name'];
        $customerPhone = $_SESSION['customer_phone'];
        $orderType     = ($body['order_type'] ?? 'ONLINE_DELIVERY') === 'PICKUP' ? 'TAKEAWAY' : 'ONLINE_DELIVERY';
        $deliveryZoneId = (int) ($body['delivery_zone_id'] ?? 0) ?: null;
        $deliveryAddress = trim($body['delivery_address'] ?? '');
        $notes = trim($body['notes'] ?? '');
        $paymentMethod = $body['payment_method'] ?? 'CARD';
        $paymentReference = trim($body['payment_reference'] ?? '');
        $depositSlipBase64 = $body['deposit_slip_base64'] ?? null;
        $paymentLabels = ['CARD' => 'Card Payment (PayHere)', 'BANK_TRANSFER' => 'Bank Transfer', 'COD' => 'Cash on Delivery'];
        $paymentNote = 'Payment: ' . ($paymentLabels[$paymentMethod] ?? $paymentMethod);
        $notes = $notes ? "{$paymentNote} — {$notes}" : $paymentNote;
        $cart = is_array($body['items'] ?? null) ? $body['items'] : [];

        if ($paymentMethod === 'BANK_TRANSFER' && ($paymentReference === '' || !$depositSlipBase64)) {
            $this->json(['success' => false, 'message' => 'Please provide your transfer reference number and a photo of the deposit slip.'], 422);
            return;
        }

        if ($customerPhone === '' || $customerName === '') {
            $this->json(['success' => false, 'message' => 'Name and phone number are required.'], 422);
            return;
        }
        if (count($cart) === 0) {
            $this->json(['success' => false, 'message' => 'Your cart is empty.'], 422);
            return;
        }
        if ($orderType === 'ONLINE_DELIVERY' && $deliveryAddress === '') {
            $this->json(['success' => false, 'message' => 'Delivery address is required.'], 422);
            return;
        }

        $menuItemModel = new MenuItem();
        $customerModel = new Customer();
        $deliveryZoneModel = new DeliveryZone();

        // Rebuild validated line items server-side — never trust client-submitted prices
        $items = [];
        $subtotal = 0.0;
        foreach ($cart as $line) {
            $menuItemId = (int) ($line['menu_item_id'] ?? 0);
            $variantId  = (int) ($line['variant_id'] ?? 0) ?: null;
            $qty        = max(1, (int) ($line['quantity'] ?? 1));
            if ($menuItemId <= 0) {
                continue;
            }
            try {
                $resolved = $menuItemModel->findWithVariant($menuItemId, $variantId);
            } catch (\Throwable $e) {
                continue; // invalid menu item id — skip it
            }
            if (empty($resolved['item']) || !$resolved['item']['is_available']) {
                continue;
            }
            $name = $resolved['item']['item_name'] . ($resolved['variant_name'] ? ' (' . $resolved['variant_name'] . ')' : '');
            $items[] = [
                'menu_item_id' => $menuItemId,
                'variant_id'   => $variantId,
                'name'         => $name,
                'unit_price'   => $resolved['unit_price'],
                'quantity'     => $qty,
            ];
            $subtotal += $resolved['unit_price'] * $qty;
        }

        if (count($items) === 0) {
            $this->json(['success' => false, 'message' => 'None of the items in your cart are currently available.'], 422);
            return;
        }

        $deliveryFee = 0.0;
        if ($orderType === 'ONLINE_DELIVERY' && $deliveryZoneId) {
            $zone = $deliveryZoneModel->find($deliveryZoneId);
            if ($zone) {
                $deliveryFee = $zone['is_free_delivery'] ? 0.0 : (float) $zone['delivery_fee'];
            }
        }

        $customerId = $customerModel->findOrCreate($customerPhone, $customerName, $deliveryAddress ?: null);
        $total = $subtotal + $deliveryFee;

        $orderModel = new Order();
        $orderId = $orderModel->createWithItems([
            'order_type'        => $orderType,
            'order_source'      => 'APP',
            'customer_id'       => $customerId,
            'delivery_zone_id'  => $orderType === 'ONLINE_DELIVERY' ? $deliveryZoneId : null,
            'status'            => 'PENDING',
            'subtotal'          => $subtotal,
            'delivery_fee'      => $deliveryFee,
            'discount'          => 0,
            'total_amount'      => $total,
            'delivery_address'  => $orderType === 'ONLINE_DELIVERY' ? $deliveryAddress : null,
            'notes'             => $notes ?: null,
            'payment_reference' => $paymentReference ?: null,
            'created_by'        => null,
        ], $items);

        if ($paymentMethod === 'BANK_TRANSFER' && $depositSlipBase64) {
            $slipPath = $this->saveDepositSlip($orderId, $depositSlipBase64);
            if ($slipPath) {
                $orderModel->update($orderId, ['deposit_slip_path' => $slipPath]);
            }
        }

        $order = $orderModel->find($orderId);
        AuditLogger::log(null, 'CUSTOMER_APP_ORDER', 'orders', "Customer app order #{$orderId}, total " . formatMoney($total));

        $this->json([
            'success'      => true,
            'order_id'     => $orderId,
            'order_number' => $order['order_number'],
            'total_amount' => $total,
            'track_url'    => (setting('public_base_url') ?: BASE_URL) . '/track?order=' . urlencode($order['order_number']),
        ]);
    }
}
