/** * api_chat_search.php * Searches for cars/categories by brand+model name (fuzzy), and when not found * returns alternatives from the same category that ARE available for the period. * * POST body (JSON or form): * car_name - string, what the user typed (e.g. "Hyundai i10" or "citadine") * date_pu - YYYY-MM-DD * date_do - YYYY-MM-DD * rental_duration - int (days, optional — auto-computed from dates) * * No parc/agence filter: availability is fleet-wide via fleet_planning. */ header('Content-Type: application/json'); header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: POST, OPTIONS'); if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; } require_once __DIR__ . '/db.php'; require_once __DIR__ . '/functions_process_rate.php'; if (!function_exists('countCarsForCategorySearch')) { function countCarsForCategorySearch(PDO $pdo, int $categoryId, string $datePu, string $dateDo): int { $stmt = $pdo->prepare(" SELECT COUNT(*) AS cnt FROM fleet_cars fc LEFT JOIN fleet_models fm ON fm.id = fc.fk_model_id WHERE fm.fk_category_id = :cat_id AND fc.status = 1 AND fc.transit = 1 AND NOT EXISTS ( SELECT 1 FROM fleet_planning fp WHERE fp.fk_fleet_id = fc.id AND fp.status = 1 AND fp.date_start < :date_do AND fp.date_end > :date_pu ) "); $stmt->execute([':cat_id' => $categoryId, ':date_pu' => $datePu, ':date_do' => $dateDo]); return (int) $stmt->fetchColumn(); } } // ── Parse input ─────────────────────────────────────────────────────────────── $input = json_decode(file_get_contents('php://input'), true); if (empty($input)) $input = $_POST; $car_name = trim($input['car_name'] ?? ''); $date_pu = trim($input['date_pu'] ?? ''); $date_do = trim($input['date_do'] ?? ''); $rental_duration = isset($input['rental_duration']) && (int)$input['rental_duration'] > 0 ? (int)$input['rental_duration'] : max(1, (int)ceil((strtotime($date_do) - strtotime($date_pu)) / 86400)); if (!$car_name || !$date_pu || !$date_do) { http_response_code(400); echo json_encode(['error' => 'Paramètres manquants: car_name, date_pu, date_do requis.']); exit; } // ── 1. Try to find the requested car/category by fuzzy name match ───────────── $pdo = pdo(); // Search in fleet_categories.name, brand name, model name (case-insensitive LIKE) $searchTerms = array_filter(explode(' ', $car_name)); $likeClauses = []; $params = []; foreach ($searchTerms as $i => $term) { $key = ":term$i"; $likeClauses[] = "(fc.name LIKE $key OR fb.name LIKE $key OR fm.name LIKE $key OR fm.subname LIKE $key)"; $params[$key] = '%' . $term . '%'; } $whereSearch = implode(' AND ', $likeClauses); $searchSql = " SELECT fc.id AS category_id, fc.name AS category_name, fc.fk_model_default_id, fb.name AS brand_name, fm.name AS model_name,fc.home_public as loable_sur_internet,fc.model_exclu as model_garantie, CONCAT_WS(', ', NULLIF(TRIM(CONCAT(IFNULL(fb.name, ''), ' ', IFNULL(fm.name, ''))), ''), NULLIF(( SELECT GROUP_CONCAT(DISTINCT TRIM(CONCAT(IFNULL(fb_sub.name, ''), ' ', IFNULL(fm_sub.name, ''))) SEPARATOR ', ') FROM fleet_cars car INNER JOIN fleet_models fm_sub ON fm_sub.id = car.fk_model_id INNER JOIN fleet_brands fb_sub ON fb_sub.id = fm_sub.fk_brand_id WHERE fm_sub.fk_category_id = fc.id AND (fc.fk_model_default_id IS NULL OR fm_sub.id != fc.fk_model_default_id) AND car.status = 1 AND car.transit = 1 ), '') ) AS model_full_name, fc.fk_category_type_id FROM fleet_categories fc LEFT JOIN fleet_models fm ON fm.id = fc.fk_model_default_id LEFT JOIN fleet_brands fb ON fb.id = fm.fk_brand_id AND fb.status = 1 WHERE fc.status = 1 AND ($whereSearch) AND fc.home_public = 1 ORDER BY fc.list_order ASC LIMIT 5 "; $stmt = $pdo->prepare($searchSql); $stmt->execute($params); $found = $stmt->fetchAll(PDO::FETCH_ASSOC); // ── 2. Get pricing for all categories (fleet-wide, no parc filter) ──────────── $quotation = [ 'date_pu_full' => $date_pu . ' 10:00:00', 'date_do_full' => $date_do . ' 10:00:00', 'rental_duration' => $rental_duration, 'fk_parc_id' => 0, // no parc filter 'fk_category_group_id' => 0, 'fk_category_id' => 0, 'special_id_pu' => 0, 'special_id_do' => 0, ]; $allPriced = getQuotationCategoriesPrice($quotation, null, null); // Override nb_car fleet-wide for each category $availableById = []; foreach ($allPriced as $cat) { $nbCar = countCarsForCategorySearch($pdo, (int)$cat['id'], $date_pu . ' 00:00:00', $date_do . ' 23:59:59'); if ($nbCar > 0) { $cat['nb_car'] = $nbCar; $availableById[$cat['id']] = $cat; } } $exactMatches = []; $unavailableMatches = []; foreach ($found as $f) { $catId = (int)$f['category_id']; if (isset($availableById[$catId])) { $cat = $availableById[$catId]; $exactMatches[] = [ 'id' => $catId, 'category_name' => $cat['name'], 'model_full_name' => $cat['model_full_name'] ?? '', 'total_amount' => round($cat['period_amount'], 2), 'day_rate' => $rental_duration > 0 ? round($cat['period_amount'] / $rental_duration, 2) : 0, 'nb_car' => $cat['nb_car'], 'is_exact' => true, 'model_garantie' => $cat['model_garantie'], ]; } else { $unavailableMatches[] = $f; } } // ── 3. For unavailable: find alternatives from same category type ────────────── $allAvailable = $availableById; // reuse for suggestions $suggested = []; if (!empty($unavailableMatches) && empty($exactMatches)) { $typeIds = array_unique(array_column($unavailableMatches, 'fk_category_type_id')); foreach ($allAvailable as $cat) { $stypeStmt = $pdo->prepare("SELECT fk_category_type_id FROM fleet_categories WHERE id = :id LIMIT 1"); $stypeStmt->execute([':id' => $cat['id']]); $catType = $stypeStmt->fetchColumn(); if (in_array($catType, $typeIds)) { $suggested[] = [ 'id' => $cat['id'], 'category_name' => $cat['name'], 'model_full_name' => $cat['model_full_name'] ?? '', 'total_amount' => round($cat['period_amount'], 2), 'day_rate' => $rental_duration > 0 ? round($cat['period_amount'] / $rental_duration, 2) : 0, 'nb_car' => $cat['nb_car'], 'is_exact' => false, 'model_garantie' => false, ]; } } } if (empty($exactMatches) && empty($suggested) && !empty($allAvailable)) { $byPrice = array_values($allAvailable); usort($byPrice, fn($a, $b) => $a['period_amount'] <=> $b['period_amount']); foreach (array_slice($byPrice, 0, 3) as $cat) { $suggested[] = [ 'id' => $cat['id'], 'category_name' => $cat['name'], 'model_full_name' => $cat['model_full_name'] ?? '', 'total_amount' => round($cat['period_amount'], 2), 'day_rate' => $rental_duration > 0 ? round($cat['period_amount'] / $rental_duration, 2) : 0, 'nb_car' => $cat['nb_car'], 'is_exact' => false, 'model_garantie' => false, ]; } } // ── 5. Build response ───────────────────────────────────────────────────────── $status = 'not_found'; if (!empty($exactMatches)) { $status = 'found'; } elseif (!empty($suggested)) { $status = 'alternatives'; } echo json_encode([ 'status' => $status, 'searched_for' => $car_name, 'exact_matches' => $exactMatches, 'alternatives' => $suggested, ]);