<!-- Vendor Api Endpoint: https://utile.filemakrxpert.com/public/api/index.php    -->



<?php
require_once __DIR__ . '/../../config/helpers.php';


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


// CORS (adjust origin as needed)
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");

// Handle preflight
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
if ($method === 'OPTIONS') {
    http_response_code(204);
    exit;
}


// Remove prefix up to /api
$pos = strpos($path, '/api/');
$route = $pos !== false ? substr($path, $pos + 4) : $path;
$route = '/' . ltrim($route, '/');
$parts = array_values(array_filter(explode('/', trim($route, '/'))));

// json_response([
//   'method' => $method,
//   'path' => $path,
//   'route' => $route,
//   'parts' => $parts
// ], 200);





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

/**
 * Send push notification to vendors in category for new lead.
 * @param PDO $pdo
 * @param int $categoryId
 * @param array $data Optional: title, body, lead_price
 * @return int Number of notifications delivered
 */
function send_new_lead_notification($pdo, $categoryId, $data = []) {
    $title = trim($data['title'] ?? $data['service'] ?? 'New Lead');
    $body = trim($data['body'] ?? $data['message'] ?? 'A new lead is available in your category!');
    $leadPrice = (int)($data['lead_price'] ?? $data['price'] ?? 0);
    if ($leadPrice > 0) $body = '₹' . $leadPrice . ' - ' . $body;
    if ($categoryId <= 0) return 0;
    $stmt = $pdo->prepare("SELECT expo_push_token FROM vendors WHERE category_id=? AND expo_push_token IS NOT NULL AND expo_push_token != '' AND is_active=1 AND kyc_status='approved'");
    $stmt->execute([$categoryId]);
    $tokens = array_filter(array_column($stmt->fetchAll(), 'expo_push_token'));
    if (empty($tokens)) return 0;
    $messages = [];
    foreach ($tokens as $t) {
        $messages[] = ['to'=>$t,'title'=>'🔔 New Lead!','body'=>$body,'sound'=>'default','priority'=>'high','data'=>['type'=>'new_lead','category_id'=>$categoryId]];
    }
    $ch = curl_init('https://exp.host/--/api/v2/push/send');
    curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true,CURLOPT_POST=>true,CURLOPT_POSTFIELDS=>json_encode($messages),CURLOPT_HTTPHEADER=>['Content-Type: application/json','Accept: application/json']]);
    $res = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    $sent = 0;
    if ($code === 200) {
        $resp = json_decode($res, true);
        if (isset($resp['data']) && is_array($resp['data'])) {
            foreach ($resp['data'] as $t) { if (($t['status']??'')==='ok') $sent++; }
        }
    }
    return $sent;
}

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

