# Search API — مواصفات لفريق Flutter

الـ Base URL ونظام الهيدرات نفس بقية API التطبيق (`/api/v2/...`).
الهيدر `App-Language: ar` (أو `en`) يحدد لغة الأسماء المرجعة **ومفتاح الكاش** — أرسلوه دائماً.

---

## 1) GET `/api/v2/search/landing`

طلب واحد لشاشة البحث كاملة. لا auth مطلوب.

Query params اختيارية: `limit_searches` (افتراضي 15)، `limit_categories` (10)، `limit_brands` (10)، `limit_products` (10) — الحد الأقصى 30 لكلٍ منها.

```json
{
  "trending_searches": [ { "id": 1, "query": "فساتين" } ],
  "trending_categories": [ /* نفس شكل عناصر /categories/top حرفياً */ ],
  "trending_brands":     [ /* نفس شكل عناصر /brands حرفياً */ ],
  "most_searched_products": [ /* نفس شكل عناصر /products/best-seller حرفياً (ProductMiniCollection) */ ],
  "placeholder_keywords": ["فساتين", "Nike", "حقائب"],
  "success": true,
  "status": 200
}
```

- **لا موديلات جديدة للمنتج/الفئة/الماركة** — استخدموا `Product` و`Category` و`Brand` الحالية كما هي.
- الرد مكاشى 15 دقيقة على السيرفر — لا داعي لكاش عميق عندكم غير الـ stale-while-revalidate المذكور في الخطة.
- في الأيام الأولى (قبل تراكم بيانات التتبع) المحتوى يأتي من fallbacks تلقائياً (بحثات قديمة / فئات مميزة / أفضل مبيعاً) — نفس الشكل تماماً، لا حالة خاصة عندكم.
- `trending_brands` يستبعد الماركات التي لا تملك أي منتج معروض، فقد تعود أقل من `limit_brands` (أو فارغة) — اعرضوا ما يصل ولا تفترضوا امتلاء القائمة. ونفس الاستبعاد يطبَّق على `brands` في `/search/suggestions`.

## 2) GET `/api/v2/search/suggestions?q={نص}`

اقتراحات مصنفة أثناء الكتابة. لا auth مطلوب.

```json
{
  "keywords":   [ { "query": "أحذية رياضية", "count": 830 } ],
  "products":   [ /* نفس شكل عناصر /products/search حرفياً (ProductMiniCollection) */ ],
  "categories": [ { "id": 4, "name": "...", "slug": "..." } ],
  "brands":     [ { "id": 3, "name": "...", "slug": "...", "logo": "..." } ],
  "success": true, "status": 200
}
```

عنصر المنتج كامل — **بيانات الخصم متضمَّنة**:

```json
{
  "id": 4824, "slug": "...", "name": "شاحن حائط Anker USB-C GaN",
  "thumbnail_image": "https://...", "main_price": "795,000 ل.س",
  "stroked_price": "883,000 ل.س", "has_discount": true, "discount": "-10%",
  "usd_price": 0, "rating": 0, "sales": 5, "is_wholesale": false,
  "links": { "details": "..." }
}
```

- الحدود: 6 keywords / 5 products / 3 categories / 3 brands.
- `q` فارغ ⇒ مصفوفات فارغة مع `success: true` (ليس خطأ).
- السيرفر يكاشي حسب أول 10 أحرف — الـ debounce (400ms) وإلغاء الردود القديمة (sequence token) مسؤوليتكم كما في الخطة.
- استخدموا **نفس** موديل `Product` الحالي لعرضها — بما فيه السعر المشطوب وشارة الخصم، تماماً كبطاقة المنتج في النتائج.

## 3) POST `/api/v2/search/track`

دفعة أحداث (batch) — أرسلوا كل 20 ثانية أو عند إغلاق الشاشة. fire-and-forget: فشله لا يجب أن يظهر للمستخدم.

```json
{
  "temp_user_id": "uuid-للضيف",
  "platform": "android",
  "events": [
    { "event_type": "search_submitted", "query": "nike", "timestamp": "2026-08-24T12:01:00Z" },
    { "event_type": "search_product_clicked", "query": "nike", "target_id": 10, "timestamp": "..." }
  ]
}
```

