<?php
/**
 * User App API - Complete functionality for customer app
 * Auth, categories, subcategories, services, cart, checkout, payment, orders, addresses, profile, coupons
 * Aligned with utile_database.sql and vendor_api.php patterns
 */
require_once __DIR__ . '/../../config/helpers.php';

$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
$uri = $_SERVER['REQUEST_URI'] ?? '/';
$path = parse_url($uri, PHP_URL_PATH);

// CORS
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
header("Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS");
if ($method === 'OPTIONS') {
    http_response_code(204);
    exit;
}

// Route: extract path after user_api.php (path_info) OR from ?path= query (for servers that strip path_info)
$script = 'user_api.php';
$pos = strpos($path, $script);
$route = ($pos !== false) ? substr($path, $pos + strlen($script)) : $path;
$route = '/' . trim($route, '/');
$parts = array_values(array_filter(explode('/', trim($route, '/'))));
// Fallback: ?path=auth/login when path_info is not available (e.g. some shared hosting)
if (empty($parts) && !empty($_GET['path'])) {
    $route = '/' . trim((string)$_GET['path'], '/');
    $parts = array_values(array_filter(explode('/', trim($route, '/'))));
}

function not_found() { json_response(['success' => false, 'message' => 'Not found'], 404); }

/**
 * Create leads from order items when order is confirmed (payment verified / COD confirmed).
 * Vendors in matching category get notified via webhook.
 */
function create_leads_for_order($pdo, $cfg, $orderId, $userId, $addressId, $paymentMode, $orderNumber = null) {
    $stmt = $pdo->prepare("SELECT name, phone FROM users WHERE id=?");
    $stmt->execute([$userId]);
    $u = $stmt->fetch();
    $stmt = $pdo->prepare("SELECT address_line1, address_line2, city, state, pincode FROM user_addresses WHERE id=? AND user_id=?");
    $stmt->execute([$addressId, $userId]);
    $a = $stmt->fetch();
    $location = trim(implode(', ', array_filter([$a['address_line1'] ?? '', $a['address_line2'] ?? '', $a['city'] ?? '', $a['state'] ?? '', $a['pincode'] ?? ''])));
    $custName = $u['name'] ?? 'Customer';
    $custPhone = $u['phone'] ?? '';
    if ($orderNumber === null) {
        $stmt = $pdo->prepare("SELECT order_number FROM user_orders WHERE id=? AND user_id=? LIMIT 1");
        $stmt->execute([$orderId, $userId]);
        $o = $stmt->fetch();
        $orderNumber = $o['order_number'] ?? ('ORD' . $orderId);
    }
    $stmt = $pdo->prepare("SELECT oi.id AS order_item_id, oi.service_id, oi.service_title, oi.price, oi.quantity, oi.time_slot, oi.notes, s.category_id FROM user_order_items oi JOIN services s ON s.id=oi.service_id WHERE oi.order_id=?");
    $stmt->execute([$orderId]);
    $items = $stmt->fetchAll();
    $webhookBase = $cfg['VENDOR_WEBHOOK_URL'] ?? $cfg['VENDOR_API_BASE_URL'] ?? getenv('VENDOR_WEBHOOK_URL') ?: getenv('VENDOR_API_BASE_URL') ?: '';
    $hasOrderCols = null;
    $hasOrderNumber = null;
    foreach ($items as $item) {
        $catId = (int)($item['category_id'] ?? 0);
        $leadPrice = (int)$item['price'] * (int)$item['quantity'];
        $timeSlot = trim($item['time_slot'] ?? 'Flexible');
        $svcDetails = trim($item['notes'] ?? '') ?: $item['service_title'];
        $orderItemId = (int)($item['order_item_id'] ?? 0);
        if ($hasOrderCols === null) {
            try {
                $stmt = $pdo->query("SHOW COLUMNS FROM leads LIKE 'order_id'");
                $hasOrderCols = (bool)$stmt->fetch();
                $stmt = $pdo->query("SHOW COLUMNS FROM leads LIKE 'order_number'");
                $hasOrderNumber = (bool)$stmt->fetch();
            } catch (Exception $e) { $hasOrderCols = false; $hasOrderNumber = false; }
        }
        if ($hasOrderCols && $hasOrderNumber) {
            $stmt = $pdo->prepare("INSERT INTO leads (title, customer_name, customer_phone, service_details, time_slot, location, lead_price, is_available, payment_mode, category_id, order_id, order_item_id, order_number) VALUES (?,?,?,?,?,?,?,1,?,?,?,?,?)");
            $stmt->execute([$item['service_title'], $custName, $custPhone, $svcDetails, $timeSlot, $location, $leadPrice, $paymentMode, $catId ?: null, $orderId, $orderItemId ?: null, $orderNumber]);
        } elseif ($hasOrderCols) {
            $stmt = $pdo->prepare("INSERT INTO leads (title, customer_name, customer_phone, service_details, time_slot, location, lead_price, is_available, payment_mode, category_id, order_id, order_item_id) VALUES (?,?,?,?,?,?,?,1,?,?,?,?)");
            $stmt->execute([$item['service_title'], $custName, $custPhone, $svcDetails, $timeSlot, $location, $leadPrice, $paymentMode, $catId ?: null, $orderId, $orderItemId ?: null]);
        } else {
            $stmt = $pdo->prepare("INSERT INTO leads (title, customer_name, customer_phone, service_details, time_slot, location, lead_price, is_available, payment_mode, category_id) VALUES (?,?,?,?,?,?,?,1,?,?)");
            $stmt->execute([$item['service_title'], $custName, $custPhone, $svcDetails, $timeSlot, $location, $leadPrice, $paymentMode, $catId ?: null]);
        }
        $leadId = (int)$pdo->lastInsertId();
        if ($webhookBase && $catId > 0) {
            $url = rtrim($webhookBase, '/');
            if (strpos($url, '/webhooks/') === false) $url .= '/webhooks/new-lead';
            $ch = curl_init($url);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_POST => true,
                CURLOPT_POSTFIELDS => json_encode(['category_id' => $catId, 'lead_id' => $leadId, 'title' => $item['service_title'], 'lead_price' => $leadPrice]),
                CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
                CURLOPT_TIMEOUT => 5
            ]);
            @curl_exec($ch);
            curl_close($ch);
        }
    }
}

/**
 * Build order object from leads array (lead-centric checkout).
 * Parses time_slot (format "DATE | TIME" e.g. "2025-03-15 | 10:00 AM - 12:00 PM") into scheduled_date and scheduled_time_slot.
 */
function build_order_from_leads($leads) {
    if (empty($leads)) return null;
    $first = $leads[0];
    $orderNumber = trim($first['order_number'] ?? '');
    $orderStatus = trim($first['order_status'] ?? 'active');
    $paymentStatus = trim($first['payment_status'] ?? 'pending');
    $items = [];
    $scheduledDate = null;
    $scheduledTimeSlot = null;
    foreach ($leads as $l) {
        $ts = trim($l['time_slot'] ?? '');
        $items[] = [
            'id' => (int)($l['id'] ?? 0),
            'lead_id' => (int)($l['id'] ?? 0),
            'service_id' => (int)($l['service_id'] ?? 0) ?: null,
            'service_title' => trim($l['title'] ?? ''),
            'price' => (int)($l['lead_price'] ?? 0),
            'quantity' => max(1, (int)($l['quantity'] ?? 1)),
            'time_slot' => $ts,
            'notes' => trim($l['service_details'] ?? '')
        ];
        if ($scheduledDate === null && $ts !== '') {
            if (strpos($ts, '|') !== false) {
                $parts = array_map('trim', explode('|', $ts, 2));
                $scheduledDate = $parts[0] ?? null;
                $scheduledTimeSlot = $parts[1] ?? $ts;
            } else {
                $scheduledTimeSlot = $ts;
            }
        }
    }
    return [
        'id' => $orderNumber,
        'order_number' => $orderNumber,
        'status' => $orderStatus === 'cancelled' ? 'cancelled' : ($paymentStatus === 'paid' ? 'confirmed' : 'pending'),
        'items' => $items,
        'address_id' => (int)($first['address_id'] ?? 0) ?: null,
        'subtotal' => (int)($first['subtotal'] ?? 0),
        'discount_amount' => (int)($first['discount_amount'] ?? 0),
        'total_amount' => (int)($first['total_amount'] ?? 0),
        'payment_status' => $paymentStatus,
        'payment_mode' => trim($first['payment_mode'] ?? 'online'),
        'coupon_id' => (int)($first['coupon_id'] ?? 0) ?: null,
        'razorpay_order_id' => trim($first['razorpay_order_id'] ?? '') ?: null,
        'razorpay_payment_id' => trim($first['razorpay_payment_id'] ?? '') ?: null,
        'created_at' => $first['created_at'] ?? null,
        'scheduled_date' => $scheduledDate,
        'scheduled_time_slot' => $scheduledTimeSlot
    ];
}

/**
 * Enrich leads-based order(s) with vendor and completion_otp per item (from jobs when vendor has picked the lead).
 * Matches by lead id to jobs.
 */