try {
    
    
        if (($parts[0] ?? '') === 'categories') {
        // $vendor = require_vendor_auth(); // if you want public, remove this line
        // If you want admin-only, replace with require_admin_auth()

        // GET /api/categories
        if ($method === 'GET' && count($parts) === 1) {
            $onlyActive = ($_GET['only_active'] ?? '1') === '1' ? 1 : 0;

            if ($onlyActive) {
                $stmt = $pdo->query("SELECT id, name, thumbnail, is_active, created_at FROM categories WHERE is_active=1 ORDER BY name ASC");
            } else {
                $stmt = $pdo->query("SELECT id, name, thumbnail, is_active, created_at FROM categories ORDER BY created_at DESC");
            }

            json_response(['success'=>true, 'categories'=>$stmt->fetchAll()]);
        }

        // GET /api/categories/{id}
        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=? LIMIT 1");
            $stmt->execute([$catId]);
            $cat = $stmt->fetch();
            if (!$cat) json_response(['success'=>false,'message'=>'Category not found'], 404);
            json_response(['success'=>true,'category'=>$cat]);
        }

        // POST /api/categories  (create)
        if ($method === 'POST' && count($parts) === 1) {
            $data = read_json();
            $name = trim($data['name'] ?? '');
            $thumbnail = trim($data['thumbnail'] ?? ''); // optional: relative path
            $is_active = isset($data['is_active']) ? (int)!!$data['is_active'] : 1;

            if ($name === '') json_response(['success'=>false,'message'=>'name required'], 422);

            // unique guard
            $stmt = $pdo->prepare("SELECT id FROM categories WHERE name=? LIMIT 1");
            $stmt->execute([$name]);
            if ($stmt->fetch()) json_response(['success'=>false,'message'=>'Category already exists'], 409);

            $stmt = $pdo->prepare("INSERT INTO categories (name, thumbnail, is_active) VALUES (?,?,?)");
            $stmt->execute([$name, $thumbnail, $is_active]);

            json_response(['success'=>true,'message'=>'Category created','id'=>(int)$pdo->lastInsertId()]);
        }

        // PUT /api/categories/{id} (update)
        if ($method === 'PUT' && count($parts) === 2) {
            $catId = (int)$parts[1];
            $data = read_json();

            $stmt = $pdo->prepare("SELECT * FROM categories WHERE id=? LIMIT 1");
            $stmt->execute([$catId]);
            $cat = $stmt->fetch();
            if (!$cat) json_response(['success'=>false,'message'=>'Category not found'], 404);

            $name = trim($data['name'] ?? $cat['name']);
            $thumbnail = trim($data['thumbnail'] ?? ($cat['thumbnail'] ?? ''));
            $is_active = isset($data['is_active']) ? (int)!!$data['is_active'] : (int)$cat['is_active'];

            if ($name === '') json_response(['success'=>false,'message'=>'name required'], 422);

            // unique guard (exclude self)
            $stmt = $pdo->prepare("SELECT id FROM categories WHERE name=? AND id<>? LIMIT 1");
            $stmt->execute([$name, $catId]);
            if ($stmt->fetch()) json_response(['success'=>false,'message'=>'Category name already used'], 409);

            $stmt = $pdo->prepare("UPDATE categories SET name=?, thumbnail=?, is_active=? WHERE id=?");
            $stmt->execute([$name, $thumbnail, $is_active, $catId]);

            json_response(['success'=>true,'message'=>'Category updated']);
        }

        // DELETE /api/categories/{id}
        if ($method === 'DELETE' && count($parts) === 2) {
            $catId = (int)$parts[1];

            // block delete if children exist
            $stmt = $pdo->prepare("SELECT COUNT(*) FROM subcategories WHERE category_id=?");
            $stmt->execute([$catId]);
            if ((int)$stmt->fetchColumn() > 0) json_response(['success'=>false,'message'=>'Category has subcategories'], 409);

            $stmt = $pdo->prepare("SELECT COUNT(*) FROM services WHERE category_id=?");
            $stmt->execute([$catId]);
            if ((int)$stmt->fetchColumn() > 0) json_response(['success'=>false,'message'=>'Category has services'], 409);

            $stmt = $pdo->prepare("DELETE FROM categories WHERE id=?");
            $stmt->execute([$catId]);

            json_response(['success'=>true,'message'=>'Category deleted']);
        }

        not_found();
    }

    // COUPONS (public - for user app 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);
        }
        try {
            $stmt = $pdo->prepare("SELECT * FROM coupon_codes WHERE code=? AND is_active=1");
            $stmt->execute([$code]);
            $coupon = $stmt->fetch();
        } catch (Exception $e) {
            json_response(['success'=>false,'valid'=>false,'message'=>'Coupon service unavailable'], 503);
        }
        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]);
    }

    // Redeem coupon (call after user payment success to increment used_count)
    if (($parts[0] ?? '') === 'coupons' && ($parts[1] ?? '') === 'redeem' && $method === 'POST') {
        $data = read_json();
        $couponId = (int)($data['coupon_id'] ?? 0);
        if ($couponId <= 0) json_response(['success'=>false,'message'=>'coupon_id required'], 422);
        $stmt = $pdo->prepare("UPDATE coupon_codes SET used_count = used_count + 1 WHERE id=?");
        $stmt->execute([$couponId]);
        if ($stmt->rowCount() === 0) json_response(['success'=>false,'message'=>'Coupon not found'], 404);
        json_response(['success'=>true,'message'=>'Coupon redeemed']);
    }
    
    
    // AUTH
    if (($parts[0] ?? '') === 'auth') {
        $action = $parts[1] ?? '';
       if ($method === 'POST' && $action === 'request-otp') {
    $data = read_json();

    // Fallback: if JSON is empty, accept normal form POST
    if (empty($data) && !empty($_POST)) {
        $data = $_POST;
    }

    // Always treat phone as string and keep only digits
    $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'];

    $stmt = $pdo->prepare("
        INSERT INTO otp_codes (phone, otp, purpose, expires_at)
        VALUES (?,?,?,DATE_ADD(NOW(), INTERVAL 10 MINUTE))
    ");
    $stmt->execute([$phone, $otp, $purpose]);

    json_response(['success' => true, 'message' => 'OTP sent (demo)', 'otp' => $otp]);
}


       if ($method === 'POST' && $action === 'register') {
    $data = read_json();

    $name         = trim($data['name'] ?? '');
    $phone        = trim($data['phone'] ?? '');
    $email        = trim($data['email'] ?? '');
    $otp          = trim($data['otp'] ?? '');

    // NEW: category fields
    $categoryId   = trim((string)($data['categoryId'] ?? ''));
    $categoryName = trim($data['categoryName'] ?? '');

    // required fields validation
    if ($name === '' || $phone === '' || $email === '' || $otp === '' || $categoryId === '' || $categoryName === '') {
        json_response([
            'success' => false,
            'message' => 'name, phone, email, otp, categoryId, categoryName required'
        ], 422);
    }

    // otp validation
    if ($otp !== $cfg['DEMO_OTP']) {
        json_response(['success' => false, 'message' => 'Invalid OTP'], 401);
    }

    // basic categoryId validation (optional but recommended)
    if (!ctype_digit($categoryId) || (int)$categoryId <= 0) {
        json_response(['success' => false, 'message' => 'Invalid categoryId'], 422);
    }
    $categoryIdInt = (int)$categoryId;

    // unique phone/email guard
    $stmt = $pdo->prepare("SELECT id FROM vendors WHERE phone = ? OR email = ? LIMIT 1");
    $stmt->execute([$phone, $email]);
    if ($stmt->fetch()) {
        json_response(['success' => false, 'message' => 'Vendor already exists'], 409);
    }

    // vendor_code
    $next = $pdo->query("SELECT IFNULL(MAX(id),0)+1 AS next_id FROM vendors")->fetch();
    $vendorCode = 'VEN' . str_pad((string)$next['next_id'], 3, '0', STR_PAD_LEFT);

    // NOTE: This assumes your `vendors` table has `category_id` and `category_name` columns.
    $stmt = $pdo->prepare("
        INSERT INTO vendors
            (vendor_code, name, phone, email, category_id, category_name, approval_status, kyc_status, is_active, wallet_balance)
        VALUES
            (?, ?, ?, ?, ?, ?, 'pending', 'not_submitted', 0, 0)
    ");
    $stmt->execute([$vendorCode, $name, $phone, $email, $categoryIdInt, $categoryName]);

    $vendorId = (int)$pdo->lastInsertId();

    $stmt = $pdo->prepare("INSERT INTO vendor_kyc (vendor_id, status) VALUES (?, 'not_submitted')");
    $stmt->execute([$vendorId]);

    // return basic
    json_response([
        'success'       => true,
        'message'       => 'Registered. Proceed to KYC.',
        'vendor_id'     => $vendorId,
        'vendor_code'   => $vendorCode,
        'categoryId'    => $categoryIdInt,
        'categoryName'  => $categoryName
    ]);
}


        if ($method === 'POST' && $action === 'login') {
            $data = read_json();
            $phone = trim($data['phone'] ?? '');
            $otp = trim($data['otp'] ?? '');
            if ($phone===''||$otp==='') json_response(['success'=>false,'message'=>'phone and otp required'], 422);
            if ($otp !== $cfg['DEMO_OTP']) json_response(['success'=>false,'message'=>'Invalid OTP'], 401);

            $stmt = $pdo->prepare("SELECT * FROM vendors WHERE phone=? LIMIT 1");
            $stmt->execute([$phone]);
            $vendor = $stmt->fetch();
            if (!$vendor) json_response(['success'=>false,'message'=>'Vendor not found'], 404);

            $vendorId = (int)$vendor['id'];
            $token = null;
            for ($i = 0; $i < 3; $i++) {
                $token = random_token(64);
                if ($token && strlen($token) >= 32) break;
            }
            if (!$token || strlen($token) < 32) {
                json_response(['success'=>false,'message'=>'Failed to generate auth token. Please try again.'], 500);
            }

            try {
                $stmt = $pdo->prepare("INSERT INTO vendor_tokens (vendor_id, token, expires_at) VALUES (?,?, DATE_ADD(NOW(), INTERVAL 7 DAY))");
                $stmt->execute([$vendorId, $token]);
                if ($stmt->rowCount() < 1) {
                    json_response(['success'=>false,'message'=>'Failed to save token. Please try again.'], 500);
                }
            } catch (PDOException $e) {
                json_response(['success'=>false,'message'=>'Login error. Please try again.'], 500);
            }

            json_response(['success'=>true,'message'=>'Login ok', 'token'=>$token, 'vendor'=>$vendor]);
        }

        not_found();
    }

    // VENDOR
    if (($parts[0] ?? '') === 'vendor') {
        $vendor = require_vendor_auth();
        $action = $parts[1] ?? '';

        if ($method === 'GET' && ($action === '' || $action === 'me')) {
            $stmt = $pdo->prepare("SELECT v.*, k.status AS kyc_record_status, k.admin_notes FROM vendors v LEFT JOIN vendor_kyc k ON k.vendor_id=v.id WHERE v.id=?");
            $stmt->execute([(int)$vendor['id']]);
            json_response(['success'=>true,'vendor'=>$stmt->fetch()]);
        }

        if ($method === 'POST' && $action === 'kyc') {
    $vendorId = (int)$vendor['id'];

    // Accept license_number from POST/REQUEST
    $licenseNumber = trim($_POST['license_number'] ?? $_REQUEST['license_number'] ?? '');
    if ($licenseNumber === '') {
        json_response(['success'=>false,'message'=>'license_number required'], 422);
    }

    // Ensure request is multipart/form-data
    // (If you're sending JSON, $_FILES will be empty and uploads will never work.)
    if (empty($_FILES)) {
        json_response([
            'success' => false,
            'message' => 'No files received. Send as multipart/form-data with file fields: aadhar_front, aadhar_back, pan_front, pan_back, license_photo'
        ], 422);
    }

    // Helper to show proper file upload errors
    $file_error_message = function(string $key): string {
        if (!isset($_FILES[$key])) return "File field '$key' missing";
        $err = (int)($_FILES[$key]['error'] ?? UPLOAD_ERR_NO_FILE);
        $map = [
            UPLOAD_ERR_OK => 'OK',
            UPLOAD_ERR_INI_SIZE => 'File too large (php.ini upload_max_filesize)',
            UPLOAD_ERR_FORM_SIZE => 'File too large (form MAX_FILE_SIZE)',
            UPLOAD_ERR_PARTIAL => 'File partially uploaded',
            UPLOAD_ERR_NO_FILE => 'No file uploaded',
            UPLOAD_ERR_NO_TMP_DIR => 'Missing temp directory',
            UPLOAD_ERR_CANT_WRITE => 'Failed to write file',
            UPLOAD_ERR_EXTENSION => 'Upload blocked by extension',
        ];
        return $map[$err] ?? "Unknown upload error ($err)";
    };

    // Require all files (so you don't “think” it saved, while it didn't)
    $requiredKeys = ['aadhar_front','aadhar_back','pan_front','pan_back','license_photo'];
    $missing = [];
    foreach ($requiredKeys as $k) {
        if (!isset($_FILES[$k]) || (int)($_FILES[$k]['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
            $missing[$k] = $file_error_message($k);
        }
    }
    if (!empty($missing)) {
        json_response([
            'success' => false,
            'message' => 'Upload failed / missing files',
            'details' => $missing
        ], 422);
    }

    // Now upload files (upload_file should move the file and return stored relative path)
    $a1 = upload_file('aadhar_front', 'kyc');
    $a2 = upload_file('aadhar_back', 'kyc');
    $p1 = upload_file('pan_front', 'kyc');
    $p2 = upload_file('pan_back', 'kyc');
    $licPhoto = upload_file('license_photo', 'kyc');

    // If upload_file() returns false/null, fail with clear message
    $failed = [];
    if (!$a1) $failed['aadhar_front'] = 'upload_file() failed';
    if (!$a2) $failed['aadhar_back'] = 'upload_file() failed';
    if (!$p1) $failed['pan_front'] = 'upload_file() failed';
    if (!$p2) $failed['pan_back'] = 'upload_file() failed';
    if (!$licPhoto) $failed['license_photo'] = 'upload_file() failed';

    if (!empty($failed)) {
        json_response([
            'success' => false,
            'message' => 'Server could not save uploaded files',
            'details' => $failed
        ], 500);
    }

    // Use transaction so paths + status update together
    $pdo->beginTransaction();

    // IMPORTANT: ensure vendor_kyc row exists (upsert)
    $stmt = $pdo->prepare("
        INSERT INTO vendor_kyc (vendor_id, status)
        VALUES (?, 'not_submitted')
        ON DUPLICATE KEY UPDATE vendor_id = vendor_id
    ");
    $stmt->execute([$vendorId]);

    // Save paths + license number
    $stmt = $pdo->prepare("
        UPDATE vendor_kyc
        SET aadhar_front_path=?,
            aadhar_back_path=?,
            pan_front_path=?,
            pan_back_path=?,
            license_photo_path=?,
            license_number=?,
            status='submitted',
            submitted_at=NOW()
        WHERE vendor_id=?
    ");
    $stmt->execute([$a1, $a2, $p1, $p2, $licPhoto, $licenseNumber, $vendorId]);

    $stmt = $pdo->prepare("
        UPDATE vendors
        SET kyc_status='submitted',
            license_number=?
        WHERE id=?
    ");
    $stmt->execute([$licenseNumber, $vendorId]);

    $pdo->commit();

    json_response([
        'success' => true,
        'message' => 'KYC submitted',
        'paths' => [
            'aadhar_front' => $a1,
            'aadhar_back' => $a2,
            'pan_front' => $p1,
            'pan_back' => $p2,
            'license_photo' => $licPhoto
        ]
    ]);
}


        if ($method === 'PUT' && $action === 'profile') {
            $data = read_json();
            $name = trim($data['name'] ?? $vendor['name']);
            $email = trim($data['email'] ?? $vendor['email']);
            $stmt = $pdo->prepare("UPDATE vendors SET name=?, email=? WHERE id=?");
            $stmt->execute([$name, $email, (int)$vendor['id']]);
            json_response(['success'=>true,'message'=>'Profile updated']);
        }

        if ($method === 'GET' && $action === 'withdrawal-method') {
            $stmt = $pdo->prepare("SELECT withdrawal_payment_mode, withdrawal_bank_details, withdrawal_upi_id FROM vendors WHERE id=?");
            $stmt->execute([(int)$vendor['id']]);
            $row = $stmt->fetch();
            $bankDetails = null;
            if (!empty($row['withdrawal_bank_details'])) {
                $bankDetails = json_decode($row['withdrawal_bank_details'], true) ?: null;
            }
            json_response([
                'success'=>true,
                'payment_mode'=>$row['withdrawal_payment_mode'] ?? null,
                'bank_details'=>$bankDetails,
                'upi_id'=>$row['withdrawal_upi_id'] ?? null
            ]);
        }

        if ($method === 'PUT' && $action === 'withdrawal-method') {
            $data = read_json();
            $paymentMode = strtolower(trim($data['payment_mode'] ?? ''));
            if (!in_array($paymentMode, ['bank', 'upi'])) {
                json_response(['success'=>false,'message'=>'payment_mode must be bank or upi'], 422);
            }
            $bankDetails = null;
            if ($paymentMode === 'bank') {
                $holder = trim($data['account_holder'] ?? '');
                $accountNo = trim($data['account_number'] ?? '');
                $ifsc = trim($data['ifsc'] ?? '');
                $bankName = trim($data['bank_name'] ?? '');
                if (!$holder || !$accountNo || !$ifsc || !$bankName) {
                    json_response(['success'=>false,'message'=>'account_holder, account_number, ifsc, bank_name required for bank'], 422);
                }
                $bankDetails = json_encode([
                    'account_holder'=>$holder,
                    'account_number'=>$accountNo,
                    'ifsc'=>$ifsc,
                    'bank_name'=>$bankName
                ]);
            }
            $upiId = $paymentMode === 'upi' ? trim($data['upi_id'] ?? '') : null;
            if ($paymentMode === 'upi' && !$upiId) {
                json_response(['success'=>false,'message'=>'upi_id required for UPI'], 422);
            }
            $stmt = $pdo->prepare("UPDATE vendors SET withdrawal_payment_mode=?, withdrawal_bank_details=?, withdrawal_upi_id=? WHERE id=?");
            $stmt->execute([$paymentMode, $bankDetails, $upiId, (int)$vendor['id']]);
            json_response(['success'=>true,'message'=>'Withdrawal method updated']);
        }

        if ($method === 'PUT' && $action === 'push-token') {
            $data = read_json();
            $token = trim($data['expo_push_token'] ?? $data['push_token'] ?? '');
            if ($token === '') {
                json_response(['success'=>false,'message'=>'expo_push_token required'], 422);
            }
            if (!preg_match('/^ExponentPushToken\[.+\]$/', $token)) {
                json_response(['success'=>false,'message'=>'Invalid Expo push token format'], 422);
            }
            $stmt = $pdo->prepare("UPDATE vendors SET expo_push_token=? WHERE id=?");
            $stmt->execute([$token, (int)$vendor['id']]);
            json_response(['success'=>true,'message'=>'Push token registered']);
        }

        if ($method === 'POST' && $action === 'simulate-approval') {
            $stmt = $pdo->prepare("UPDATE vendors SET approval_status='approved', is_active=1 WHERE id=?");
            $stmt->execute([(int)$vendor['id']]);
            json_response(['success'=>true,'message'=>'Vendor activated (demo)']);
        }

        not_found();
    }

    // WEBHOOK: new lead notification (call when lead is created/updated - from customer app, admin, or after DB change)
    if (($parts[0] ?? '') === 'webhooks' && ($parts[1] ?? '') === 'new-lead') {
        if ($method !== 'POST') {
            json_response(['success'=>false,'message'=>'Method not allowed'], 405);
        }
        $data = read_json();
        $categoryId = (int)($data['category_id'] ?? 0);
        $leadId = (int)($data['lead_id'] ?? 0);
        if ($leadId > 0) {
            $stmt = $pdo->prepare("SELECT id, category_id, title, lead_price FROM leads WHERE id=? LIMIT 1");
            $stmt->execute([$leadId]);
            $lead = $stmt->fetch();
            if (!$lead) json_response(['success'=>false,'message'=>'Lead not found'], 404);
            $categoryId = (int)($lead['category_id'] ?? 0);
            $data['title'] = $lead['title'] ?? $data['title'] ?? 'New Lead';
            $data['lead_price'] = (int)($lead['lead_price'] ?? 0);
        }
        if ($categoryId <= 0) json_response(['success'=>false,'message'=>'category_id or lead_id with valid category required'], 422);
        $sent = send_new_lead_notification($pdo, $categoryId, $data);
        $stmt = $pdo->prepare("SELECT COUNT(*) FROM vendors WHERE category_id=? AND expo_push_token IS NOT NULL AND expo_push_token != '' AND is_active=1 AND kyc_status='approved'");
        $stmt->execute([$categoryId]);
        $recipients = (int)$stmt->fetchColumn();
        json_response(['success'=>true,'message'=>'Notification sent','recipients'=>$recipients,'delivered'=>$sent]);
    }

    // DASHBOARD
    if (($parts[0] ?? '') === 'dashboard') {
        $vendor = require_vendor_auth();
        if ($method === 'GET' && (($parts[1] ?? '') === 'summary')) {
            $vendorId = (int)$vendor['id'];

            // wallet balance
            // $wallet = (int)$pdo->prepare("SELECT wallet_balance FROM vendors WHERE id=?")->execute([$vendorId]) or 0;

            $stmt = $pdo->prepare("SELECT wallet_balance, total_earnings, total_jobs FROM vendors WHERE id=?");
            $stmt->execute([$vendorId]);
            $v = $stmt->fetch();

            // Include both online and COD earnings (wallet_transactions only has online; COD jobs don't create wallet txns)
            $stmt = $pdo->prepare("SELECT IFNULL(SUM(j.earning_amount),0) AS today_earnings FROM jobs j WHERE j.vendor_id=? AND j.status='completed' AND DATE(j.completed_at)=CURDATE()");
            $stmt->execute([$vendorId]);
            $todayE = (int)$stmt->fetch()['today_earnings'];

            $stmt = $pdo->prepare("SELECT COUNT(*) AS c FROM jobs j JOIN leads l ON l.id=j.lead_id WHERE j.vendor_id=? AND j.status IN ('accepted','in_progress') AND l.time_slot LIKE 'Today%'");
            $stmt->execute([$vendorId]);
            $todayVisits = (int)$stmt->fetch()['c'];

            $stmt = $pdo->prepare("SELECT COUNT(*) AS c FROM jobs j JOIN leads l ON l.id=j.lead_id WHERE j.vendor_id=? AND j.status IN ('accepted','in_progress') AND l.time_slot LIKE 'Tomorrow%'");
            $stmt->execute([$vendorId]);
            $tomorrowVisits = (int)$stmt->fetch()['c'];

            $stmt = $pdo->prepare("SELECT j.id, j.completed_at, j.earning_amount, l.title, l.location, l.time_slot, COALESCE(j.payment_mode, l.payment_mode, 'online') AS payment_mode FROM jobs j JOIN leads l ON l.id=j.lead_id WHERE j.vendor_id=? AND j.status='completed' ORDER BY j.completed_at DESC LIMIT 5");
            $stmt->execute([$vendorId]);
            $recentCompleted = $stmt->fetchAll();

            json_response(['success'=>true,'data'=>[
                'today_earnings'=>$todayE,
                'wallet_balance'=>(int)($v['wallet_balance'] ?? 0),
                'today_visits'=>$todayVisits,
                'tomorrow_visits'=>$tomorrowVisits,
                'recent_completed_jobs'=>$recentCompleted,
                'total_earnings'=>(int)($v['total_earnings'] ?? 0),
                'total_jobs'=>(int)($v['total_jobs'] ?? 0),
            ]]);
        }
        not_found();
    }

    // LEADS
    if (($parts[0] ?? '') === 'leads') {
        // POST /api/leads - create lead (admin/customer app) + trigger push notification
        if ($method === 'POST' && count($parts) === 1) {
            $data = read_json();
            $categoryId = (int)($data['category_id'] ?? 0);
            $title = trim($data['title'] ?? 'New Lead');
            $leadPrice = (int)($data['lead_price'] ?? $data['price'] ?? 0);
            $location = trim($data['location'] ?? $data['address'] ?? '');
            $timeSlot = trim($data['time_slot'] ?? $data['timeSlot'] ?? 'Today 10AM-12PM');
            $customerName = trim($data['customer_name'] ?? $data['customerName'] ?? '');
            $customerPhone = trim($data['customer_phone'] ?? $data['phone'] ?? $data['customerPhone'] ?? '');
            $paymentMode = strtolower(trim($data['payment_mode'] ?? $data['paymentMode'] ?? 'online'));
            if ($paymentMode !== 'cod') $paymentMode = 'online';

            if ($categoryId <= 0) json_response(['success'=>false,'message'=>'category_id required'], 422);
            if ($title === '') json_response(['success'=>false,'message'=>'title required'], 422);

            $stmt = $pdo->prepare("INSERT INTO leads (category_id, title, lead_price, location, time_slot, customer_name, customer_phone, phone, payment_mode, is_available) VALUES (?,?,?,?,?,?,?,?,?,1)");
            $stmt->execute([$categoryId, $title, $leadPrice, $location, $timeSlot, $customerName, $customerPhone, $customerPhone, $paymentMode]);
            $leadId = (int)$pdo->lastInsertId();

            send_new_lead_notification($pdo, $categoryId, ['title'=>$title,'lead_price'=>$leadPrice]);

            json_response(['success'=>true,'message'=>'Lead created','id'=>$leadId]);
        }

        $vendor = require_vendor_auth();
        $vendorId = (int)$vendor['id'];
        $vendorCategoryId = (int)($vendor['category_id'] ?? 0);

        if ($method === 'GET' && count($parts) === 1) {
            // Only show leads matching vendor's registered category
            if ($vendorCategoryId <= 0) {
                json_response(['success'=>true,'leads'=>[]]);
            } else {
                $stmt = $pdo->prepare("SELECT * FROM leads WHERE is_available=1 AND category_id=? ORDER BY created_at DESC");
                $stmt->execute([$vendorCategoryId]);
                json_response(['success'=>true,'leads'=>$stmt->fetchAll()]);
            }
        }

        if ($method === 'GET' && count($parts) === 2) {
            $leadId = (int)$parts[1];
            if ($vendorCategoryId <= 0) {
                json_response(['success'=>false,'message'=>'Lead not found'], 404);
            } else {
                $stmt = $pdo->prepare("SELECT * FROM leads WHERE id=? AND category_id=?");
                $stmt->execute([$leadId, $vendorCategoryId]);
                $lead = $stmt->fetch();
                if (!$lead) json_response(['success'=>false,'message'=>'Lead not found'], 404);
                json_response(['success'=>true,'lead'=>$lead]);
            }
        }

        if ($method === 'POST' && count($parts) === 3 && $parts[2] === 'pick') {
            $leadId = (int)$parts[1];

            // check lead available and belongs to vendor's category
            if ($vendorCategoryId <= 0) {
                json_response(['success'=>false,'message'=>'Cannot pick lead'], 403);
            }
            $stmt = $pdo->prepare("SELECT * FROM leads WHERE id=? AND is_available=1 AND category_id=? FOR UPDATE");
            $pdo->beginTransaction();
            $stmt->execute([$leadId, $vendorCategoryId]);
            $lead = $stmt->fetch();
            if (!$lead) { $pdo->rollBack(); json_response(['success'=>false,'message'=>'Lead not available'], 409); }

            // wallet: 20% of lead price deducted when picking lead
            $stmt = $pdo->prepare("SELECT wallet_balance FROM vendors WHERE id=? FOR UPDATE");
            $stmt->execute([$vendorId]);
            $bal = (int)$stmt->fetch()['wallet_balance'];
            $price = (int)$lead['lead_price'];
            $deductAmount = (int)round($price * 0.2);
            if ($bal < $deductAmount) { $pdo->rollBack(); json_response(['success'=>false,'message'=>'Insufficient balance. Recharge needed.'], 402); }

            // deduct 20% and transaction
            $stmt = $pdo->prepare("UPDATE vendors SET wallet_balance = wallet_balance - ? WHERE id=?");
            $stmt->execute([$deductAmount, $vendorId]);

            $leadPaymentMode = strtolower(trim($lead['payment_mode'] ?? 'online'));
            $stmt = $pdo->prepare("INSERT INTO wallet_transactions (vendor_id, type, amount, description, ref_type, ref_id, payment_mode) VALUES (?,?,?,?, 'lead', ?, ?)");
            $stmt->execute([$vendorId, 'debit', $deductAmount, 'Picked lead #' . $leadId . ' (20% of ₹' . $price . ')', $leadId, $leadPaymentMode]);

            // generate completion OTP for customer (4 digits), store in leads
            $completionOtp = str_pad((string)random_int(1000, 9999), 4, '0');
            $stmt = $pdo->prepare("UPDATE leads SET is_available=0, completion_otp=? WHERE id=?");
            $stmt->execute([$completionOtp, $leadId]);

            // create job: earning = 80% of lead price, store payment_mode from lead
            $earning = (int)round($price * 0.8);
            $paymentMode = $lead['payment_mode'] ?? 'online';
            $stmt = $pdo->prepare("INSERT INTO jobs (vendor_id, lead_id, status, earning_amount, payment_mode) VALUES (?,?, 'accepted', ?, ?)");
            $stmt->execute([$vendorId, $leadId, $earning, $paymentMode]);

            $jobId = (int)$pdo->lastInsertId();
            $pdo->commit();

            json_response(['success'=>true,'message'=>'Lead picked','job_id'=>$jobId,'completion_otp'=>$completionOtp]);
        }

        not_found();
    }

    // JOBS
    if (($parts[0] ?? '') === 'jobs') {
        $vendor = require_vendor_auth();
        $vendorId = (int)$vendor['id'];

        if ($method === 'GET' && (($parts[1] ?? '') === 'ongoing')) {
            $stmt = $pdo->prepare("SELECT j.*, l.title, l.location, l.time_slot, l.lead_price, l.service_details, l.customer_name, l.customer_phone, l.customer_phone AS lead_phone, l.completion_otp, COALESCE(j.payment_mode, l.payment_mode, 'online') AS payment_mode FROM jobs j JOIN leads l ON l.id=j.lead_id WHERE j.vendor_id=? AND j.status IN ('accepted','in_progress') ORDER BY j.created_at DESC");
            $stmt->execute([$vendorId]);
            json_response(['success'=>true,'jobs'=>$stmt->fetchAll()]);
        }

        if ($method === 'GET' && (($parts[1] ?? '') === 'completed')) {
            $stmt = $pdo->prepare("SELECT j.*, l.title, l.location, l.time_slot, l.lead_price, l.service_details, COALESCE(j.payment_mode, l.payment_mode, 'online') AS payment_mode FROM jobs j JOIN leads l ON l.id=j.lead_id WHERE j.vendor_id=? AND j.status='completed' ORDER BY j.completed_at DESC");
            $stmt->execute([$vendorId]);
            json_response(['success'=>true,'jobs'=>$stmt->fetchAll()]);
        }

        if ($method === 'POST' && isset($parts[1]) && isset($parts[2]) && $parts[2] === 'start') {
            $jobId = (int)$parts[1];

            $stmt = $pdo->prepare("SELECT * FROM jobs WHERE id=? AND vendor_id=? LIMIT 1");
            $stmt->execute([$jobId, $vendorId]);
            $job = $stmt->fetch();
            if (!$job) json_response(['success'=>false,'message'=>'Job not found'], 404);
            if ($job['status'] !== 'accepted') json_response(['success'=>false,'message'=>'Job cannot be started'], 409);

            $before = upload_file('before_photo', 'jobs');
            if (!$before) json_response(['success'=>false,'message'=>'before_photo required'], 422);

            $stmt = $pdo->prepare("UPDATE jobs SET status='in_progress', before_photo_path=?, started_at=NOW() WHERE id=?");
            $stmt->execute([$before, $jobId]);

            json_response(['success'=>true,'message'=>'Job started']);
        }

        if ($method === 'POST' && isset($parts[1]) && isset($parts[2]) && $parts[2] === 'complete') {
            $jobId = (int)$parts[1];
            $otp = trim($_POST['otp'] ?? $_REQUEST['otp'] ?? '');

            $stmt = $pdo->prepare("SELECT j.*, l.completion_otp, COALESCE(j.payment_mode, l.payment_mode, 'online') AS payment_mode FROM jobs j JOIN leads l ON l.id=j.lead_id WHERE j.id=? AND j.vendor_id=? LIMIT 1");
            $stmt->execute([$jobId, $vendorId]);
            $job = $stmt->fetch();
            if (!$job) json_response(['success'=>false,'message'=>'Job not found'], 404);
            if ($job['status'] !== 'in_progress') json_response(['success'=>false,'message'=>'Job cannot be completed'], 409);
            $storedOtp = trim($job['completion_otp'] ?? '');
            $validOtp = !empty($storedOtp) ? ($otp === $storedOtp) : ($otp === ($cfg['COMPLETION_OTP'] ?? '5678'));
            if (!$validOtp) json_response(['success'=>false,'message'=>'Invalid completion OTP'], 401);

            $after = upload_file('after_photo', 'jobs');
            if (!$after) json_response(['success'=>false,'message'=>'after_photo required'], 422);

            $pdo->beginTransaction();

            $earning = (int)($job['earning_amount'] ?? 500);
            $paymentMode = strtolower(trim($job['payment_mode'] ?? 'online'));

            $stmt = $pdo->prepare("UPDATE jobs SET status='completed', after_photo_path=?, completion_otp_entered=?, completed_at=NOW() WHERE id=?");
            $stmt->execute([$after, $otp, $jobId]);

            if ($paymentMode === 'online') {
                $stmt = $pdo->prepare("UPDATE vendors SET wallet_balance = wallet_balance + ?, total_earnings = total_earnings + ?, total_jobs = total_jobs + 1 WHERE id=?");
                $stmt->execute([$earning, $earning, $vendorId]);
                $stmt = $pdo->prepare("INSERT INTO wallet_transactions (vendor_id, type, amount, description, ref_type, ref_id, payment_mode) VALUES (?,?,?,?, 'job', ?, ?)");
                $stmt->execute([$vendorId, 'earning', $earning, 'Job earning (online)', $jobId, 'online']);
            } else {
                $stmt = $pdo->prepare("UPDATE vendors SET total_earnings = total_earnings + ?, total_jobs = total_jobs + 1 WHERE id=?");
                $stmt->execute([$earning, $vendorId]);
            }

            $pdo->commit();

            json_response(['success'=>true,'message'=>'Job completed', 'earning'=>$earning, 'wallet_credited'=>$paymentMode === 'online']);
        }

        not_found();
    }

    // WALLET
    if (($parts[0] ?? '') === 'wallet') {
        $vendor = require_vendor_auth();
        $vendorId = (int)$vendor['id'];

        if ($method === 'GET' && (($parts[1] ?? '') === 'summary')) {
            $stmt = $pdo->prepare("SELECT wallet_balance, total_earnings, total_jobs FROM vendors WHERE id=?");
            $stmt->execute([$vendorId]);
            json_response(['success'=>true,'data'=>$stmt->fetch()]);
        }

        if ($method === 'GET' && (($parts[1] ?? '') === 'history')) {
            $stmt = $pdo->prepare("SELECT * FROM wallet_transactions WHERE vendor_id=? ORDER BY created_at DESC LIMIT 200");
            $stmt->execute([$vendorId]);
            json_response(['success'=>true,'transactions'=>$stmt->fetchAll()]);
        }

        if ($method === 'POST' && (($parts[1] ?? '') === 'create-order')) {
            $data = read_json();
            $amount = (int)($data['amount'] ?? 0);
            if ($amount <= 0) json_response(['success'=>false,'message'=>'amount must be > 0'], 422);
            $amountPaise = $amount * 100;
            $receipt = 'rec_' . $vendorId . '_' . time();
            $razorpayKey = getenv('RAZORPAY_KEY_ID') ?: 'rzp_live_RjcbqtjrfS8004';
            $razorpaySecret = getenv('RAZORPAY_KEY_SECRET') ?: 'WeS0GpG1fSjmMNjTKYIdcQ7K';
            $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);
            $order = json_decode($res, true);
            if ($code !== 200 || empty($order['id'])) {
                json_response(['success'=>false,'message'=>$order['error']['description'] ?? 'Failed to create order'], 400);
            }
            json_response(['success'=>true,'order_id'=>$order['id'],'amount'=>$amount,'currency'=>'INR','key_id'=>$razorpayKey]);
        }

        if ($method === 'POST' && (($parts[1] ?? '') === 'verify-recharge')) {
            $data = read_json();
            $orderId = trim($data['razorpay_order_id'] ?? $data['order_id'] ?? '');
            $paymentId = trim($data['razorpay_payment_id'] ?? $data['payment_id'] ?? '');
            $signature = trim($data['razorpay_signature'] ?? $data['signature'] ?? '');
            $amount = (int)($data['amount'] ?? 0);
            if (!$orderId || !$paymentId || !$signature || $amount <= 0) {
                json_response(['success'=>false,'message'=>'Invalid verification data'], 422);
            }
            $razorpaySecret = getenv('RAZORPAY_KEY_SECRET') ?: 'WeS0GpG1fSjmMNjTKYIdcQ7K';
            $expectedSig = hash_hmac('sha256', $orderId . '|' . $paymentId, $razorpaySecret);
            if (!hash_equals($expectedSig, $signature)) {
                json_response(['success'=>false,'message'=>'Payment verification failed'], 400);
            }
            $pdo->beginTransaction();
            $stmt = $pdo->prepare("SELECT id FROM wallet_transactions WHERE description LIKE ? AND ref_type='recharge' LIMIT 1");
            $stmt->execute(['%' . $orderId . '%']);
            if ($stmt->fetch()) {
                $pdo->rollBack();
                json_response(['success'=>false,'message'=>'Payment already processed'], 409);
            }
            $stmt = $pdo->prepare("UPDATE vendors SET wallet_balance = wallet_balance + ? WHERE id=?");
            $stmt->execute([$amount, $vendorId]);
            $stmt = $pdo->prepare("INSERT INTO wallet_transactions (vendor_id, type, amount, description, ref_type, payment_mode) VALUES (?,?,?,?, 'recharge', 'online')");
            $stmt->execute([$vendorId, 'credit', $amount, 'Recharge via Razorpay (Order: ' . $orderId . ')', 'recharge']);
            $pdo->commit();
            json_response(['success'=>true,'message'=>'Recharged', 'amount'=>$amount]);
        }

        if ($method === 'POST' && (($parts[1] ?? '') === 'recharge')) {
            $data = read_json();
            $amount = (int)($data['amount'] ?? 0);
            if ($amount <= 0) json_response(['success'=>false,'message'=>'amount must be > 0'], 422);
            $pdo->beginTransaction();
            $stmt = $pdo->prepare("UPDATE vendors SET wallet_balance = wallet_balance + ? WHERE id=?");
            $stmt->execute([$amount, $vendorId]);
            $stmt = $pdo->prepare("INSERT INTO wallet_transactions (vendor_id, type, amount, description, ref_type, payment_mode) VALUES (?,?,?,?, 'recharge', 'online')");
            $stmt->execute([$vendorId, 'credit', $amount, 'Recharge via Razorpay']);
            $pdo->commit();
            json_response(['success'=>true,'message'=>'Recharged', 'amount'=>$amount]);
        }

        if ($method === 'POST' && (($parts[1] ?? '') === 'withdraw-request')) {
            $data = read_json();
            $amount = (int)($data['amount'] ?? 0);
            if ($amount <= 0) json_response(['success'=>false,'message'=>'amount must be > 0'], 422);
            $stmt = $pdo->prepare("SELECT wallet_balance, withdrawal_payment_mode, withdrawal_bank_details, withdrawal_upi_id FROM vendors WHERE id=?");
            $stmt->execute([$vendorId]);
            $v = $stmt->fetch();
            if (empty($v['withdrawal_payment_mode']) || !in_array($v['withdrawal_payment_mode'], ['bank', 'upi'])) {
                json_response(['success'=>false,'message'=>'Please add withdrawal payment method in Profile first'], 422);
            }
            $bal = (int)($v['wallet_balance'] ?? 0);
            if ($bal < $amount) {
                json_response(['success'=>false,'message'=>'Insufficient balance'], 402);
            }
            $paymentMode = $v['withdrawal_payment_mode'];
            $bankDetails = $v['withdrawal_bank_details'] ?? null;
            $upiId = $v['withdrawal_upi_id'] ?? null;
            $pdo->beginTransaction();
            $stmt = $pdo->prepare("INSERT INTO withdrawal_requests (vendor_id, amount, status, payment_status, payment_mode, bank_details, upi_id) VALUES (?,?, 'pending', 'unpaid', ?, ?, ?)");
            $stmt->execute([$vendorId, $amount, $paymentMode, $bankDetails, $upiId]);
            $reqId = (int)$pdo->lastInsertId();
            $stmt = $pdo->prepare("UPDATE vendors SET wallet_balance = wallet_balance - ? WHERE id=?");
            $stmt->execute([$amount, $vendorId]);
            $stmt = $pdo->prepare("INSERT INTO wallet_transactions (vendor_id, type, amount, description, ref_type, ref_id) VALUES (?,?,?,?, 'withdrawal', ?)");
            $stmt->execute([$vendorId, 'debit', $amount, 'Withdrawal request', $reqId]);
            $pdo->commit();
            json_response(['success'=>true,'message'=>'Withdrawal request submitted','id'=>$reqId,'amount'=>$amount]);
        }

        if ($method === 'GET' && (($parts[1] ?? '') === 'withdrawals')) {
            $stmt = $pdo->prepare("SELECT id, amount, status, payment_status, payment_mode, notes, created_at, updated_at FROM withdrawal_requests WHERE vendor_id=? ORDER BY created_at DESC LIMIT 100");
            $stmt->execute([$vendorId]);
            json_response(['success'=>true,'withdrawals'=>$stmt->fetchAll()]);
        }

        not_found();
    }
    
    
        /* =========================================================
       CATEGORIES API
       Endpoints:
         GET    /api/categories
         GET    /api/categories/{id}
         POST   /api/categories
         PUT    /api/categories/{id}
         DELETE /api/categories/{id}
       ========================================================= */



    /* =========================================================
       SUBCATEGORIES API
       Endpoints:
         GET    /api/subcategories?category_id=1
         GET    /api/subcategories/{id}
         POST   /api/subcategories
         PUT    /api/subcategories/{id}
         DELETE /api/subcategories/{id}
       ========================================================= */
    if (($parts[0] ?? '') === 'subcategories') {
        $vendor = require_vendor_auth();

        // GET /api/subcategories?category_id=1
        if ($method === 'GET' && count($parts) === 1) {
            $categoryId = (int)($_GET['category_id'] ?? 0);
            $onlyActive = ($_GET['only_active'] ?? '1') === '1' ? 1 : 0;

            $sql = "SELECT s.id, s.category_id, s.name, s.thumbnail, s.is_active, s.created_at, c.name AS category_name
                    FROM subcategories s
                    JOIN categories c ON c.id=s.category_id";
            $where = [];
            $params = [];

            if ($onlyActive) { $where[] = "s.is_active=1"; }
            if ($categoryId > 0) { $where[] = "s.category_id=?"; $params[] = $categoryId; }

            if ($where) $sql .= " WHERE " . implode(" AND ", $where);
            $sql .= " ORDER BY s.name ASC";

            $stmt = $pdo->prepare($sql);
            $stmt->execute($params);

            json_response(['success'=>true, 'subcategories'=>$stmt->fetchAll()]);
        }

        // GET /api/subcategories/{id}
        if ($method === 'GET' && count($parts) === 2) {
            $subId = (int)$parts[1];
            $stmt = $pdo->prepare("SELECT s.*, c.name AS category_name
                                   FROM subcategories s
                                   JOIN categories c ON c.id=s.category_id
                                   WHERE s.id=? LIMIT 1");
            $stmt->execute([$subId]);
            $sub = $stmt->fetch();
            if (!$sub) json_response(['success'=>false,'message'=>'Subcategory not found'], 404);
            json_response(['success'=>true,'subcategory'=>$sub]);
        }

        // POST /api/subcategories
        if ($method === 'POST' && count($parts) === 1) {
            $data = read_json();
            $category_id = (int)($data['category_id'] ?? 0);
            $name = trim($data['name'] ?? '');
            $thumbnail = trim($data['thumbnail'] ?? '');
            $is_active = isset($data['is_active']) ? (int)!!$data['is_active'] : 1;

            if ($category_id <= 0 || $name === '') json_response(['success'=>false,'message'=>'category_id and name required'], 422);

            // category exists
            $stmt = $pdo->prepare("SELECT id FROM categories WHERE id=? LIMIT 1");
            $stmt->execute([$category_id]);
            if (!$stmt->fetch()) json_response(['success'=>false,'message'=>'Category not found'], 404);

            // unique per category
            $stmt = $pdo->prepare("SELECT id FROM subcategories WHERE category_id=? AND name=? LIMIT 1");
            $stmt->execute([$category_id, $name]);
            if ($stmt->fetch()) json_response(['success'=>false,'message'=>'Subcategory already exists in this category'], 409);

            $stmt = $pdo->prepare("INSERT INTO subcategories (category_id, name, thumbnail, is_active) VALUES (?,?,?,?)");
            $stmt->execute([$category_id, $name, $thumbnail, $is_active]);

            json_response(['success'=>true,'message'=>'Subcategory created','id'=>(int)$pdo->lastInsertId()]);
        }

        // PUT /api/subcategories/{id}
        if ($method === 'PUT' && count($parts) === 2) {
            $subId = (int)$parts[1];
            $data = read_json();

            $stmt = $pdo->prepare("SELECT * FROM subcategories WHERE id=? LIMIT 1");
            $stmt->execute([$subId]);
            $sub = $stmt->fetch();
            if (!$sub) json_response(['success'=>false,'message'=>'Subcategory not found'], 404);

            $category_id = isset($data['category_id']) ? (int)$data['category_id'] : (int)$sub['category_id'];
            $name = trim($data['name'] ?? $sub['name']);
            $thumbnail = trim($data['thumbnail'] ?? ($sub['thumbnail'] ?? ''));
            $is_active = isset($data['is_active']) ? (int)!!$data['is_active'] : (int)$sub['is_active'];

            if ($category_id <= 0 || $name === '') json_response(['success'=>false,'message'=>'category_id and name required'], 422);

            // category exists
            $stmt = $pdo->prepare("SELECT id FROM categories WHERE id=? LIMIT 1");
            $stmt->execute([$category_id]);
            if (!$stmt->fetch()) json_response(['success'=>false,'message'=>'Category not found'], 404);

            // unique guard exclude self
            $stmt = $pdo->prepare("SELECT id FROM subcategories WHERE category_id=? AND name=? AND id<>? LIMIT 1");
            $stmt->execute([$category_id, $name, $subId]);
            if ($stmt->fetch()) json_response(['success'=>false,'message'=>'Subcategory name already used in this category'], 409);

            $stmt = $pdo->prepare("UPDATE subcategories SET category_id=?, name=?, thumbnail=?, is_active=? WHERE id=?");
            $stmt->execute([$category_id, $name, $thumbnail, $is_active, $subId]);

            json_response(['success'=>true,'message'=>'Subcategory updated']);
        }

        // DELETE /api/subcategories/{id}
        if ($method === 'DELETE' && count($parts) === 2) {
            $subId = (int)$parts[1];

            // block delete if services exist
            $stmt = $pdo->prepare("SELECT COUNT(*) FROM services WHERE subcategory_id=?");
            $stmt->execute([$subId]);
            if ((int)$stmt->fetchColumn() > 0) json_response(['success'=>false,'message'=>'Subcategory has services'], 409);

            $stmt = $pdo->prepare("DELETE FROM subcategories WHERE id=?");
            $stmt->execute([$subId]);

            json_response(['success'=>true,'message'=>'Subcategory deleted']);
        }

        not_found();
    }


    /* =========================================================
       SERVICES API
       Endpoints:
         GET    /api/services?category_id=1&subcategory_id=2
         GET    /api/services/{id}
         POST   /api/services
         PUT    /api/services/{id}
         DELETE /api/services/{id}
       ========================================================= */
    if (($parts[0] ?? '') === 'services') {
        $vendor = require_vendor_auth();

        // GET /api/services?category_id=&subcategory_id=
        if ($method === 'GET' && count($parts) === 1) {
            $categoryId = (int)($_GET['category_id'] ?? 0);
            $subcategoryId = (int)($_GET['subcategory_id'] ?? 0);
            $onlyActive = ($_GET['only_active'] ?? '1') === '1' ? 1 : 0;

            $sql = "SELECT s.*,
                           c.name AS category_name,
                           sc.name AS subcategory_name
                    FROM services s
                    JOIN categories c ON c.id=s.category_id
                    JOIN subcategories sc ON sc.id=s.subcategory_id";
            $where = [];
            $params = [];

            if ($onlyActive) $where[] = "s.is_active=1";
            if ($categoryId > 0) { $where[] = "s.category_id=?"; $params[] = $categoryId; }
            if ($subcategoryId > 0) { $where[] = "s.subcategory_id=?"; $params[] = $subcategoryId; }

            if ($where) $sql .= " WHERE " . implode(" AND ", $where);
            $sql .= " ORDER BY s.created_at DESC";

            $stmt = $pdo->prepare($sql);
            $stmt->execute($params);
            $rows = $stmt->fetchAll();

            // decode images_json
            foreach ($rows as &$r) {
                $imgs = json_decode($r['images_json'] ?? '[]', true);
                $r['images'] = is_array($imgs) ? $imgs : [];
            }

            json_response(['success'=>true,'services'=>$rows]);
        }

        // GET /api/services/{id}
        if ($method === 'GET' && count($parts) === 2) {
            $serviceId = (int)$parts[1];
            $stmt = $pdo->prepare("SELECT s.*,
                                          c.name AS category_name,
                                          sc.name AS subcategory_name
                                   FROM services s
                                   JOIN categories c ON c.id=s.category_id
                                   JOIN subcategories sc ON sc.id=s.subcategory_id
                                   WHERE s.id=? LIMIT 1");
            $stmt->execute([$serviceId]);
            $svc = $stmt->fetch();
            if (!$svc) json_response(['success'=>false,'message'=>'Service not found'], 404);

            $imgs = json_decode($svc['images_json'] ?? '[]', true);
            $svc['images'] = is_array($imgs) ? $imgs : [];

            json_response(['success'=>true,'service'=>$svc]);
        }

        // POST /api/services (create)
        if ($method === 'POST' && count($parts) === 1) {
            $data = read_json();
            $category_id = (int)($data['category_id'] ?? 0);
            $subcategory_id = (int)($data['subcategory_id'] ?? 0);
            $title = trim($data['title'] ?? '');
            $price = (int)($data['price'] ?? 0);
            $description = trim($data['description'] ?? '');
            $thumbnail = trim($data['thumbnail'] ?? '');
            $images = $data['images'] ?? []; // array of paths
            $is_active = isset($data['is_active']) ? (int)!!$data['is_active'] : 1;

            if ($category_id<=0 || $subcategory_id<=0 || $title==='' || $price<=0 || $thumbnail==='') {
                json_response(['success'=>false,'message'=>'category_id, subcategory_id, title, price, thumbnail required'], 422);
            }

            // validate subcategory belongs to category
            $stmt = $pdo->prepare("SELECT id FROM subcategories WHERE id=? AND category_id=? LIMIT 1");
            $stmt->execute([$subcategory_id, $category_id]);
            if (!$stmt->fetch()) json_response(['success'=>false,'message'=>'Invalid category/subcategory pair'], 422);

            if (!is_array($images)) $images = [];
            $images_json = json_encode(array_values($images));

            $stmt = $pdo->prepare("INSERT INTO services (category_id, subcategory_id, title, price, description, thumbnail, images_json, is_active)
                                   VALUES (?,?,?,?,?,?,?,?)");
            $stmt->execute([$category_id, $subcategory_id, $title, $price, $description, $thumbnail, $images_json, $is_active]);

            json_response(['success'=>true,'message'=>'Service created','id'=>(int)$pdo->lastInsertId()]);
        }

        // PUT /api/services/{id} (update)
        if ($method === 'PUT' && count($parts) === 2) {
            $serviceId = (int)$parts[1];
            $data = read_json();

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

            $category_id = isset($data['category_id']) ? (int)$data['category_id'] : (int)$svc['category_id'];
            $subcategory_id = isset($data['subcategory_id']) ? (int)$data['subcategory_id'] : (int)$svc['subcategory_id'];
            $title = trim($data['title'] ?? $svc['title']);
            $price = isset($data['price']) ? (int)$data['price'] : (int)$svc['price'];
            $description = trim($data['description'] ?? ($svc['description'] ?? ''));
            $thumbnail = trim($data['thumbnail'] ?? ($svc['thumbnail'] ?? ''));
            $is_active = isset($data['is_active']) ? (int)!!$data['is_active'] : (int)$svc['is_active'];

            // If client sends images array, replace; if not, keep existing
            $images_json = $svc['images_json'];
            if (array_key_exists('images', $data)) {
                $images = $data['images'];
                if (!is_array($images)) $images = [];
                $images_json = json_encode(array_values($images));
            }

            if ($category_id<=0 || $subcategory_id<=0 || $title==='' || $price<=0 || $thumbnail==='') {
                json_response(['success'=>false,'message'=>'category_id, subcategory_id, title, price, thumbnail required'], 422);
            }

            // validate subcategory belongs to category
            $stmt = $pdo->prepare("SELECT id FROM subcategories WHERE id=? AND category_id=? LIMIT 1");
            $stmt->execute([$subcategory_id, $category_id]);
            if (!$stmt->fetch()) json_response(['success'=>false,'message'=>'Invalid category/subcategory pair'], 422);

            $stmt = $pdo->prepare("UPDATE services
                                   SET category_id=?, subcategory_id=?, title=?, price=?, description=?, thumbnail=?, images_json=?, is_active=?
                                   WHERE id=?");
            $stmt->execute([$category_id, $subcategory_id, $title, $price, $description, $thumbnail, $images_json, $is_active, $serviceId]);

            json_response(['success'=>true,'message'=>'Service updated']);
        }

        // DELETE /api/services/{id}
        if ($method === 'DELETE' && count($parts) === 2) {
            $serviceId = (int)$parts[1];

            $stmt = $pdo->prepare("DELETE FROM services WHERE id=?");
            $stmt->execute([$serviceId]);

            json_response(['success'=>true,'message'=>'Service deleted']);
        }

        not_found();
    }

    

    not_found();
} catch (Exception $e) {
    if (db()->inTransaction()) db()->rollBack();
    json_response(['success'=>false,'message'=>'Server error','error'=>$e->getMessage()], 500);
}
