# Public Site Vehicles Split Export Task

## Goal

Перестроить public export для vehicles так, чтобы source of truth остался в backend vehicle public-localization layer + route registry, но фронт больше не получал монолитные:

- `generated/site/vehicles/index.js`
- `generated/site/vehicles/by-slug.js`

со всеми locale detail payload внутри.

Нужен split-export формат, где:

- listing грузится только для текущей locale
- detail грузится только для текущей locale и конкретной vehicle entity
- route/SEO truth хранится отдельно в маленьком registry-модуле

## Why

Сейчас vehicle export раздувает generated output:

- каждый vehicle detail entity дублирует `availableLocales` + `localized[]`
- `vehicles/index.js` тянет весь multi-locale dataset
- `vehicles/by-slug.js` тянет giant locale lookup map со всеми detail payload сразу

Это ломает размер snapshot, ухудшает chunking и заставляет фронт импортить намного больше данных, чем реально нужно для текущей страницы.

## Hard Rules

- Не использовать `content_items` для vehicle localization.
- Использовать только existing vehicle public-localization layer и route registry.
- Не терять current SEO truth:
  - `path`
  - `href`
  - `canonical`
  - `hreflang`
  - `redirect_from`
  - `availableLocales`
  - `localized[]`
  - `x_default`
- Не дублировать полный multi-locale payload в каждом entity.
- Фронт должен иметь возможность загрузить:
  - только current locale listing
  - только exact current locale detail
- Если detail для locale нет, фронт должен получить exact EN/default detail path из registry.
- Не допускать фронтовой самогенерации vehicle URLs.
- Сохранить совместимость данных для current public pages, но убрать giant all-locales blobs из generated output.

## Current Backend Touchpoints

Текущая логика собирается здесь:

- `backend/app/Support/PublicSite/PublicSiteExportService.php`
- `backend/app/Support/PublicSite/PublicSiteEsmWriter.php`
- `backend/app/Support/PublicSite/PublicRouteCatalogService.php`

Текущий монолитный vehicle export пишется здесь:

- `generated/site/vehicles/index.js`
- `generated/site/vehicles/by-slug.js`

Сейчас `PublicSiteExportService` уже строит vehicle localization variants через existing vehicle public-localization layer, и это нужно сохранить.

## Required Output Structure

Вместо текущего giant export нужен split format:

```text
generated/site/
  vehicles/
    routes.js
    index/
      en.js
      fr.js
      es.js
      ru.js
      de.js
      ka.js
      it.js
      ar.js
      he.js
      pl.js
      tr.js
    detail/
      <contentKey-or-id>/
        en.js
        fr.js
        es.js
        ru.js
        de.js
        ka.js
        it.js
        ar.js
        he.js
        pl.js
        tr.js
```

### 1. `vehicles/routes.js`

Это маленький registry module без тяжелого detail payload.

Он должен хранить только route truth и identity:

- `contentKey`
- `entityId`
- `path`
- `availableLocales`
- `localized[]`
- `canonical`
- `redirect_from`
- `x_default`

Допустимо добавить еще:

- `defaultLocale`
- `defaultPath`
- `defaultHref`

Но нельзя тащить сюда:

- `longDescription`
- `marketingHighlights`
- `features`
- `gallery`
- `reviews`
- `includedInPrice`
- `conditionReport`
- любые другие тяжелые detail поля

### 2. `vehicles/index/<locale>.js`

Это listing payload только для одной locale.

Пример:

```js
export const vehiclesPage = { ... }
export const fleetCategories = [ ... ]
export const fleetGalleryImages = [ ... ]
export const fleetUseCases = [ ... ]
export const vehicleItems = [ ... ]
export const vehiclesIndex = [ ... ]
```

Но только для `current locale`.

В listing entity допускаются lightweight route fields:

- `contentKey`
- `entityId`
- `locale`
- `path`
- `href`
- `availableLocales`

Не нужно тащить полный `localized[]` graph в каждом item, если он уже есть в `vehicles/routes.js`.

### 3. `vehicles/detail/<contentKey-or-id>/<locale>.js`

Это exact locale detail payload для одной vehicle entity.

Пример:

```js
export const vehicleDetail = { ... }
```

Только одна entity и только одна locale.

Допустимо внутри detail оставить:

- current locale route fields
- current locale SEO
- current locale marketing content
- needed transactional presentation fields

Не нужно дублировать в detail весь multi-locale heavy payload.

`availableLocales` и `localized[]` можно оставить lightweight, если они нужны detail page для locale switcher, но их canonical source должен быть route registry.

## Registry Contract

`vehicles/routes.js` должен давать фронту достаточно данных, чтобы:

1. найти entity по:
   - `contentKey`
   - `entityId`
   - localized path
2. получить exact locale path, если locale существует
3. получить exact EN/default path, если locale detail отсутствует
4. построить:
   - canonical
   - hreflang links
   - redirect resolution