function enrich_leads_with_vendor($pdo, $orders) {
    foreach ($orders as &$ord) {
        $ord['assigned_vendor'] = null;
        $ord['completion_otp'] = null;
        if (empty($ord['items'])) continue;

        foreach ($ord['items'] as &$it) {
            $it['vendor'] = null;
            $it['completion_otp'] = null;
            $leadId = (int)($it['lead_id'] ?? $it['id'] ?? 0);
            if ($leadId <= 0) continue;

            $stmt = $pdo->prepare("
                SELECT l.id, l.completion_otp, j.vendor_id, v.name AS vendor_name, v.phone AS vendor_phone
                FROM leads l
                INNER JOIN jobs j ON j.lead_id = l.id AND j.status IN ('accepted', 'in_progress', 'completed')
                INNER JOIN vendors v ON v.id = j.vendor_id
                WHERE l.id = ?
            ");
            $stmt->execute([$leadId]);
            $row = $stmt->fetch();
            if ($row && !empty($row['vendor_id'])) {
                $it['vendor'] = [
                    'id' => (int)$row['vendor_id'],
                    'name' => $row['vendor_name'] ?? '',
                    'phone' => $row['vendor_phone'] ?? '',
                    'profile_photo' => null
                ];
                $it['completion_otp'] = trim($row['completion_otp'] ?? '') ?: null;
            }
        }

        $firstVendor = null;
        $firstOtp = null;
        foreach ($ord['items'] as $it) {
            if (!empty($it['vendor']) && !$firstVendor) $firstVendor = $it['vendor'];
            if (!empty($it['completion_otp']) && !$firstOtp) $firstOtp = $it['completion_otp'];
        }
        $ord['assigned_vendor'] = $firstVendor;
        $ord['completion_otp'] = $firstOtp;

        // Derive order status from jobs table (jobs.lead_id = leads.id)
        // Only "completed" when ALL leads in this order have at least one completed job
        $currentStatus = strtolower(trim($ord['status'] ?? ''));
        if ($currentStatus === 'cancelled') {
            // Keep cancelled - do not override from jobs
        } else {
        $leadIds = array_filter(array_map(function($it) { return (int)($it['lead_id'] ?? $it['id'] ?? 0); }, $ord['items']));
        if (!empty($leadIds)) {
            $placeholders = implode(',', array_fill(0, count($leadIds), '?'));
            $totalLeads = count($leadIds);
            // Count distinct leads that have a completed job
            $stmt = $pdo->prepare("SELECT COUNT(DISTINCT j.lead_id) AS n FROM jobs j WHERE j.lead_id IN ($placeholders) AND j.status = 'completed'");
            $stmt->execute(array_values($leadIds));
            $completedLeadsCount = (int)($stmt->fetch()['n'] ?? 0);
            $allCompleted = ($completedLeadsCount === $totalLeads);
            if ($allCompleted) {
                $ord['status'] = 'completed';
            } else {
                // Get all job statuses to determine in_progress / accepted
                $stmt = $pdo->prepare("SELECT j.status FROM jobs j WHERE j.lead_id IN ($placeholders)");
                $stmt->execute(array_values($leadIds));
                $jobStatuses = $stmt->fetchAll(PDO::FETCH_COLUMN);
                if (in_array('in_progress', $jobStatuses)) {
                    $ord['status'] = 'in_progress';
                } elseif (in_array('accepted', $jobStatuses)) {
                    $ord['status'] = 'accepted';
                } elseif (in_array('completed', $jobStatuses)) {
                    $ord['status'] = 'in_progress';
                }
            }
        }
        }
    }
    return $orders;
}

/**
 * Enrich order(s) with vendor and completion_otp per item (from leads/jobs when vendor has picked the lead).
 */
function enrich_orders_with_vendor($pdo, $orders, $userPhone) {
    $hasOrderId = null;
    $hasOrderNumber = null;
    try {
        $r = $pdo->query("SHOW COLUMNS FROM leads LIKE 'order_id'");
        $hasOrderId = (bool)$r->fetch();
        $r = $pdo->query("SHOW COLUMNS FROM leads LIKE 'order_number'");
        $hasOrderNumber = (bool)$r->fetch();
    } catch (Exception $e) { $hasOrderId = false; $hasOrderNumber = false; }

    foreach ($orders as &$ord) {
        $ord['assigned_vendor'] = null;
        $ord['completion_otp'] = null;
        if (empty($ord['items'])) continue;

        foreach ($ord['items'] as &$it) {
            $it['vendor'] = null;
            $it['completion_otp'] = null;
        }

        $oid = (int)($ord['id'] ?? 0);
        $orderNumber = trim($ord['order_number'] ?? '');
        if ($oid <= 0 && $orderNumber === '') continue;

        $leadsWithJob = [];
        if ($hasOrderId && $oid > 0) {
            // Match by order_id (preferred - strict link)
            $stmt = $pdo->prepare("
                SELECT l.id, l.order_item_id, l.title, l.completion_otp, j.vendor_id, v.name AS vendor_name, v.phone AS vendor_phone
                FROM leads l
                INNER JOIN jobs j ON j.lead_id = l.id AND j.status IN ('accepted', 'in_progress', 'completed')
                INNER JOIN vendors v ON v.id = j.vendor_id
                WHERE l.order_id = ?
            ");
            $stmt->execute([$oid]);
            $leadsWithJob = $stmt->fetchAll();
        }
        if (empty($leadsWithJob) && $hasOrderNumber && $orderNumber !== '') {
            // Fallback: match by order_number (when order_id not in leads)
            $stmt = $pdo->prepare("
                SELECT l.id, l.order_item_id, l.title, l.completion_otp, j.vendor_id, v.name AS vendor_name, v.phone AS vendor_phone
                FROM leads l
                INNER JOIN jobs j ON j.lead_id = l.id AND j.status IN ('accepted', 'in_progress', 'completed')
                INNER JOIN vendors v ON v.id = j.vendor_id
                WHERE l.order_number = ?
            ");
            $stmt->execute([$orderNumber]);
            $leadsWithJob = $stmt->fetchAll();
        }

        foreach ($ord['items'] as &$it) {
            $match = null;
            // Match by order_item_id (strict) when available; else by title for order_number-only path
            if (!empty($leadsWithJob)) {
                $oiId = (int)($it['id'] ?? 0);
                $itemTitle = trim($it['service_title'] ?? '');
                foreach ($leadsWithJob as $l) {
                    $leadOrderItemId = (int)($l['order_item_id'] ?? 0);
                    $leadTitle = trim($l['title'] ?? '');
                    $idMatch = $leadOrderItemId > 0 && $leadOrderItemId === $oiId;
                    $titleMatch = $itemTitle !== '' && $leadTitle === $itemTitle;
                    if ($l['vendor_id'] && ($idMatch || (!$idMatch && $titleMatch))) {
                        $match = $l;
                        break;
                    }
                }
            }
            if ($match && !empty($match['vendor_id'])) {
                $it['vendor'] = [
                    'id' => (int)$match['vendor_id'],
                    'name' => $match['vendor_name'] ?? '',
                    'phone' => $match['vendor_phone'] ?? '',
                    'profile_photo' => null
                ];
                $it['completion_otp'] = trim($match['completion_otp'] ?? '') ?: null;
            }
        }

        $firstVendor = null;
        $firstOtp = null;
        foreach ($ord['items'] as $it) {
            if ($it['vendor'] && !$firstVendor) $firstVendor = $it['vendor'];
            if ($it['completion_otp'] && !$firstOtp) $firstOtp = $it['completion_otp'];
        }
        $ord['assigned_vendor'] = $firstVendor;
        $ord['completion_otp'] = $firstOtp;
    }
    return $orders;
}

/**
 * Trigger vendor webhooks for leads (like create_leads_for_order).
 */
function trigger_lead_webhooks($pdo, $cfg, $leads) {
    $webhookBase = $cfg['VENDOR_WEBHOOK_URL'] ?? $cfg['VENDOR_API_BASE_URL'] ?? getenv('VENDOR_WEBHOOK_URL') ?: getenv('VENDOR_API_BASE_URL') ?: '';
    if (!$webhookBase) return;
    $url = rtrim($webhookBase, '/');
    if (strpos($url, '/webhooks/') === false) $url .= '/webhooks/new-lead';
    foreach ($leads as $l) {
        $catId = (int)($l['category_id'] ?? 0);
        if ($catId > 0) {
            $ch = curl_init($url);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_POST => true,
                CURLOPT_POSTFIELDS => json_encode(['category_id' => $catId, 'lead_id' => (int)$l['id'], 'title' => $l['title'] ?? '', 'lead_price' => (int)($l['lead_price'] ?? 0)]),
                CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
                CURLOPT_TIMEOUT => 5
            ]);
            @curl_exec($ch);
            curl_close($ch);
        }
    }
}

$pdo = db();
$cfg = app_config();

// Ensure vendor webhook URL is available (for notifications when user books)
if (empty($cfg['VENDOR_WEBHOOK_URL'])) {
    $cfg['VENDOR_WEBHOOK_URL'] = getenv('VENDOR_WEBHOOK_URL') ?: getenv('VENDOR_API_BASE_URL') ?: '';
}
if (empty($cfg['VENDOR_API_BASE_URL'])) {
    $cfg['VENDOR_API_BASE_URL'] = getenv('VENDOR_API_BASE_URL') ?: getenv('VENDOR_WEBHOOK_URL') ?: '';
}
// Vendor API base - used for webhooks when user creates booking. Set in config or env.
// Example: https://utile.filemakrxpert.com/public/api
define('VENDOR_WEBHOOK_BASE_FALLBACK', $cfg['VENDOR_WEBHOOK_URL'] ?? $cfg['VENDOR_API_BASE_URL'] ?? '');

// Razorpay config (from env or app_config)
$razorpayKey = getenv('RAZORPAY_KEY_ID') ?: ($cfg['RAZORPAY_KEY_ID'] ?? '');
$razorpaySecret = getenv('RAZORPAY_KEY_SECRET') ?: ($cfg['RAZORPAY_KEY_SECRET'] ?? '');

try {

    // ==================== AUTH ====================
    if (($parts[0] ?? '') === 'auth') {
        $action = $parts[1] ?? '';

        // POST /auth/request-otp
        if ($method === 'POST' && $action === 'request-otp') {
            $data = read_json();
            if (empty($data) && !empty($_POST)) $data = $_POST;
            $phone = preg_replace('/\D+/', '', (string)($data['phone'] ?? ''));
            $purpose = (($data['purpose'] ?? 'login') === 'register') ? 'register' : 'login';
            if ($phone === '') json_response(['success' => false, 'message' => 'Phone required'], 422);

            $otp = $cfg['DEMO_OTP'] ?? str_pad((string)random_int(1000, 9999), 4, '0');
            $stmt = $pdo->prepare("INSERT INTO otp_codes (phone, otp, purpose, expires_at) VALUES (?,?,?, DATE_ADD(NOW(), INTERVAL 10 MINUTE))");
            $stmt->execute([$phone, $otp, $purpose]);
            $maskOtp = ($cfg['DEMO_OTP'] ?? null) ? $otp : null; // Only expose OTP in demo mode
            json_response(['success' => true, 'message' => $maskOtp ? 'OTP sent (demo)' : 'OTP sent', 'otp' => $maskOtp]);
        }

        // POST /auth/register
        if ($method === 'POST' && $action === 'register') {
            $data = read_json();
            if (empty($data) && !empty($_POST)) $data = $_POST;
            $name = trim($data['name'] ?? '');
            $phone = preg_replace('/\D+/', '', (string)($data['phone'] ?? ''));
            $email = trim($data['email'] ?? '');
            $otp = trim($data['otp'] ?? '');
            if ($name === '' || $phone === '' || $email === '' || $otp === '') {
                json_response(['success' => false, 'message' => 'name, phone, email, otp required'], 422);
            }
            $demoOtp = $cfg['DEMO_OTP'] ?? null;
            $validOtp = ($demoOtp && $otp === $demoOtp);
            if (!$validOtp) {
                $stmt = $pdo->prepare("SELECT id FROM otp_codes WHERE phone=? AND otp=? AND purpose='register' AND expires_at > NOW() ORDER BY id DESC LIMIT 1");
                $stmt->execute([$phone, $otp]);
                $validOtp = (bool)$stmt->fetch();
            }
            if (!$validOtp) json_response(['success' => false, 'message' => 'Invalid or expired OTP'], 401);
            $stmt = $pdo->prepare("SELECT id FROM users WHERE phone=? OR email=? LIMIT 1");
            $stmt->execute([$phone, $email]);
            if ($stmt->fetch()) json_response(['success' => false, 'message' => 'User already exists'], 409);

            $stmt = $pdo->prepare("INSERT INTO users (name, phone, email) VALUES (?,?,?)");
            $stmt->execute([$name, $phone, $email]);
            $userId = (int)$pdo->lastInsertId();

            $token = random_token(64);
            $stmt = $pdo->prepare("INSERT INTO user_tokens (user_id, token, expires_at) VALUES (?,?, DATE_ADD(NOW(), INTERVAL 30 DAY))");
            $stmt->execute([$userId, $token]);

            $stmt = $pdo->prepare("SELECT id, name, email, phone, created_at FROM users WHERE id=?");
            $stmt->execute([$userId]);
            $user = $stmt->fetch();
            json_response(['success' => true, 'message' => 'Registered', 'token' => $token, 'user' => $user]);
        }

        // POST /auth/login - logs in existing user OR auto-registers new user, same response format
        if ($method === 'POST' && $action === 'login') {
            $data = read_json();
            if (empty($data) && !empty($_POST)) $data = $_POST;
            $phone = preg_replace('/\D+/', '', (string)($data['phone'] ?? ''));
            $otp = trim($data['otp'] ?? '');
            $name = trim($data['name'] ?? '');
            $email = trim($data['email'] ?? '');
            if ($phone === '' || $otp === '') json_response(['success' => false, 'message' => 'phone and otp required'], 422);

            $demoOtp = $cfg['DEMO_OTP'] ?? null;
            $validOtp = ($demoOtp && $otp === $demoOtp);
            if (!$validOtp) {
                $stmt = $pdo->prepare("SELECT id FROM otp_codes WHERE phone=? AND otp=? AND (purpose='login' OR purpose='register') AND expires_at > NOW() ORDER BY id DESC LIMIT 1");
                $stmt->execute([$phone, $otp]);
                $validOtp = (bool)$stmt->fetch();
            }
            if (!$validOtp) json_response(['success' => false, 'message' => 'Invalid or expired OTP'], 401);

            $stmt = $pdo->prepare("SELECT id, name, email, phone, created_at FROM users WHERE phone=? LIMIT 1");
            $stmt->execute([$phone]);
            $user = $stmt->fetch();

            if (!$user) {
                // New user: auto-register with phone-verified OTP, then return login response
                $defaultName = $name !== '' ? $name : ('User ' . substr($phone, -4));
                $defaultEmail = $email !== '' ? $email : ($phone . '@utile.user');
                $stmt = $pdo->prepare("SELECT id FROM users WHERE email=? LIMIT 1");
                $stmt->execute([$defaultEmail]);
                if ($stmt->fetch()) {
                    $defaultEmail = $phone . '.' . time() . '@utile.user';
                }
                $stmt = $pdo->prepare("INSERT INTO users (name, phone, email) VALUES (?,?,?)");
                $stmt->execute([$defaultName, $phone, $defaultEmail]);
                $userId = (int)$pdo->lastInsertId();
                $stmt = $pdo->prepare("SELECT id, name, email, phone, created_at FROM users WHERE id=?");
                $stmt->execute([$userId]);
                $user = $stmt->fetch();
            }

            $token = random_token(64);
            $stmt = $pdo->prepare("INSERT INTO user_tokens (user_id, token, expires_at) VALUES (?,?, DATE_ADD(NOW(), INTERVAL 30 DAY))");
            $stmt->execute([(int)$user['id'], $token]);
            json_response(['success' => true, 'message' => 'Login ok', 'token' => $token, 'user' => $user]);
        }

        // POST /auth/logout - invalidate current token
        if ($method === 'POST' && $action === 'logout') {
            $user = require_user_auth();
            $token = trim($_SERVER['HTTP_AUTHORIZATION'] ?? '');
            if (preg_match('/^Bearer\s+(.+)$/i', $token, $m)) {
                $token = trim($m[1]);
                $stmt = $pdo->prepare("DELETE FROM user_tokens WHERE user_id=? AND token=?");
                $stmt->execute([(int)$user['id'], $token]);
            }
            json_response(['success' => true, 'message' => 'Logged out']);
        }

        not_found();
    }

    // ==================== CATEGORIES (public) ====================
    if (($parts[0] ?? '') === 'categories') {
        $cityId = (int)($_GET['city_id'] ?? 1);
        // GET /categories - list categories (filter by city via service_cities when table exists)
        if ($method === 'GET' && count($parts) === 1) {
            $onlyActive = ($_GET['only_active'] ?? '1') === '1' ? 1 : 0;
            try {
                $stmt = $pdo->query("SHOW TABLES LIKE 'service_cities'");
                if ($stmt->rowCount() > 0 && $cityId > 0) {
                    $stmt = $pdo->prepare("SELECT DISTINCT c.id, c.name, c.thumbnail, c.is_active, c.created_at FROM categories c INNER JOIN services s ON s.category_id=c.id INNER JOIN service_cities sc ON sc.service_id=s.id AND sc.city_id=? WHERE c.is_active=1 ORDER BY c.name ASC");
                    $stmt->execute([$cityId]);
                } else {
                    $stmt = $onlyActive ? $pdo->query("SELECT id, name, thumbnail, is_active, created_at FROM categories WHERE is_active=1 ORDER BY name ASC") : $pdo->query("SELECT id, name, thumbnail, is_active, created_at FROM categories ORDER BY name ASC");
                }
            } catch (Exception $e) {
                $stmt = $onlyActive ? $pdo->query("SELECT id, name, thumbnail, is_active, created_at FROM categories WHERE is_active=1 ORDER BY name ASC") : $pdo->query("SELECT id, name, thumbnail, is_active, created_at FROM categories ORDER BY name ASC");
            }
            $rows = $stmt ? $stmt->fetchAll() : [];
            json_response(['success' => true, 'categories' => $rows]);
        }
        // GET /categories/{id}/subcategories - subcategories by category
        if ($method === 'GET' && count($parts) === 3 && ($parts[2] ?? '') === 'subcategories') {
            $catId = (int)$parts[1];
            if ($catId <= 0) json_response(['success' => false, 'message' => 'Invalid category'], 422);
            $stmt = $pdo->prepare("SELECT id, category_id, name, thumbnail, is_active FROM subcategories WHERE category_id=? AND is_active=1 ORDER BY name ASC");
            $stmt->execute([$catId]);
            json_response(['success' => true, 'subcategories' => $stmt->fetchAll()]);
        }
        // GET /categories/{id} - single category detail
        if ($method === 'GET' && count($parts) === 2) {
            $catId = (int)$parts[1];
            $stmt = $pdo->prepare("SELECT id, name, thumbnail, is_active, created_at FROM categories WHERE id=?");
            $stmt->execute([$catId]);
            $cat = $stmt->fetch();
            if (!$cat) json_response(['success' => false, 'message' => 'Category not found'], 404);
            json_response(['success' => true, 'category' => $cat]);
        }
    }

    // ==================== SUBCATEGORIES BY CATEGORY (public) ====================
    if (($parts[0] ?? '') === 'subcategories') {
        $subId = (int)($parts[1] ?? 0);
        $catId = (int)($_GET['category_id'] ?? 0);
        // GET /subcategories/{id}/services - services by subcategory (optionally filter by city_id)
        if ($method === 'GET' && count($parts) === 3 && ($parts[2] ?? '') === 'services') {
            if ($subId <= 0) json_response(['success' => false, 'message' => 'Invalid subcategory'], 422);
            $cityId = (int)($_GET['city_id'] ?? 1);
            try {
                $r = $pdo->query("SHOW TABLES LIKE 'service_cities'");
                if ($r->rowCount() > 0 && $cityId > 0) {
                    $stmt = $pdo->prepare("SELECT s.id, s.category_id, s.subcategory_id, s.title, s.price, s.description, s.thumbnail, s.images_json, s.is_active FROM services s INNER JOIN service_cities sc ON sc.service_id=s.id AND sc.city_id=? WHERE s.subcategory_id=? AND s.is_active=1 ORDER BY s.title ASC");
                    $stmt->execute([$cityId, $subId]);
                } else {
                    $stmt = $pdo->prepare("SELECT id, category_id, subcategory_id, title, price, description, thumbnail, images_json, is_active FROM services WHERE subcategory_id=? AND is_active=1 ORDER BY title ASC");
                    $stmt->execute([$subId]);
                }
            } catch (Exception $e) {
                $stmt = $pdo->prepare("SELECT id, category_id, subcategory_id, title, price, description, thumbnail, images_json, is_active FROM services WHERE subcategory_id=? AND is_active=1 ORDER BY title ASC");
                $stmt->execute([$subId]);
            }
            $rows = $stmt->fetchAll();
            foreach ($rows as &$r) {
                $imgs = json_decode($r['images_json'] ?? '[]', true);
                $r['images'] = is_array($imgs) ? $imgs : [];
            }
            json_response(['success' => true, 'services' => $rows]);
        }
        if ($subId > 0 && count($parts) === 2) {
            $stmt = $pdo->prepare("SELECT id, category_id, name, thumbnail, is_active FROM subcategories WHERE id=?");
            $stmt->execute([$subId]);
            $sub = $stmt->fetch();
            if (!$sub) json_response(['success' => false, 'message' => 'Subcategory not found'], 404);
            json_response(['success' => true, 'subcategory' => $sub]);
        }
        $catId = $catId ?: $subId;
        if ($catId <= 0) json_response(['success' => false, 'message' => 'category_id required'], 422);
        $stmt = $pdo->prepare("SELECT id, category_id, name, thumbnail, is_active FROM subcategories WHERE category_id=? AND is_active=1 ORDER BY name ASC");
        $stmt->execute([$catId]);
        json_response(['success' => true, 'subcategories' => $stmt->fetchAll()]);
    }

    // ==================== SERVICES BY SUBCATEGORY (public) ====================
    if (($parts[0] ?? '') === 'services') {
        $subId = (int)($parts[1] ?? $_GET['subcategory_id'] ?? 0);
        $cityId = (int)($_GET['city_id'] ?? 1);
        try {
            $r = $pdo->query("SHOW TABLES LIKE 'service_cities'");
            $hasServiceCities = $r && $r->rowCount() > 0;
            if ($hasServiceCities && $cityId > 0) {
                if ($subId > 0) {
                    $stmt = $pdo->prepare("SELECT s.id, s.category_id, s.subcategory_id, s.title, s.price, s.description, s.thumbnail, s.images_json, s.is_active FROM services s INNER JOIN service_cities sc ON sc.service_id=s.id AND sc.city_id=? WHERE s.subcategory_id=? AND s.is_active=1 ORDER BY s.title ASC");
                    $stmt->execute([$cityId, $subId]);
                } else {
                    $stmt = $pdo->prepare("SELECT s.id, s.category_id, s.subcategory_id, s.title, s.price, s.description, s.thumbnail, s.images_json, s.is_active FROM services s INNER JOIN service_cities sc ON sc.service_id=s.id AND sc.city_id=? WHERE s.is_active=1 ORDER BY s.title ASC");
                    $stmt->execute([$cityId]);
                }
            } else {
                if ($subId > 0) {
                    $stmt = $pdo->prepare("SELECT id, category_id, subcategory_id, title, price, description, thumbnail, images_json, is_active FROM services WHERE subcategory_id=? AND is_active=1 ORDER BY title ASC");
                    $stmt->execute([$subId]);
                } else {
                    $stmt = $pdo->query("SELECT id, category_id, subcategory_id, title, price, description, thumbnail, images_json, is_active FROM services WHERE is_active=1 ORDER BY title ASC");
                }
            }
        } catch (Exception $e) {
            if ($subId > 0) {
                $stmt = $pdo->prepare("SELECT id, category_id, subcategory_id, title, price, description, thumbnail, images_json, is_active FROM services WHERE subcategory_id=? AND is_active=1 ORDER BY title ASC");
                $stmt->execute([$subId]);
            } else {
                $stmt = $pdo->query("SELECT id, category_id, subcategory_id, title, price, description, thumbnail, images_json, is_active FROM services WHERE is_active=1 ORDER BY title ASC");
            }
        }
        $rows = $stmt ? $stmt->fetchAll() : [];
        foreach ($rows as &$r) {
            $imgs = json_decode($r['images_json'] ?? '[]', true);
            $r['images'] = is_array($imgs) ? $imgs : [];
        }
        json_response(['success' => true, 'services' => $rows]);
    }

    // ==================== SEARCH (public) – services, subcategories, categories ====================
    if (($parts[0] ?? '') === 'search' && $method === 'GET' && count($parts) === 1) {
        $q = trim($_GET['q'] ?? '');
        $cityId = (int)($_GET['city_id'] ?? 1);
        $result = ['categories' => [], 'subcategories' => [], 'services' => []];

        if (strlen($q) < 2) {
            json_response(['success' => true, 'search' => $result]);
            exit;
        }

        $like = '%' . $q . '%';

        try {
            $stmt = $pdo->prepare("SELECT id, name, thumbnail, is_active FROM categories WHERE is_active=1 AND name LIKE ? ORDER BY name ASC LIMIT 10");
            $stmt->execute([$like]);
            $result['categories'] = $stmt->fetchAll();

            $stmt = $pdo->prepare("SELECT s.id, s.category_id, s.name, s.thumbnail, s.is_active FROM subcategories s WHERE s.is_active=1 AND s.name LIKE ? ORDER BY s.name ASC LIMIT 15");
            $stmt->execute([$like]);
            $result['subcategories'] = $stmt->fetchAll();

            $r = $pdo->query("SHOW TABLES LIKE 'service_cities'");
            $hasServiceCities = $r && $r->rowCount() > 0;
            if ($hasServiceCities && $cityId > 0) {
                $stmt = $pdo->prepare("SELECT s.id, s.category_id, s.subcategory_id, s.title, s.price, s.description, s.thumbnail, s.images_json FROM services s INNER JOIN service_cities sc ON sc.service_id=s.id AND sc.city_id=? WHERE s.is_active=1 AND (s.title LIKE ? OR s.description LIKE ?) ORDER BY s.title ASC LIMIT 30");
                $stmt->execute([$cityId, $like, $like]);
            } else {
                $stmt = $pdo->prepare("SELECT id, category_id, subcategory_id, title, price, description, thumbnail, images_json FROM services WHERE is_active=1 AND (title LIKE ? OR description LIKE ?) ORDER BY title ASC LIMIT 30");
                $stmt->execute([$like, $like]);
            }
            $rows = $stmt ? $stmt->fetchAll() : [];
            foreach ($rows as &$r) {
                $imgs = json_decode($r['images_json'] ?? '[]', true);
                $r['images'] = is_array($imgs) ? $imgs : [];
            }
            $result['services'] = $rows;
        } catch (Exception $e) {
            $result = ['categories' => [], 'subcategories' => [], 'services' => []];
        }

        json_response(['success' => true, 'search' => $result]);
        exit;
    }

    // ==================== CONTACT FORM (public) ====================
    if (($parts[0] ?? '') === 'contact' && $method === 'POST' && count($parts) === 1) {
        $data = read_json();
        if (empty($data) && !empty($_POST)) $data = $_POST;
        $name = trim($data['name'] ?? '');
        $email = trim($data['email'] ?? '');
        $phone = trim($data['phone'] ?? '');
        $message = trim($data['message'] ?? '');
        if ($name === '' || $email === '' || $message === '') {
            json_response(['success' => false, 'message' => 'name, email, message required'], 422);
        }
        try {
            $stmt = $pdo->prepare("INSERT INTO contact_messages (name, email, phone, message) VALUES (?,?,?,?)");
            $stmt->execute([$name, $email, $phone, $message]);
            json_response(['success' => true, 'message' => 'Message sent. We will get back to you soon.']);
        } catch (Exception $ex) {
            json_response(['success' => false, 'message' => 'Could not save message. Please try again.'], 500);
        }
    }

    // ==================== MASTER STATES / CITIES / PINCODES (public) ====================
    if (($parts[0] ?? '') === 'states' && $method === 'GET' && count($parts) === 1) {
        try {
            $stmt = $pdo->query("SELECT id, name, code FROM master_states WHERE is_active=1 ORDER BY name ASC");
            $rows = $stmt ? $stmt->fetchAll() : [];
        } catch (Exception $e) {
            $rows = [];
        }
        json_response(['success' => true, 'states' => $rows]);
    }
    if (($parts[0] ?? '') === 'cities' && $method === 'GET' && count($parts) === 1) {
        $stateId = (int)($_GET['state_id'] ?? 0);
        try {
            if ($stateId > 0) {
                $stmt = $pdo->prepare("SELECT id, state_id, name FROM master_cities WHERE state_id=? AND is_active=1 ORDER BY name ASC");
                $stmt->execute([$stateId]);
            } else {
                $stmt = $pdo->query("SELECT id, state_id, name FROM master_cities WHERE is_active=1 ORDER BY name ASC");
            }
            $rows = $stmt ? $stmt->fetchAll() : [];
        } catch (Exception $e) {
            $rows = [];
        }
        json_response(['success' => true, 'cities' => $rows]);
    }
    if (($parts[0] ?? '') === 'pincodes' && $method === 'GET' && count($parts) === 1) {
        $cityId = (int)($_GET['city_id'] ?? 0);
        $search = trim($_GET['q'] ?? '');
        try {
            if ($cityId > 0) {
                $stmt = $pdo->prepare("SELECT id, city_id, pincode, area_name FROM master_pincodes WHERE city_id=? AND is_active=1 ORDER BY pincode ASC");
                $stmt->execute([$cityId]);
            } elseif ($search !== '') {
                $stmt = $pdo->prepare("SELECT id, city_id, pincode, area_name FROM master_pincodes WHERE (pincode LIKE ? OR area_name LIKE ?) AND is_active=1 ORDER BY pincode ASC LIMIT 50");
                $like = '%' . $search . '%';
                $stmt->execute([$like, $like]);
            } else {
                $rows = [];
                json_response(['success' => true, 'pincodes' => $rows]);
                exit;
            }
            $rows = $stmt ? $stmt->fetchAll() : [];
        } catch (Exception $e) {
            $rows = [];
        }
        json_response(['success' => true, 'pincodes' => $rows]);
    }

    // ==================== SERVICE DETAIL (public) ====================
    if (($parts[0] ?? '') === 'service' && isset($parts[1])) {
        $serviceId = (int)$parts[1];
        $stmt = $pdo->prepare("SELECT s.*, c.name AS category_name, sc.name AS subcategory_name FROM services s LEFT JOIN categories c ON c.id=s.category_id LEFT JOIN subcategories sc ON sc.id=s.subcategory_id WHERE s.id=? AND s.is_active=1");
        $stmt->execute([$serviceId]);
        $service = $stmt->fetch();
        if (!$service) json_response(['success' => false, 'message' => 'Service not found'], 404);
        $imgs = json_decode($service['images_json'] ?? '[]', true);
        $service['images'] = is_array($imgs) ? $imgs : [];
        json_response(['success' => true, 'service' => $service]);
    }

    // ==================== PROTECTED ROUTES (require user auth) ====================
    $user = null;
    $protectedPrefixes = ['cart', 'checkout', 'orders', 'addresses', 'profile', 'payment'];
    $isProtected = in_array($parts[0] ?? '', $protectedPrefixes);
    if ($isProtected) {
        $user = require_user_auth();
    }

    // ==================== CART ====================
    if (($parts[0] ?? '') === 'cart') {
        $userId = (int)$user['id'];

        // GET /cart - list cart with service details (includes category_id for same-category rule)
        if ($method === 'GET' && count($parts) === 1) {
            $stmt = $pdo->prepare("
                SELECT c.id, c.service_id, c.quantity, c.time_slot, c.address_id, c.notes, c.created_at,
                       s.title, s.price, s.thumbnail, s.description, s.category_id
                FROM user_cart c
                JOIN services s ON s.id = c.service_id AND s.is_active=1
                WHERE c.user_id=?
                ORDER BY c.created_at DESC
            ");
            $stmt->execute([$userId]);
            $items = $stmt->fetchAll();
            $subtotal = 0;
            foreach ($items as &$i) {
                $i['line_total'] = (int)$i['price'] * (int)$i['quantity'];
                $subtotal += $i['line_total'];
            }
            json_response(['success' => true, 'cart' => $items, 'subtotal' => $subtotal]);
        }

        // POST /cart - add to cart (only same category allowed; adding different category clears previous)
        if ($method === 'POST' && count($parts) === 1) {
            $data = read_json();
            $serviceId = (int)($data['service_id'] ?? 0);
            $quantity = max(1, (int)($data['quantity'] ?? 1));
            $timeSlot = trim($data['time_slot'] ?? '');
            $addressId = (int)($data['address_id'] ?? 0) ?: null;
            $notes = trim($data['notes'] ?? '');
            if ($serviceId <= 0) json_response(['success' => false, 'message' => 'service_id required'], 422);

            $stmt = $pdo->prepare("SELECT id, price, category_id FROM services WHERE id=? AND is_active=1");
            $stmt->execute([$serviceId]);
            $svc = $stmt->fetch();
            if (!$svc) json_response(['success' => false, 'message' => 'Service not found'], 404);
            $newCatId = (int)($svc['category_id'] ?? 0);

            // If cart has items from a different category, clear cart first
            $cartCleared = false;
            $stmt = $pdo->prepare("SELECT s.category_id FROM user_cart c JOIN services s ON s.id=c.service_id WHERE c.user_id=? LIMIT 1");
            $stmt->execute([$userId]);
            $existing = $stmt->fetch();
            if ($existing) {
                $existingCatId = (int)($existing['category_id'] ?? 0);
                if ($existingCatId !== $newCatId) {
                    $stmt = $pdo->prepare("DELETE FROM user_cart WHERE user_id=?");
                    $stmt->execute([$userId]);
                    $cartCleared = true;
                }
            }

            $stmt = $pdo->prepare("INSERT INTO user_cart (user_id, service_id, quantity, time_slot, address_id, notes) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE quantity=quantity+?, time_slot=COALESCE(NULLIF(?,''),time_slot), address_id=COALESCE(?,address_id), notes=COALESCE(NULLIF(?,''),notes)");
            $stmt->execute([$userId, $serviceId, $quantity, $timeSlot, $addressId, $notes, $quantity, $timeSlot, $addressId, $notes]);
            $msg = $cartCleared ? 'Added to cart. Previous items from another category were removed.' : 'Added to cart';
            json_response(['success' => true, 'message' => $msg]);
        }

        // PUT /cart/{id} - update cart item
        if ($method === 'PUT' && count($parts) === 2) {
            $cartId = (int)$parts[1];
            $data = read_json();
            $quantity = (int)($data['quantity'] ?? 0);
            $timeSlot = trim($data['time_slot'] ?? '');
            $addressId = isset($data['address_id']) ? ((int)$data['address_id'] ?: null) : null;
            $notes = array_key_exists('notes', $data) ? trim($data['notes'] ?? '') : null;

            $stmt = $pdo->prepare("SELECT id FROM user_cart WHERE id=? AND user_id=?");
            $stmt->execute([$cartId, $userId]);
            if (!$stmt->fetch()) json_response(['success' => false, 'message' => 'Cart item not found'], 404);

            $updates = [];
            $params = [];
            if ($quantity > 0) { $updates[] = "quantity=?"; $params[] = $quantity; }
            if ($timeSlot !== '') { $updates[] = "time_slot=?"; $params[] = $timeSlot; }
            if ($addressId !== null) { $updates[] = "address_id=?"; $params[] = $addressId; }
            if ($notes !== null) { $updates[] = "notes=?"; $params[] = $notes; }
            if (empty($updates)) json_response(['success' => false, 'message' => 'Nothing to update'], 422);
            $params[] = $cartId;
            $params[] = $userId;
            $stmt = $pdo->prepare("UPDATE user_cart SET " . implode(',', $updates) . " WHERE id=? AND user_id=?");
            $stmt->execute($params);
            json_response(['success' => true, 'message' => 'Cart updated']);
        }

        // DELETE /cart/{id} - remove from cart
        if ($method === 'DELETE' && count($parts) === 2) {
            $cartId = (int)$parts[1];
            $stmt = $pdo->prepare("DELETE FROM user_cart WHERE id=? AND user_id=?");
            $stmt->execute([$cartId, $userId]);
            if ($stmt->rowCount() === 0) json_response(['success' => false, 'message' => 'Cart item not found'], 404);
            json_response(['success' => true, 'message' => 'Removed from cart']);
        }

        // DELETE /cart - clear cart
        if ($method === 'DELETE' && count($parts) === 1) {
            $stmt = $pdo->prepare("DELETE FROM user_cart WHERE user_id=?");
            $stmt->execute([$userId]);
            json_response(['success' => true, 'message' => 'Cart cleared']);
        }

        not_found();
    }

    // ==================== COUPONS (public) ====================
    // GET /coupons - list active coupons (for offers display)
    if (($parts[0] ?? '') === 'coupons' && $method === 'GET' && count($parts) === 1) {
        $stmt = $pdo->query("SELECT id, code, discount_type, discount_value, min_amount, max_uses, used_count, valid_from, valid_to FROM coupon_codes WHERE is_active=1 ORDER BY created_at DESC");
        $rows = $stmt->fetchAll();
        $now = date('Y-m-d H:i:s');
        $valid = [];
        foreach ($rows as $r) {
            $ok = true;
            if ($r['valid_from'] && $now < $r['valid_from']) $ok = false;
            if ($r['valid_to'] && $now > $r['valid_to']) $ok = false;
            if ($r['max_uses'] !== null && (int)$r['used_count'] >= (int)$r['max_uses']) $ok = false;
            if ($ok) $valid[] = $r;
        }
        json_response(['success' => true, 'coupons' => $valid]);
    }

    // POST /coupons/validate - validate coupon at checkout
    if (($parts[0] ?? '') === 'coupons' && ($parts[1] ?? '') === 'validate' && $method === 'POST') {
        $data = read_json();
        $amount = (int)($data['amount'] ?? 0);
        $code = strtoupper(trim($data['code'] ?? $data['coupon_code'] ?? ''));
        if ($amount <= 0 || $code === '') json_response(['success' => false, 'valid' => false, 'message' => 'Amount and coupon code required'], 422);

        $stmt = $pdo->prepare("SELECT * FROM coupon_codes WHERE code=? AND is_active=1");
        $stmt->execute([$code]);
        $coupon = $stmt->fetch();
        if (!$coupon) json_response(['success' => true, 'valid' => false, 'message' => 'Invalid or expired coupon']);

        $now = date('Y-m-d H:i:s');
        if ($coupon['valid_from'] && $now < $coupon['valid_from']) json_response(['success' => true, 'valid' => false, 'message' => 'Coupon not yet valid']);
        if ($coupon['valid_to'] && $now > $coupon['valid_to']) json_response(['success' => true, 'valid' => false, 'message' => 'Coupon expired']);
        if ($coupon['max_uses'] !== null && (int)$coupon['used_count'] >= (int)$coupon['max_uses']) json_response(['success' => true, 'valid' => false, 'message' => 'Coupon limit reached']);
        if ($amount < (int)$coupon['min_amount']) json_response(['success' => true, 'valid' => false, 'message' => 'Minimum amount ₹' . $coupon['min_amount'] . ' required']);

        $discount = 0;
        if (($coupon['discount_type'] ?? '') === 'fixed') {
            $discount = min((int)$coupon['discount_value'], $amount);
        } else {
            $discount = (int)round($amount * (int)$coupon['discount_value'] / 100);
        }
        $finalAmount = max(1, $amount - $discount);
        json_response(['success' => true, 'valid' => true, 'coupon_id' => (int)$coupon['id'], 'discount_amount' => $discount, 'original_amount' => $amount, 'final_amount' => $finalAmount, 'message' => 'Coupon applied! You save ₹' . $discount]);
    }

    // ==================== CHECKOUT ====================
    if (($parts[0] ?? '') === 'checkout') {
        $userId = (int)$user['id'];

        // POST /checkout - create order
        if ($method === 'POST' && count($parts) === 1) {
            $data = read_json();
            $addressId = (int)($data['address_id'] ?? 0);
            $paymentMode = trim($data['payment_mode'] ?? 'online');
            $couponId = (int)($data['coupon_id'] ?? 0) ?: null;
            $notes = trim($data['notes'] ?? '');
            $scheduleDate = trim($data['date'] ?? '');
            $scheduleTimeSlot = trim($data['time_slot'] ?? '');

            if ($addressId <= 0) json_response(['success' => false, 'message' => 'address_id required'], 422);
            if (!in_array($paymentMode, ['online', 'cod'])) $paymentMode = 'online';

            $stmt = $pdo->prepare("SELECT id FROM user_addresses WHERE id=? AND user_id=?");
            $stmt->execute([$addressId, $userId]);
            if (!$stmt->fetch()) json_response(['success' => false, 'message' => 'Address not found'], 404);

            $stmt = $pdo->prepare("
                SELECT c.id, c.service_id, c.quantity, c.time_slot, c.address_id, c.notes, s.title, s.price, s.category_id
                FROM user_cart c
                JOIN services s ON s.id=c.service_id AND s.is_active=1
                WHERE c.user_id=?
            ");
            $stmt->execute([$userId]);
            $cartItems = $stmt->fetchAll();
            if (empty($cartItems)) json_response(['success' => false, 'message' => 'Cart is empty'], 422);

            // Ensure all cart items are from same category
            $catIds = array_values(array_unique(array_map(fn($i) => (int)($i['category_id'] ?? 0), $cartItems)));
            $firstCatId = (int)($catIds[0] ?? 0);
            if (count($catIds) > 1 || $firstCatId <= 0) {
                json_response(['success' => false, 'message' => 'Cart must contain services from a single category. Please remove items from other categories.'], 422);
            }

            $subtotal = 0;
            foreach ($cartItems as $item) $subtotal += (int)$item['price'] * (int)$item['quantity'];

            $discountAmount = 0;
            if ($couponId) {
                $stmt = $pdo->prepare("SELECT * FROM coupon_codes WHERE id=? AND is_active=1");
                $stmt->execute([$couponId]);
                $coupon = $stmt->fetch();
                if ($coupon) {
                    $now = date('Y-m-d H:i:s');
                    $valid = $subtotal >= (int)$coupon['min_amount'];
                    if ($coupon['valid_from'] && $now < $coupon['valid_from']) $valid = false;
                    if ($coupon['valid_to'] && $now > $coupon['valid_to']) $valid = false;
                    if ($coupon['max_uses'] !== null && (int)$coupon['used_count'] >= (int)$coupon['max_uses']) $valid = false;
                    if ($valid) {
                        if (($coupon['discount_type'] ?? '') === 'fixed') {
                            $discountAmount = min((int)$coupon['discount_value'], $subtotal);
                        } else {
                            $discountAmount = (int)round($subtotal * (int)$coupon['discount_value'] / 100);
                        }
                    }
                }
            }
            $totalAmount = max(1, $subtotal - $discountAmount);

            $orderNumber = 'ORD' . date('Ymd') . strtoupper(substr(uniqid(), -6));
            $razorpayOrderId = null;
            if ($paymentMode === 'online' && $razorpayKey && $razorpaySecret) {
                $amountPaise = $totalAmount * 100;
                $receipt = 'ord_' . $userId . '_' . time();
                $ch = curl_init('https://api.razorpay.com/v1/orders');
                curl_setopt_array($ch, [
                    CURLOPT_RETURNTRANSFER => true,
                    CURLOPT_POST => true,
                    CURLOPT_POSTFIELDS => json_encode(['amount' => $amountPaise, 'currency' => 'INR', 'receipt' => $receipt]),
                    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Basic ' . base64_encode($razorpayKey . ':' . $razorpaySecret)]
                ]);
                $res = curl_exec($ch);
                $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                curl_close($ch);
                $rpOrder = json_decode($res, true);
                if ($code === 200 && !empty($rpOrder['id'])) $razorpayOrderId = $rpOrder['id'];
            }

            // Lead-centric: create leads directly from cart (no user_orders)
            $stmt = $pdo->prepare("SELECT name, phone FROM users WHERE id=?");
            $stmt->execute([$userId]);
            $u = $stmt->fetch();
            $stmt = $pdo->prepare("SELECT address_line1, address_line2, city, state, pincode FROM user_addresses WHERE id=? AND user_id=?");
            $stmt->execute([$addressId, $userId]);
            $a = $stmt->fetch();
            $location = trim(implode(', ', array_filter([$a['address_line1'] ?? '', $a['address_line2'] ?? '', $a['city'] ?? '', $a['state'] ?? '', $a['pincode'] ?? ''])));
            $custName = $u['name'] ?? 'Customer';
            $custPhone = $u['phone'] ?? '';
            $scheduleStr = trim($scheduleDate !== '' || $scheduleTimeSlot !== '' ? trim($scheduleDate . ' | ' . $scheduleTimeSlot, ' |') : '');

            $insertedLeadId = null;
            $insertedLeadCatId = (int)($cartItems[0]['category_id'] ?? $firstCatId ?? 0);
            $lastLeadId = null;
            $pdo->beginTransaction();
            $lastLeadId = null;
            $leadCatIdForWebhook = null;
            $leadTitleForWebhook = null;
            $leadPriceForWebhook = null;
            try {
                // Check if leads has order_number and payment columns (migration run)
                $hasOrderNumberCol = false;
                $hasPaymentCols = false;
                $hasServiceIdCol = false;
                try {
                    $r = $pdo->query("SHOW COLUMNS FROM leads LIKE 'order_number'");
                    $hasOrderNumberCol = (bool)$r->fetch();
                    $r = $pdo->query("SHOW COLUMNS FROM leads LIKE 'user_id'");
                    $hasPaymentCols = (bool)$r->fetch();
                    $r = $pdo->query("SHOW COLUMNS FROM leads LIKE 'service_id'");
                    $hasServiceIdCol = (bool)$r->fetch();
                } catch (Exception $e) {}

                // Single lead per order: aggregate all cart items into one lead with full details
                $catId = (int)($cartItems[0]['category_id'] ?? 0);
                if ($catId <= 0 && $firstCatId > 0) $catId = $firstCatId; // ensure we always have valid category_id for vendor notifications
                $firstItem = $cartItems[0];
                $timeSlot = trim($firstItem['time_slot'] ?? '');
                if ($timeSlot === '' && $scheduleStr !== '') $timeSlot = $scheduleStr;
                if ($timeSlot === '') $timeSlot = 'Flexible';

                // Get category name for title
                $categoryName = 'Services';
                if ($catId) {
                    $stmt = $pdo->prepare("SELECT name FROM categories WHERE id=? LIMIT 1");
                    $stmt->execute([$catId]);
                    $catRow = $stmt->fetch();
                    if ($catRow) $categoryName = trim($catRow['name'] ?? '');
                }

                // Build aggregated service_details: "Service1 × qty @ ₹price = ₹line; Service2 × qty @ ₹price = ₹line; Subtotal: ₹X"
                $svcLines = [];
                foreach ($cartItems as $item) {
                    $linePrice = (int)$item['price'] * max(1, (int)$item['quantity']);
                    $svcLines[] = $item['title'] . ' × ' . max(1, (int)$item['quantity']) . ' @ ₹' . (int)$item['price'] . ' = ₹' . $linePrice;
                }
                $svcDetails = implode("\n• ", $svcLines);
                if (count($cartItems) > 1) $svcDetails = "• " . $svcDetails . "\nSubtotal: ₹" . $subtotal;
                else $svcDetails = "• " . $svcDetails;

                // Title: category name + item count if multiple services
                $leadTitle = count($cartItems) === 1 ? $firstItem['title'] : ($categoryName . ' (' . count($cartItems) . ' services)' );

                $leadPrice = $subtotal;
                $firstServiceId = (int)($firstItem['service_id'] ?? 0) ?: null;

                if ($hasOrderNumberCol && $hasPaymentCols && $hasServiceIdCol) {
                    $stmt = $pdo->prepare("INSERT INTO leads (title, customer_name, customer_phone, service_details, time_slot, location, lead_price, is_available, payment_mode, category_id, order_number, user_id, address_id, subtotal, discount_amount, total_amount, payment_status, razorpay_order_id, coupon_id, service_id, quantity) VALUES (?,?,?,?,?,?,?,0,?,?,?,?,?,?,?,?,?,?,?,?,1)");
                    $stmt->execute([$leadTitle, $custName, $custPhone, $svcDetails, $timeSlot, $location, $leadPrice, $paymentMode, $catId, $orderNumber, $userId, $addressId, $subtotal, $discountAmount, $totalAmount, 'pending', $razorpayOrderId, $couponId, $firstServiceId]);
                } elseif ($hasOrderNumberCol && $hasPaymentCols) {
                    $stmt = $pdo->prepare("INSERT INTO leads (title, customer_name, customer_phone, service_details, time_slot, location, lead_price, is_available, payment_mode, category_id, order_number, user_id, address_id, subtotal, discount_amount, total_amount, payment_status, razorpay_order_id, coupon_id) VALUES (?,?,?,?,?,?,?,0,?,?,?,?,?,?,?,?,?,?,?,?)");
                    $stmt->execute([$leadTitle, $custName, $custPhone, $svcDetails, $timeSlot, $location, $leadPrice, $paymentMode, $catId, $orderNumber, $userId, $addressId, $subtotal, $discountAmount, $totalAmount, 'pending', $razorpayOrderId, $couponId]);
                } else {
                    $stmt = $pdo->prepare("INSERT INTO leads (title, customer_name, customer_phone, service_details, time_slot, location, lead_price, is_available, payment_mode, category_id) VALUES (?,?,?,?,?,?,?,0,?,?)");
                    $stmt->execute([$leadTitle, $custName, $custPhone, $svcDetails, $timeSlot, $location, $leadPrice, $paymentMode, $catId]);
                }
                $lastLeadId = (int)$pdo->lastInsertId();
                $leadCatIdForWebhook = $catId;
                $leadTitleForWebhook = $leadTitle;
                $leadPriceForWebhook = $leadPrice;

                $stmt = $pdo->prepare("DELETE FROM user_cart WHERE user_id=?");
                $stmt->execute([$userId]);
                $pdo->commit();
            } catch (Exception $e) {
                $pdo->rollBack();
                json_response(['success' => false, 'message' => 'Order failed: ' . $e->getMessage()], 500);
            }

            // For COD: send vendor notification immediately by category_id (vendors in that category get notified)
            if ($paymentMode === 'cod') {
                $webhookBase = $cfg['VENDOR_WEBHOOK_URL'] ?? $cfg['VENDOR_API_BASE_URL'] ?? getenv('VENDOR_WEBHOOK_URL') ?: getenv('VENDOR_API_BASE_URL') ?: '';
                if ($webhookBase) {
                    $webhookSent = false;
                    if ($hasOrderNumberCol && $hasPaymentCols) {
                        $leadCols = "id, category_id, title, lead_price";
                        if (!empty($hasServiceIdCol)) $leadCols .= ", service_id";
                        $stmt = $pdo->prepare("SELECT $leadCols FROM leads WHERE order_number=? AND user_id=?");
                        $stmt->execute([$orderNumber, $userId]);
                        foreach ($stmt->fetchAll() as $l) {
                            $leadCatId = (int)($l['category_id'] ?? 0);
                            if ($leadCatId <= 0 && !empty($hasServiceIdCol) && !empty($l['service_id'])) {
                                $stmt2 = $pdo->prepare("SELECT category_id FROM services WHERE id=? LIMIT 1");
                                $stmt2->execute([$l['service_id']]);
                                $svc = $stmt2->fetch();
                                $leadCatId = (int)($svc['category_id'] ?? 0);
                            }
                            if ($leadCatId > 0) {
                                $url = rtrim($webhookBase, '/');
                                if (strpos($url, '/webhooks/') === false) $url .= '/webhooks/new-lead';
                                $ch = curl_init($url);
                                curl_setopt_array($ch, [
                                    CURLOPT_RETURNTRANSFER => true,
                                    CURLOPT_POST => true,
                                    CURLOPT_POSTFIELDS => json_encode(['category_id' => $leadCatId, 'lead_id' => (int)$l['id'], 'title' => $l['title'] ?? $leadTitle, 'lead_price' => (int)($l['lead_price'] ?? $subtotal)]),
                                    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
                                    CURLOPT_TIMEOUT => 5
                                ]);
                                @curl_exec($ch);
                                curl_close($ch);
                                $webhookSent = true;
                            }
                        }
                    }
                    // Fallback: when leads table has old schema (no order_number/user_id) or SELECT returned no rows
                    if (!$webhookSent && $lastLeadId > 0 && $leadCatIdForWebhook > 0) {
                        $url = rtrim($webhookBase, '/');
                        if (strpos($url, '/webhooks/') === false) $url .= '/webhooks/new-lead';
                        $ch = curl_init($url);
                        curl_setopt_array($ch, [
                            CURLOPT_RETURNTRANSFER => true,
                            CURLOPT_POST => true,
                            CURLOPT_POSTFIELDS => json_encode(['category_id' => $leadCatIdForWebhook, 'lead_id' => $lastLeadId, 'title' => $leadTitleForWebhook ?? $leadTitle, 'lead_price' => (int)($leadPriceForWebhook ?? $subtotal)]),
                            CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
                            CURLOPT_TIMEOUT => 5
                        ]);
                        @curl_exec($ch);
                        curl_close($ch);
                    }
                }
            }

            $out = ['success' => true, 'message' => 'Order created', 'order_number' => $orderNumber, 'total_amount' => $totalAmount, 'payment_mode' => $paymentMode];
            if ($paymentMode === 'online' && $razorpayOrderId) {
                $out['razorpay_order_id'] = $razorpayOrderId;
                $out['key_id'] = $razorpayKey;
                $out['amount'] = $totalAmount;
            }
            if ($paymentMode === 'cod') {
                $out['message'] = 'Order placed. Pay on delivery.';
            }
            json_response($out);
        }

        not_found();
    }

    // ==================== PAYMENT ====================
    if (($parts[0] ?? '') === 'payment') {
        $userId = (int)$user['id'];
        $action = $parts[1] ?? '';

        // POST /payment/verify - verify Razorpay payment (lead-centric: find by razorpay_order_id OR order_number+user_id)
        if ($method === 'POST' && $action === 'verify') {
            $data = read_json();
            $orderNumber = trim($data['order_number'] ?? $data['order_id'] ?? '');
            $rpOrderId = trim($data['razorpay_order_id'] ?? '');
            $paymentId = trim($data['razorpay_payment_id'] ?? $data['payment_id'] ?? '');
            $signature = trim($data['razorpay_signature'] ?? $data['signature'] ?? '');
            if (!$paymentId || !$signature) json_response(['success' => false, 'message' => 'razorpay_payment_id and razorpay_signature required'], 422);

            try {
                $r = $pdo->query("SHOW COLUMNS FROM leads LIKE 'razorpay_order_id'");
                $hasRpCol = (bool)$r->fetch();
            } catch (Exception $e) { $hasRpCol = false; }
            if (!$hasRpCol) json_response(['success' => false, 'message' => 'Leads table not migrated'], 500);

            // Find leads: by razorpay_order_id first, or by order_number+user_id (prefer order_number)
            $leads = [];
            $orderNum = null;
            if ($rpOrderId) {
                $stmt = $pdo->prepare("SELECT * FROM leads WHERE razorpay_order_id=? AND user_id=? ORDER BY id");
                $stmt->execute([$rpOrderId, $userId]);
                $leads = $stmt->fetchAll();
                $orderNum = $leads[0]['order_number'] ?? null;
            }
            if (empty($leads) && $orderNumber !== '') {
                $stmt = $pdo->prepare("SELECT * FROM leads WHERE order_number=? AND user_id=? ORDER BY id");
                $stmt->execute([$orderNumber, $userId]);
                $leads = $stmt->fetchAll();
                $orderNum = $orderNumber;
                if (!empty($leads)) $rpOrderId = trim($leads[0]['razorpay_order_id'] ?? '');
            }
            if (empty($leads)) json_response(['success' => false, 'message' => 'Order not found'], 404);

            $lead = $leads[0];
            if (($lead['payment_status'] ?? '') === 'paid') {
                $orderData = build_order_from_leads($leads);
                json_response(['success' => true, 'message' => 'Already paid', 'order' => $orderData]);
            }

            if (!$rpOrderId) json_response(['success' => false, 'message' => 'razorpay_order_id required for verification'], 422);
            $expectedSig = hash_hmac('sha256', $rpOrderId . '|' . $paymentId, $razorpaySecret);
            if (!hash_equals($expectedSig, $signature)) json_response(['success' => false, 'message' => 'Payment verification failed'], 400);

            $pdo->beginTransaction();
            $stmt = $pdo->prepare("UPDATE leads SET payment_status='paid', razorpay_payment_id=?, is_available=1 WHERE order_number=? AND user_id=?");
            $stmt->execute([$paymentId, $orderNum, $userId]);
            if (!empty($lead['coupon_id'])) {
                $stmt = $pdo->prepare("UPDATE coupon_codes SET used_count = used_count + 1 WHERE id=?");
                $stmt->execute([$lead['coupon_id']]);
            }
            $pdo->commit();

            $webhookBase = $cfg['VENDOR_WEBHOOK_URL'] ?? $cfg['VENDOR_API_BASE_URL'] ?? getenv('VENDOR_WEBHOOK_URL') ?: getenv('VENDOR_API_BASE_URL') ?: '';
            if ($webhookBase) {
                $leadCols = "id, category_id, title, lead_price";
                try { $r = $pdo->query("SHOW COLUMNS FROM leads LIKE 'service_id'"); $hasSvc = (bool)$r->fetch(); } catch (Exception $e) { $hasSvc = false; }
                if ($hasSvc) $leadCols .= ", service_id";
                $stmt = $pdo->prepare("SELECT $leadCols FROM leads WHERE order_number=? AND user_id=?");
                $stmt->execute([$orderNum, $userId]);
                foreach ($stmt->fetchAll() as $l) {
                    $catId = (int)($l['category_id'] ?? 0);
                    if ($catId <= 0 && !empty($l['service_id'])) {
                        $stmt2 = $pdo->prepare("SELECT category_id FROM services WHERE id=? LIMIT 1");
                        $stmt2->execute([$l['service_id']]);
                        $svc = $stmt2->fetch();
                        $catId = (int)($svc['category_id'] ?? 0);
                    }
                    if ($catId > 0) {
                        $url = rtrim($webhookBase, '/');
                        if (strpos($url, '/webhooks/') === false) $url .= '/webhooks/new-lead';
                        $ch = curl_init($url);
                        curl_setopt_array($ch, [
                            CURLOPT_RETURNTRANSFER => true,
                            CURLOPT_POST => true,
                            CURLOPT_POSTFIELDS => json_encode(['category_id' => $catId, 'lead_id' => (int)$l['id'], 'title' => $l['title'], 'lead_price' => (int)$l['lead_price']]),
                            CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
                            CURLOPT_TIMEOUT => 5
                        ]);
                        @curl_exec($ch);
                        curl_close($ch);
                    }
                }
            }

            $stmt = $pdo->prepare("SELECT * FROM leads WHERE order_number=? AND user_id=? ORDER BY id");
            $stmt->execute([$orderNum, $userId]);
            $updatedLeads = $stmt->fetchAll();
            $orderData = build_order_from_leads($updatedLeads);
            $orderData = enrich_leads_with_vendor($pdo, [$orderData])[0] ?? $orderData;
            json_response(['success' => true, 'message' => 'Payment verified', 'order' => $orderData]);
        }

        // POST /payment/cod-confirm - confirm COD order (lead-centric: order_number required)
        if ($method === 'POST' && $action === 'cod-confirm') {
            $data = read_json();
            $orderNumber = trim($data['order_number'] ?? $data['order_id'] ?? '');
            if ($orderNumber === '') json_response(['success' => false, 'message' => 'order_number required'], 422);

            $stmt = $pdo->prepare("SELECT * FROM leads WHERE order_number=? AND user_id=? ORDER BY id");
            $stmt->execute([$orderNumber, $userId]);
            $leads = $stmt->fetchAll();
            if (empty($leads)) json_response(['success' => false, 'message' => 'Order not found'], 404);

            $lead = $leads[0];
            if (trim($lead['payment_mode'] ?? '') !== 'cod') json_response(['success' => false, 'message' => 'Not a COD order'], 400);
            if (($lead['payment_status'] ?? '') === 'paid') {
                $orderData = build_order_from_leads($leads);
                $orderData = enrich_leads_with_vendor($pdo, [$orderData])[0] ?? $orderData;
                json_response(['success' => true, 'message' => 'Already confirmed', 'order' => $orderData]);
            }

            $pdo->beginTransaction();
            $stmt = $pdo->prepare("UPDATE leads SET payment_status='paid', is_available=1 WHERE order_number=? AND user_id=?");
            $stmt->execute([$orderNumber, $userId]);
            if (!empty($lead['coupon_id'])) {
                $stmt = $pdo->prepare("UPDATE coupon_codes SET used_count = used_count + 1 WHERE id=?");
                $stmt->execute([$lead['coupon_id']]);
            }
            $pdo->commit();

            $webhookBase = $cfg['VENDOR_WEBHOOK_URL'] ?? $cfg['VENDOR_API_BASE_URL'] ?? getenv('VENDOR_WEBHOOK_URL') ?: getenv('VENDOR_API_BASE_URL') ?: '';
            if ($webhookBase) {
                $leadCols = "id, category_id, title, lead_price";
                try { $r = $pdo->query("SHOW COLUMNS FROM leads LIKE 'service_id'"); $hasSvc = (bool)$r->fetch(); } catch (Exception $e) { $hasSvc = false; }
                if ($hasSvc) $leadCols .= ", service_id";
                $stmt = $pdo->prepare("SELECT $leadCols FROM leads WHERE order_number=? AND user_id=?");
                $stmt->execute([$orderNumber, $userId]);
                foreach ($stmt->fetchAll() as $l) {
                    $catId = (int)($l['category_id'] ?? 0);
                    if ($catId <= 0 && !empty($l['service_id'])) {
                        $stmt2 = $pdo->prepare("SELECT category_id FROM services WHERE id=? LIMIT 1");
                        $stmt2->execute([$l['service_id']]);
                        $svc = $stmt2->fetch();
                        $catId = (int)($svc['category_id'] ?? 0);
                    }
                    if ($catId > 0) {
                        $url = rtrim($webhookBase, '/');
                        if (strpos($url, '/webhooks/') === false) $url .= '/webhooks/new-lead';
                        $ch = curl_init($url);
                        curl_setopt_array($ch, [
                            CURLOPT_RETURNTRANSFER => true,
                            CURLOPT_POST => true,
                            CURLOPT_POSTFIELDS => json_encode(['category_id' => $catId, 'lead_id' => (int)$l['id'], 'title' => $l['title'], 'lead_price' => (int)$l['lead_price']]),
                            CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
                            CURLOPT_TIMEOUT => 5
                        ]);
                        @curl_exec($ch);
                        curl_close($ch);
                    }
                }
            }

            $stmt = $pdo->prepare("SELECT * FROM leads WHERE order_number=? AND user_id=? ORDER BY id");
            $stmt->execute([$orderNumber, $userId]);
            $updatedLeads = $stmt->fetchAll();
            $orderData = build_order_from_leads($updatedLeads);
            $orderData = enrich_leads_with_vendor($pdo, [$orderData])[0] ?? $orderData;
            json_response(['success' => true, 'message' => 'Order confirmed. Pay on delivery.', 'order_number' => $orderNumber, 'order' => $orderData]);
        }

        not_found();
    }

    // ==================== MY BOOKINGS / ORDERS ====================
    if (($parts[0] ?? '') === 'orders') {
        $userId = (int)$user['id'];

        // GET /orders - list my orders (lead-centric: group leads by order_number)
        if ($method === 'GET' && count($parts) === 1) {
            $stmt = $pdo->prepare("SELECT * FROM leads WHERE user_id=? ORDER BY created_at DESC");
            $stmt->execute([$userId]);
            $allLeads = $stmt->fetchAll();
            $byOrderNumber = [];
            foreach ($allLeads as $l) {
                $on = trim($l['order_number'] ?? '');
                if ($on === '') continue;
                if (!isset($byOrderNumber[$on])) $byOrderNumber[$on] = [];
                $byOrderNumber[$on][] = $l;
            }
            $orders = [];
            foreach ($byOrderNumber as $orderNum => $leads) {
                $ord = build_order_from_leads($leads);
                if ($ord) $orders[] = $ord;
            }
            usort($orders, function ($a, $b) {
                $tA = $a['created_at'] ?? '';
                $tB = $b['created_at'] ?? '';
                return strcmp($tB, $tA);
            });
            $orders = array_slice($orders, 0, 100);
            if (!empty($orders)) $orders = enrich_leads_with_vendor($pdo, $orders);
            json_response(['success' => true, 'orders' => $orders]);
        }

        // GET /orders/{id} - single order detail (id = order_number string)
        if ($method === 'GET' && count($parts) === 2) {
            $orderNumber = trim($parts[1]);
            if ($orderNumber === '') json_response(['success' => false, 'message' => 'Order not found'], 404);

            $stmt = $pdo->prepare("SELECT * FROM leads WHERE order_number=? AND user_id=? ORDER BY id");
            $stmt->execute([$orderNumber, $userId]);
            $leads = $stmt->fetchAll();
            if (empty($leads)) json_response(['success' => false, 'message' => 'Order not found'], 404);

            $order = build_order_from_leads($leads);
            $order = enrich_leads_with_vendor($pdo, [$order])[0] ?? $order;
            json_response(['success' => true, 'order' => $order]);
        }

        // PUT /orders/{id}/cancel - cancel order (id = order_number, updates leads.order_status)
        if ($method === 'PUT' && count($parts) === 3 && ($parts[2] ?? '') === 'cancel') {
            $orderNumber = trim($parts[1]);
            if ($orderNumber === '') json_response(['success' => false, 'message' => 'Order not found'], 404);

            $stmt = $pdo->prepare("SELECT id FROM leads WHERE order_number=? AND user_id=? LIMIT 1");
            $stmt->execute([$orderNumber, $userId]);
            if (!$stmt->fetch()) json_response(['success' => false, 'message' => 'Order not found'], 404);

            $hasOrderStatus = false;
            try {
                $r = $pdo->query("SHOW COLUMNS FROM leads LIKE 'order_status'");
                $hasOrderStatus = (bool)$r->fetch();
            } catch (Exception $e) {}
            if ($hasOrderStatus) {
                $stmt = $pdo->prepare("UPDATE leads SET order_status='cancelled' WHERE order_number=? AND user_id=?");
                $stmt->execute([$orderNumber, $userId]);
            }
            json_response(['success' => true, 'message' => 'Order cancelled']);
        }

        // PUT /orders/{id}/reschedule - reschedule booking (update leads.time_slot)
        if ($method === 'PUT' && count($parts) === 3 && ($parts[2] ?? '') === 'reschedule') {
            $orderNumber = trim($parts[1]);
            if ($orderNumber === '') json_response(['success' => false, 'message' => 'Order not found'], 404);

            $stmt = $pdo->prepare("SELECT id FROM leads WHERE order_number=? AND user_id=? LIMIT 1");
            $stmt->execute([$orderNumber, $userId]);
            if (!$stmt->fetch()) json_response(['success' => false, 'message' => 'Order not found'], 404);

            $data = read_json();
            $date = trim($data['date'] ?? '');
            $timeSlot = trim($data['time_slot'] ?? '');
            if ($date === '' || $timeSlot === '') {
                json_response(['success' => false, 'message' => 'date and time_slot are required'], 422);
            }

            $newTimeSlot = $date . ' | ' . $timeSlot;
            $stmt = $pdo->prepare("UPDATE leads SET time_slot=? WHERE order_number=? AND user_id=?");
            $stmt->execute([$newTimeSlot, $orderNumber, $userId]);

            json_response(['success' => true, 'message' => 'Booking rescheduled']);
        }

        not_found();
    }

    // ==================== ADDRESSES ====================
    if (($parts[0] ?? '') === 'addresses') {
        $userId = (int)$user['id'];

        // GET /addresses
        if ($method === 'GET' && count($parts) === 1) {
            $stmt = $pdo->prepare("SELECT * FROM user_addresses WHERE user_id=? ORDER BY is_default DESC, created_at DESC");
            $stmt->execute([$userId]);
            json_response(['success' => true, 'addresses' => $stmt->fetchAll()]);
        }

        // POST /addresses - add address
        if ($method === 'POST' && count($parts) === 1) {
            $data = read_json();
            $label = trim($data['label'] ?? 'Home');
            $line1 = trim($data['address_line1'] ?? $data['address_line1'] ?? '');
            $line2 = trim($data['address_line2'] ?? $data['address_line2'] ?? '');
            $city = trim($data['city'] ?? '');
            $state = trim($data['state'] ?? '');
            $pincode = trim($data['pincode'] ?? '');
            $phone = trim($data['phone'] ?? $user['phone'] ?? '');
            $isDefault = !empty($data['is_default']) ? 1 : 0;
            if ($line1 === '' || $city === '' || $pincode === '') json_response(['success' => false, 'message' => 'address_line1, city, pincode required'], 422);

            if ($isDefault) {
                $pdo->prepare("UPDATE user_addresses SET is_default=0 WHERE user_id=?")->execute([$userId]);
            }
            $stmt = $pdo->prepare("INSERT INTO user_addresses (user_id, label, address_line1, address_line2, city, state, pincode, phone, is_default) VALUES (?,?,?,?,?,?,?,?,?)");
            $stmt->execute([$userId, $label ?: 'Home', $line1, $line2, $city, $state, $pincode, $phone, $isDefault]);
            $id = (int)$pdo->lastInsertId();
            json_response(['success' => true, 'message' => 'Address added', 'id' => $id]);
        }

        // PUT /addresses/{id} - edit address
        if ($method === 'PUT' && count($parts) === 2) {
            $addrId = (int)$parts[1];
            $data = read_json();
            $stmt = $pdo->prepare("SELECT * FROM user_addresses WHERE id=? AND user_id=?");
            $stmt->execute([$addrId, $userId]);
            $addr = $stmt->fetch();
            if (!$addr) json_response(['success' => false, 'message' => 'Address not found'], 404);

            $label = array_key_exists('label', $data) ? trim($data['label'] ?? '') : $addr['label'];
            $line1 = array_key_exists('address_line1', $data) ? trim($data['address_line1'] ?? '') : $addr['address_line1'];
            $line2 = array_key_exists('address_line2', $data) ? trim($data['address_line2'] ?? '') : ($addr['address_line2'] ?? '');
            $city = array_key_exists('city', $data) ? trim($data['city'] ?? '') : $addr['city'];
            $state = array_key_exists('state', $data) ? trim($data['state'] ?? '') : ($addr['state'] ?? '');
            $pincode = array_key_exists('pincode', $data) ? trim($data['pincode'] ?? '') : $addr['pincode'];
            $phone = array_key_exists('phone', $data) ? trim($data['phone'] ?? '') : ($addr['phone'] ?? '');
            $isDefault = array_key_exists('is_default', $data) ? (int)!!$data['is_default'] : (int)$addr['is_default'];

            if ($line1 === '' || $city === '' || $pincode === '') json_response(['success' => false, 'message' => 'address_line1, city, pincode required'], 422);

            if ($isDefault) {
                $pdo->prepare("UPDATE user_addresses SET is_default=0 WHERE user_id=?")->execute([$userId]);
            }
            $stmt = $pdo->prepare("UPDATE user_addresses SET label=?, address_line1=?, address_line2=?, city=?, state=?, pincode=?, phone=?, is_default=? WHERE id=? AND user_id=?");
            $stmt->execute([$label ?: 'Home', $line1, $line2, $city, $state, $pincode, $phone, $isDefault, $addrId, $userId]);
            json_response(['success' => true, 'message' => 'Address updated']);
        }

        // DELETE /addresses/{id}
        if ($method === 'DELETE' && count($parts) === 2) {
            $addrId = (int)$parts[1];
            $stmt = $pdo->prepare("DELETE FROM user_addresses WHERE id=? AND user_id=?");
            $stmt->execute([$addrId, $userId]);
            if ($stmt->rowCount() === 0) json_response(['success' => false, 'message' => 'Address not found'], 404);
            json_response(['success' => true, 'message' => 'Address deleted']);
        }

        not_found();
    }

    // ==================== PROFILE ====================
    if (($parts[0] ?? '') === 'profile') {
        $userId = (int)$user['id'];

        // GET /profile
        if ($method === 'GET' && count($parts) === 1) {
            $stmt = $pdo->prepare("SELECT id, name, email, phone, created_at, updated_at FROM users WHERE id=?");
            $stmt->execute([$userId]);
            json_response(['success' => true, 'user' => $stmt->fetch()]);
        }

        // PUT /profile - update profile
        if ($method === 'PUT' && count($parts) === 1) {
            $data = read_json();
            $stmt = $pdo->prepare("SELECT * FROM users WHERE id=?");
            $stmt->execute([$userId]);
            $u = $stmt->fetch();

            $name = array_key_exists('name', $data) ? trim($data['name'] ?? '') : $u['name'];
            $email = array_key_exists('email', $data) ? trim($data['email'] ?? '') : $u['email'];
            $phone = array_key_exists('phone', $data) ? preg_replace('/\D+/', '', trim($data['phone'] ?? '')) : ($u['phone'] ?? '');
            if ($name === '') json_response(['success' => false, 'message' => 'name required'], 422);
            if (strlen($phone) < 10 && $phone !== '') json_response(['success' => false, 'message' => 'Phone must be 10 digits'], 422);

            $stmt = $pdo->prepare("SELECT id FROM users WHERE email=? AND id<>?");
            $stmt->execute([$email, $userId]);
            if ($stmt->fetch()) json_response(['success' => false, 'message' => 'Email already used'], 409);
            if ($phone !== '') {
                $stmt = $pdo->prepare("SELECT id FROM users WHERE phone=? AND id<>?");
                $stmt->execute([$phone, $userId]);
                if ($stmt->fetch()) json_response(['success' => false, 'message' => 'Phone already used'], 409);
            }

            $stmt = $pdo->prepare("UPDATE users SET name=?, email=?, phone=? WHERE id=?");
            $stmt->execute([$name, $email, $phone ?: $u['phone'], $userId]);
            $stmt = $pdo->prepare("SELECT id, name, email, phone, created_at, updated_at FROM users WHERE id=?");
            $stmt->execute([$userId]);
            json_response(['success' => true, 'message' => 'Profile updated', 'user' => $stmt->fetch()]);
        }

        not_found();
    }

    not_found();

} catch (Exception $e) {
    json_response(['success' => false, 'message' => $e->getMessage()], 500);
}