- `event_type` أحد: `search_submitted` | `search_suggestion_clicked` | `search_product_clicked` | `search_category_clicked` | `search_brand_clicked`.
- `target_id` مطلوب منطقياً لأحداث النقر على منتج/فئة/ماركة (id الهدف).
- إن كان المستخدم مسجلاً أرسلوا توكن Sanctum المعتاد في الهيدر — السيرفر يلتقط `user_id` منه تلقائياً؛ وإلا `temp_user_id`.
- القيود: 50 حدث كحد أقصى بالطلب، `query` حتى 191 حرف، throttle 60 طلب/دقيقة. الرد عند الرفض: 422 مع `errors`.
- `timestamp` يُقبل لكن السيرفر يسجل وقته الخاص — لا تعتمدوا عليه.

## 4) GET `/api/v2/filter/options?name={نص}` — فلاتر سياقية

يرجع فقط الفئات والماركات والمقاسات والألوان ومدى السعر **الموجودة فعلاً** في نتائج هذه الكلمة — بدل قوائم عامة ثابتة. لا auth مطلوب.

Query params اختيارية إضافية (لتضييق الخيارات مع تراكم الفلاتر): `categories`, `brands`, `min`, `max` — بنفس صيغة `/products/search`.

```json
{
  "data": {
    "categories": [ { "id": 12, "name": "حقائب مدرسية", "slug": "school-bags", "product_count": 34 } ],
    "brands":     [ { "id": 5, "name": "Comfort", "slug": "comfort", "logo": "https://...", "product_count": 12 } ],
    "sizes":      ["S", "M", "L"],
    "colors":     [ { "name": "أسود", "code": "#000000" } ],
    "price":      { "min": 800, "max": 2690 }
  },
  "success": true, "status": 200
}
```

- `name` فارغ ⇒ خيارات المتجر كاملة (ليس خطأ).
- `price.min/max` بوحدة `unit_price` الخام — **نفس الوحدة** التي يفلتر بها `min`/`max` في `/products/search`، فمرّرها كما هي دون تحويل.
- `categories` و`brands` مرتبة تنازلياً حسب `product_count`، وحد أقصى 30 لكلٍ منهما.
- `sizes` مرتبة بترتيب الأدمن المعرَّف في لوحة التحكم لا بترتيب الاكتشاف.
- الرد مكاشى 5 دقائق على السيرفر حسب اللغة وكل الـ params.

## 5) توسعة `/products/search` — فلترة بالمقاس واللون

بارامترَان جديدان، كلاهما قوائم مفصولة بفواصل:

```
GET /products/search?name=قميص&sizes=M,L&colors=%23FF0000,%23000000
```

- `sizes` — القيم كما ترد في `filter/options.data.sizes` حرفياً.
- `colors` — أكواد الألوان كما ترد في `filter/options.data.colors[].code` (لا تنسَ ترميز `#` إلى `%23`).
- المنطق: **OR داخل نفس الفلتر** (M أو L)، و**AND بين الفلاتر** (مقاس M **و** لون أحمر) — نفس سلوك بقية الفلاتر.
- بقية البارامترات (`page`, `sort_key`, `brands`, `categories`, `min`, `max`) بلا تغيير.

## 6) توحيد الإملاء العربي (سلوك جديد في كل نقاط البحث)

المطابقة تتم على صيغة موحّدة للحرف، فلا فرق بين `ة`/`ه`، و`أ إ آ ٱ`/`ا`، و`ى`/`ي`، ويُتجاهل التطويل `ـ`:

- `حقيبه` تعيد نفس نتائج `حقيبة` (268 منتجاً في كلتيهما)، و`احذية` = `أحذية`.
- ينطبق على: `/products/search`، `/search/suggestions`، `/filter/options`، وبحث الويب — و`product_count` في `filter/options` يبقى مطابقاً لعدد نتائج `/products/search` لنفس الكلمة.
- **لا تطبّقوا توحيداً من طرفكم**: أرسلوا نص المستخدم كما كتبه. والنص المعروض في `keywords` يبقى بإملائه الصحيح المخزَّن (من يكتب `حقيبه` يرى الاقتراح `حقيبة`).

## الموجود بلا تغيير (يُستخدم كما هو)

- `GET /products/search?page=&name=&sort_key=&brands=&categories=&min=&max=` — النتائج والفلاتر والفرز (+ `sizes`/`colors` الجديدين أعلاه).
- `GET /filter/categories` و `GET /filter/brands` — القوائم العامة؛ ما زالت تعمل، لكن `filter/options` أفضل منها داخل نتائج بحث.
- `GET /get-search-suggestions` — القديم، سيبقى للنسخ المنشورة؛ الشاشة الجديدة تستخدم `/search/suggestions`.