Пример минимальной shape:

```js
export const vehicleRouteRegistry = [
  {
    contentKey: 'vehicle_range_rover_3g',
    entityId: 123,
    defaultLocale: 'en',
    path: '/vehicles/range-rover-3g',
    href: 'https://bent.ge/vehicles/range-rover-3g',
    canonical: 'https://bent.ge/vehicles/range-rover-3g',
    x_default: 'https://bent.ge/vehicles/range-rover-3g',
    availableLocales: ['en', 'fr', 'ru'],
    localized: [
      {
        locale: 'en',
        path: '/vehicles/range-rover-3g',
        href: 'https://bent.ge/vehicles/range-rover-3g',
        slug: 'vehicles/range-rover-3g',
        publicSlug: 'vehicles/range-rover-3g',
        title: 'Range Rover 3G',
      },
      {
        locale: 'fr',
        path: '/fr/vehicules/range-rover-3g',
        href: 'https://bent.ge/fr/vehicules/range-rover-3g',
        slug: 'fr/vehicules/range-rover-3g',
        publicSlug: 'fr/vehicules/range-rover-3g',
        title: 'Range Rover 3G',
      }
    ],
    redirect_from: [
      '/vehicles/range-rover-3g-old'
    ],
  }
]
```

## Backend Implementation Requirements

### 1. Keep current source-of-truth layering

Do not move vehicle localization into `content_items`.

Use current backend layering:

- vehicle public-localization rows
- route catalog / route registry
- existing SEO path logic

### 2. Split writer logic in `PublicSiteEsmWriter`

Replace current monolithic vehicle modules with:

- `vehicles/routes.js`
- `vehicles/index/<locale>.js`
- `vehicles/detail/<contentKey-or-id>/<locale>.js`

### 3. Keep payload builder clean

`PublicSiteExportService` should still build normalized vehicle payloads.

Но writer не должен просто dump-ить один giant array.
Нужно нормализовать export into:

- route registry dataset
- listing datasets by locale
- detail datasets by locale + entity

### 4. Stable identity

Every vehicle must have stable backend identity usable in export:

- `contentKey`
- `entityId`

`detail/<contentKey-or-id>/...` должен использовать один канонический ключ.
Предпочтительно `contentKey`, если он уже стабилен и используется фронтом.

### 5. No frontend URL guessing

Backend route registry must expose exact paths for:

- existing locale
- default locale
- x-default
- redirects

Фронт не должен склеивать:

- `/${locale}/...`
- slug segments
- fallback route paths

самостоятельно.

## Backward Compatibility Requirement

Не ломать текущие public pages по shape резко.

Допустимо:

- добавить новый split export
- временно оставить current modules как compatibility layer

Но target state должен быть такой:

- frontend больше не зависит от giant all-locales `vehicles/index.js`
- frontend больше не зависит от giant all-locales `vehicles/by-slug.js`

Если нужен переходный этап:

1. backend генерирует новый split format
2. frontend переходит на него
3. старые vehicle modules удаляются после переключения

## Acceptance Criteria

### Data correctness

- Vehicle localization остается в existing backend public-localization layer.
- `content_items` не участвует в vehicle localization.
- `vehicles/routes.js` содержит полный route/SEO truth без тяжелого detail payload.
- `vehicles/index/<locale>.js` содержит только current locale listing payload.
- `vehicles/detail/<contentKey-or-id>/<locale>.js` содержит только current locale detail payload.
- Для missing locale detail registry отдает exact EN/default path.

### SEO correctness

- Не теряются:
  - `canonical`
  - `hreflang`
  - `x_default`
  - `redirect_from`
  - `availableLocales`
  - `localized[]`
- Existing localized vehicle URLs остаются стабильными.

### Performance / size

- `vehicles/index.js` giant all-locales blob больше не нужен как primary source.
- `vehicles/by-slug.js` giant all-locales lookup больше не нужен как primary source.
- Generated output size для vehicle export заметно уменьшается.
- Frontend imports only current locale listing and exact detail locale file.

### Operational

- `php artisan site:export --format=esm --path=...` генерирует новый split structure.
- Export deterministic:
  - stable key order
  - stable array order
  - stable file layout

## Deliverables

1. Backend code changes in:
   - `PublicSiteExportService`
   - `PublicSiteEsmWriter`
   - related route/registry helpers if needed
2. Generated file structure according to this task
3. Short implementation note:
   - what changed
   - chosen detail key (`contentKey` or `entityId`)
   - what temporary compatibility layer remains
4. Smoke-check result from real export:

```bash
php artisan site:export --format=esm --path=/some/test/path
```

Must confirm that generated output includes:

- `vehicles/routes.js`
- `vehicles/index/en.js`
- `vehicles/index/fr.js`
- `vehicles/detail/<key>/en.js`

and no longer depends on giant all-locales vehicle blobs as the primary format.
