# Plan refaktoryzacji frontendu shopPRO

## Kontekst

Panel administratora (33 moduły) został w pełni zmigrowany na architekturę Domain + DI + Controllers (ver. 0.237–0.277). Teraz kolej na frontend: klasy `front/`, `shop/`, `cms/`, klasy utility z `autoload/` oraz szablony `templates/`.

**Cel:** przenieść logikę biznesową z warstwy frontendowej do warstwy domenowej (`Domain/`), zachowując kompatybilność wsteczną przez fasady. Zastosować te same wzorce co w adminie (DI, repozytoria/serwisy, testy PHPUnit).

---

## Inwentaryzacja — co istnieje

### front/controls/ (8 klas — handlery requestów)
| Klasa | Status | Logika biznesowa |
|-------|--------|-----------------|
| Site | Router główny | route(), check_url_params(), title() |
| ShopBasket | MIXED | Operacje koszyka, add/remove/quantity, checkout |
| ShopClient | Fasada | Deleguje do factory |
| ShopOrder | KRYTYCZNY | Webhooki płatności (tPay, Przelewy24, Hotpay) — bezpośrednie operacje DB |
| ShopProduct | Fasada | lazy_loading, warehouse_message, draw_product_attributes |
| ShopProducer | Fasada | list(), products() |
| ShopCoupon | Fasada | use_coupon(), delete_coupon() |
| Newsletter | Fasada | signin(), confirm(), unsubscribe() |

### front/factory/ (20 klas — pobieranie danych + logika)
| Klasa | Status | Priorytet migracji |
|-------|--------|--------------------|
| ShopProduct | ORYGINALNA LOGIKA (~370 linii) | KRYTYCZNY — product_details(), promoted/top/new products |
| ShopOrder | ORYGINALNA LOGIKA (~180 linii) | KRYTYCZNY — basket_save() tworzy zamówienie |
| ShopClient | ORYGINALNA LOGIKA | KRYTYCZNY — login (BUG: hardcoded bypass 'Legia1916'), signup, recover |
| ShopCategory | ORYGINALNA LOGIKA | WYSOKI — złożone SQL z language fallback |
| Articles | ORYGINALNA LOGIKA | WYSOKI — złożone SQL z language fallback |
| ShopPromotion | ORYGINALNA LOGIKA | WYSOKI — silnik promocji (5 typów) |
| ShopBasket | Fasada | ŚREDNI — summary_price, count |
| ShopTransport | CZĘŚCIOWO zmigrowana | ŚREDNI — transport_methods z filtrowaniem |
| ShopPaymentMethod | ZMIGROWANA (Domain) | — |
| ShopStatuses | ZMIGROWANA (Domain) | — |
| Scontainers | ZMIGROWANA (Domain) | — |
| Newsletter | ZMIGROWANA (Domain) — usunięta | — |
| Settings | Fasada (BUG: get_single_settings_value ignoruje $param) | NISKI |
| Languages | USUNIĘTA — przepięta na Domain | — |
| Layouts | Fasada | NISKI |
| Banners | USUNIETA — przepieta na Domain | — |
| Menu | Fasada | NISKI |
| Pages | Fasada | NISKI |
| ShopAttribute | Fasada | NISKI |
| ShopCoupon | Model danych | NISKI |

### front/view/ (12 klas — renderowanie)
| Klasa | Status |
|-------|--------|
| Site | KRYTYCZNY — show() ~600 linii, pattern substitution engine |
| ShopCategory | VIEW z logiką routingu (infinite scroll vs pagination) |
| Articles, Menu, Scontainers | Czyste VIEW |
| Banners | PRZENIESIONA do `front\Views\Banners` |
| Languages, Newsletter | PRZENIESIONE do `front\Views\` (nowy namespace) |
| ShopClient, ShopOrder, ShopPaymentMethod | Czyste VIEW |
| ShopTransport | PUSTA klasa (placeholder) |

### shop/ (14 klas — encje biznesowe)
| Klasa | Linii | Status | Priorytet |
|-------|-------|--------|-----------|
| Product | ~950 | KRYTYCZNY — pricing, atrybuty, search, cache, permutacje | KRYTYCZNY |
| Order | ~500+ | KRYTYCZNY — statusy, Apilo sync, emaile, webhooki | KRYTYCZNY |
| Promotion | ~200 | WYSOKI — matching aktywnych promocji + delegacja do factory | WYSOKI |
| Basket | ~80 | ŚREDNI — walidacja stanów magazynowych | ŚREDNI |
| Category | ~60 | NISKI — proste zapytania, pusty konstruktor | NISKI |
| Search | ~80 | NISKI — wrapper na szablony | NISKI |
| Coupon | ~60 | NISKI — niekompletne metody | NISKI |
| Transport | ~30 | NISKI — transport_list() | NISKI |
| PaymentMethod | — | ZMIGROWANA (fasada do Domain) | — |
| Producer | — | ZMIGROWANA (fasada do Domain) | — |
| ProductSet | — | ZMIGROWANA (fasada do Domain) | — |
| ProductAttribute | ~100 | OK — dobry caching | — |
| ProductCustomField | ~50 | OK — Redis caching | — |
| Shop | ~30 | OK — utility do cen | — |

### autoload/ root (klasy utility)
| Klasa | Linii | Status |
|-------|-------|--------|
| S | ~960 | PRZENIESIONA do Shared\Helpers\Helpers — 12 metod usunieto, bug fix |
| Tpl | ~90 | PRZENIESIONA do `Shared\Tpl\Tpl` — DRY render(), bug fix branch 3, ~135 plikow przepietych |
| CacheHandler | ~50 | ZMIGROWANY do `Shared\Cache\CacheHandler` — wrappery usuniete |
| RedisConnection | ~40 | ZMIGROWANY do `Shared\Cache\RedisConnection` — wrappery usuniete |
| Email | ~100 | OK — PHPMailer wrapper (drobne poprawki) |
| Log | ~20 | OK — audit logging |
| DbModel | — | USUNIETA — logika wbudowana w `shop\Promotion` |
| Cache | ~50 | USUNIETA — zastapiona CacheHandler (Redis) w ver. 0.282 |
| Html | ~80 | OK — form helpers |
| Image | ~100 | OK — GD wrapper |
| Mobile_Detect | — | USUNIETA — przestarzala detekcja UA, zastapiona responsive design |

### cms/ (1 klasa)
| Klasa | Status |
|-------|--------|
| Layout | BUG w __get() — referuje $this->data które nie istnieje |

### templates/ (75 plików w 15 modułach)
articles(8), banner(2), controls(1), menu(4), newsletter(2), scontainers(1), shop-basket(9), shop-category(6), shop-client(8), shop-coupon(1), shop-order(3), shop-producer(3), shop-product(12), shop-search(3), site(11)

### Entry points
- `index.php` — główny frontend (autoload, sesja, DB, routing, DOM post-processing)
- `ajax.php` — AJAX handler (koszyk, transport, kontakt)
- `api.php` — REST API (Ekomi CSV export)
- `download.php` — pobieranie plików
- `cron-turstmate.php` — TrustMate integracja

---

## Znane bugi do naprawy podczas refaktoringu

1. **KRYTYCZNY** `front\factory\ShopClient::login()` — hardcoded password bypass `'Legia1916'`
2. `front\factory\Settings::get_single_settings_value()` — ignoruje `$param`, zawsze zwraca `firm_name`
3. ~~`front\factory\Newsletter::newsletter_unsubscribe()` — błędna składnia SQL w delete~~ **NAPRAWIONE** — `NewsletterRepository::unsubscribe()` z poprawną składnią medoo `delete()`
4. `cms\Layout::__get()` — referuje nieistniejące `$this->data`
5. `shop\Search` — typo w use: `shop\Produt` (brak 'c')

---

## Kolejność migracji (graf zależności)

```
Settings, Languages (liście — brak zależności)
    ↓
Banners, Menu, Pages, Articles, Layouts (zależą od Languages)
    ↓
Category (zależy od Languages)
    ↓
Promotion (zależy od Category — rozbicie circular dep)
    ↓
Product Frontend (zależy od Category, Promotion, Languages)
    ↓
Client/Auth (standalone + security fix)
    ↓
Transport, Payment, Coupon (frontend serwisy)
    ↓
Basket (zależy od Product, Promotion, Transport, Coupon)
    ↓
Order Creation (zależy od Basket, Product, Transport, Payment)
    ↓
Payment Webhooks (zależy od Order)
    ↓
shop\Order instance + Apilo (zależy od Order Operations)
    ↓
shop\Product instance + cache (zależy od ProductFrontendService)
    ↓
Frontend App + Controllers (nowa warstwa DI)
    ↓
Site Layout Engine (zależy od wszystkiego)
    ↓
Entry Point Unification (index.php, ajax.php)
    ↓
Legacy Cleanup
```

---

## Etapy migracji

### Etap: Settings + Languages — ZREALIZOWANY

**Cel:** Dodac metody frontendowe (z cache Redis) do istniejacych repozytoriow Domain.

**DODANE METODY (do istniejacych klas):**
- `Domain/Settings/SettingsRepository` — `allSettings($skipCache)`, `getSingleValue($param)` (FIX: uzywa poprawnie $param)
- `Domain/Languages/LanguagesRepository` — `defaultLanguage()`, `activeLanguages()`, `translations($lang)`
- Testy: dopisane do `SettingsRepositoryTest` (6 testow), `LanguagesRepositoryTest` (7 testow)

**ZMIANA:**
- `front/factory/Settings` → fasada delegujaca do `SettingsRepository`
- `front/factory/Languages` → fasada delegujaca do `LanguagesRepository`
- `tests/bootstrap.php` — uzupelniony stub CacheHandler o `get()`/`set()`/`exists()`

**BUG FIX:** `get_single_settings_value()` — zmiana `['param' => 'firm_name']` na `['param' => $param]`

---

### Etap: Newsletter Frontend — ZREALIZOWANY

**Cel:** Przeniesienie logiki frontendowej z `front\factory\Newsletter` do `Domain\Newsletter\NewsletterRepository`. Migracja view do nowego namespace `front\Views`.

**DODANE METODY (do istniejącej klasy `NewsletterRepository`):**
- `unsubscribe(string $hash): bool` — FIX: poprawna składnia medoo `delete()` (2 args zamiast 3)
- `confirmSubscription(string $hash): bool`
- `getHashByEmail(string $email): ?string`
- `removeByEmail(string $email): bool`
- `signup(string $email, string $serverName, bool $ssl, array $settings): bool`
- `sendQueued(int $limit, string $serverName, bool $ssl, string $unsubscribeLabel): bool`
- Konstruktor rozszerzony o opcjonalne: `ArticleRepository`, `NewsletterPreviewRenderer` (lazy-init)
- Testy: 10 nowych testów w `NewsletterRepositoryTest`

**ZMIANA:**
- `front/factory/Newsletter` → USUNIĘTA (logika przeniesiona do `NewsletterRepository`)
- `front/view/Newsletter` → USUNIĘTA, zastąpiona przez `front/Views/Newsletter` (nowy namespace, bez `class.` prefix)
- `front/controls/Newsletter` → thin wrapper na `NewsletterRepository`
- `front/view/Site::show()` → `\front\Views\Newsletter::render()`
- `index.php` → `$newsletterRepo->sendQueued()`
- `front/factory/ShopClient` (4 miejsca) → `NewsletterRepository::templateByName()`
- `tests/bootstrap.php` — dodane stuby: `S::email_check()`, `S::get_session()`, `S::set_session()`

**BUG FIX:** `newsletter_unsubscribe()` — medoo `delete()` wywoływane z 3 argumentami zamiast 2

---

### Etap: Category Frontend Service

**Cel:** Migracja `front\factory\ShopCategory` do Domain.

**NOWE:**
- `Domain/Category/CategoryFrontendService.php` — `getCategorySort()`, `categoryName()`, `categoryUrl()`, `blogCategoryProducts()`, `categoryProducts()`, `productsId()` (złożone SQL z sortowaniem/language fallback/paginacją), `categoryDetails()`, `categoriesDetails()` (rekurencyjne), `categoryProductsCount()`
- Testy: `CategoryFrontendServiceTest`

**ZMIANA:**
- `front/factory/ShopCategory` → fasada
- `front/factory/ShopProduct::product_categories()` → deleguje do `CategoryFrontendService`

---

### Etap: Articles Frontend — ZREALIZOWANY

**Cel:** Migracja `front\factory\Articles`, `front\view\Articles` i statycznych metod `class.Article` do Domain + Views.

**DODANE METODY (do istniejącej klasy `ArticleRepository`):**
- `articleDetailsFrontend(int $articleId, string $langId): ?array` — z copy_from fallback + Redis cache
- `articlesIds(int $pageId, string $langId, int $limit, int $sortType, int $from): ?array` — złożone SQL z language fallback + sortowanie + LIMIT + Redis cache
- `pageArticlesCount(int $pageId, string $langId): int` — COUNT z Redis cache
- `pageArticles(array $page, string $langId, int $bs): array` — paginacja
- `news(int $pageId, int $limit, string $langId): ?array` — inline sort_type query (eliminacja zależności od `front\factory\Pages`)
- `articleNoindex(int $articleId, string $langId): bool` — jawny $langId zamiast `global $lang`
- `topArticles(int $pageId, int $limit, string $langId): ?array` — ORDER BY views DESC + Redis cache
- `newsListArticles(int $pageId, int $limit, string $langId): ?array` — ORDER BY date_add DESC + CacheHandler (Redis) zamiast legacy `\Cache`

**NOWE:**
- `front\Views\Articles` — czysty VIEW + utility:
  - Renderowanie: `fullArticle()`, `miniatureArticlesList()`, `entryArticlesList()`, `fullArticlesList()`, `news()`, `newsList()`
  - Utility: `generateTableOfContents()`, `processHeaders()`, `generateHeadersIds()`, `getImage()`

**ZMIANA:**
- `front\factory\Articles` → fasada (10 metod delegujących do repo + Views)
- `front\view\Articles` → fasada (5 metod delegujących do repo + Views)
- `class.Article` → USUNIĘTA (encja + metody statyczne przeniesione do `ArticleRepository` + `front\Views\Articles`)
- `front\view\Site::show()` → 5 sekcji przepiętych na repo + Views
- `front\controls\Site::route()` → single article + page_type switch przepięte na repo + Views
- 5 szablonów `templates/articles/*` → `\front\Views\Articles::`
- `tests/bootstrap.php` — dodany stub `S::is_array_fix()`
- Testy: 13 nowych w `ArticleRepositoryTest` (450 OK, 1431 asercji)

---

### Etap: Banners Frontend — ZREALIZOWANY

**Cel:** Migracja `front\factory\Banners` i `front\view\Banners` do Domain + Views.

**DODANE METODY (do istniejącej klasy `BannerRepository`):**
- `banners(string $langId): ?array` — aktywne banery (home_page=0), filtrowanie dat, Redis cache, plaski format languages
- `mainBanner(string $langId): ?array` — baner glowny (home_page=1), filtrowanie dat, Redis cache, plaski format languages

**NOWE:**
- `front\Views\Banners` — czysty VIEW (`banners()`, `mainBanner()`)

**ZMIANA:**
- `front\factory\Banners` → USUNIETA (logika przeniesiona do `BannerRepository`)
- `front\view\Banners` → USUNIETA (zastapiona przez `front\Views\Banners`)
- `front\view\Site::show()` — przepiecie na `$bannerRepo->banners()` / `$bannerRepo->mainBanner()` + `\front\Views\Banners::`
- Testy: 4 nowe w `BannerRepositoryTest` (454 OK, 1449 asercji)

---

### Etap: Menu, Pages, Layouts Frontend Services

**Cel:** Migracja pozostałych fabryk "liściowych".

**NOWE:**
- `Domain/Menu/MenuFrontendService.php` — `menuDetails()`, `menuPages()` (rekurencja)
- `Domain/Pages/PagesFrontendService.php` — `pageDetails()`, `mainPageId()`, `langUrl()`, `pageSort()`
- `Domain/Layouts/LayoutsFrontendService.php` — `activeLayout()`, `articleLayout()`, `productLayout()`, `categoryLayout()`, `defaultLayout()`, `categoryDefaultLayout()`
- Testy: 3 pliki testowe

**ZMIANA:**
- `front/factory/Menu`, `Pages`, `Layouts` → fasady

**BUG FIX:** `cms\Layout::__get()` — poprawka referencji do `$this->data`

---

### Etap: Promotion Engine (rozbicie circular dependency)

**Cel:** Przeniesienie silnika promocji do Domain. Rozbicie cyklicznej zależności `shop\Promotion ↔ front\factory\ShopPromotion`.

**Obecna zależność cykliczna:**
```
shop\Promotion::find_promotion() → front\factory\ShopPromotion::promotion_type_XX()
front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on_promotion()
```

**Rozwiązanie:** Wszystko w jednym serwisie `PromotionFrontendService`.

**NOWE:**
- `Domain/Promotion/PromotionFrontendService.php`:
  - `getActivePromotions()` (z `shop\Promotion`)
  - `applyPromotions(array $basket)` (z `shop\Promotion::find_promotion()`)
  - `private applyType01..05()` (z `front\factory\ShopPromotion`)
  - `private isProductOnPromotion()` (z `shop\Product` — proste zapytanie)
- Testy: `PromotionFrontendServiceTest` (7 typów promocji + brak aktywnych)

**ZMIANA:**
- `shop/Promotion` → fasada do `PromotionFrontendService::applyPromotions()`
- `front/factory/ShopPromotion` → fasada

---

### Etap: Product Frontend Service

**Cel:** Migracja `front\factory\ShopProduct` i statycznych metod `shop\Product` do Domain.

**NOWE:**
- `Domain/Product/ProductFrontendService.php`:
  - Z `front\factory\ShopProduct`: `productDetails()` (~330 linii z Redis), `promotedProducts()`, `topProducts()`, `newProducts()`, `productUrl()`, `getMinimalPrice()`, `isProductActive()`, `getProductSku()`, `getProductEan()`, `productImage()`, `productWp()`, `randomProducts()`, `warehouseMessageZero()`, `warehouseMessageNonzero()`
  - Z `shop\Product` (statyczne): `isProductOnPromotion()`, `getProductImg()`, `getProductUrl()`, `addVisit()`, `productSetsForBasket()`, `searchProductsByName()`, `searchProductByNameAjax()`
- Testy: `ProductFrontendServiceTest`

**ZMIANA:**
- `front/factory/ShopProduct` → fasada
- `shop/Product` statyczne metody → fasady do `ProductFrontendService`

**UWAGA:** Konstruktor `shop\Product`, `getFromCache()`, `calculate_basket_product_price()` — zostają na razie (instancyjne, używane w szablonach). Migracja w etapie "Product Instance + Cache".

---

### Etap: Client Authentication (Security Fix)

**Cel:** Migracja `front\factory\ShopClient` + NAPRAWIENIE hardcoded password bypass.

**NOWE:**
- `Domain/Client/ClientFrontendService.php`:
  - `login()` — **BEZ** bypassa 'Legia1916', z opcjonalną migracją md5 → password_hash
  - `signup()`, `registerConfirm()`, `sendPasswordRecovery()`, `resetPassword()`
  - `clientDetails()`, `clientOrders()`
  - CRUD adresów: `saveAddress()`, `deleteAddress()`, `getAddresses()`, `markAddressAsCurrent()`
- Testy: `ClientFrontendServiceTest` — **KRYTYCZNY test: login z 'Legia1916' NIE przechodzi**

**ZMIANA:**
- `front/factory/ShopClient` → fasada
- `front/controls/ShopClient` → deleguje do serwisu

---

### Etap: Transport, Payment, Coupon Frontend Services

**Cel:** Frontend serwisy dla transportu, płatności i kuponów.

**NOWE:**
- `Domain/Transport/TransportFrontendService.php` — `transportMethods()` (filtrowanie WP, free delivery), `transportCost()`, `transportDetails()`
- `Domain/PaymentMethod/PaymentMethodFrontendService.php` — `activePaymentMethods()`, `paymentMethodsByTransport()`, `paymentMethodDetails()`
- `Domain/Coupon/CouponFrontendService.php` — `validateCoupon()`, `applyCoupon()`, `markCouponUsed()`
- Testy: 3 pliki testowe

**ZMIANA:**
- `front/factory/ShopTransport` → fasada
- `front/factory/ShopCoupon` → fasada
- `shop/Coupon` → fasada
- `shop/Transport` → fasada

---

### Etap: Basket Service

**Cel:** Migracja logiki koszyka do `Domain\Basket\BasketService`.

**NOWE:**
- `Domain/Basket/BasketService.php`:
  - `addProduct()`, `removeProduct()`, `changeQuantity()`, `increaseQuantity()`, `decreaseQuantity()`
  - `summaryPrice()`, `countProducts()`, `countProductsText()`
  - `validateStock()` (z `shop\Basket::check_product_quantity_in_stock()`)
  - `calculateProductPrice()` (z `shop\Product::calculate_basket_product_price()` ~112 linii)
- Testy: `BasketServiceTest` (pricing z promocjami/kuponami, walidacja stanów)

**ZMIANA:**
- `front/factory/ShopBasket` → fasada
- `front/controls/ShopBasket` — wewnętrznie używa `BasketService`
- `shop/Basket` → fasada
- `shop/Product::calculate_basket_product_price()` → fasada

---

### Etap: shop\Product Instance + Cache

**Cel:** Refaktoring konstruktora `shop\Product` i `getFromCache()`.

**NOWE:**
- `Domain/Product/ProductDataLoader.php`:
  - `loadFull(int $productId, ?string $langId, ?string $permutationHash)` — ładowanie pełnych danych produktu
  - `loadCached()` — Redis cache wrapper
- Testy: `ProductDataLoaderTest`

**ZMIANA:**
- `shop/Product` — konstruktor i `getFromCache()` delegują do `ProductDataLoader`
- ArrayAccess interface zachowany (szablony dalej używają `$product->property`)

---

### Etap: Order Creation Frontend Service

**Cel:** Migracja `front\factory\ShopOrder::basket_save()` (~180 linii).

**NOWE:**
- `Domain/Order/OrderFrontendService.php`:
  - `createOrder()` — tworzenie zamówienia z koszyka (walidacja, kalkulacja cen, insert, redukcja stanów, obsługa kuponu, wysyłka emaili, auto-status dla pobrania)
  - `generateOrderNumber()` — format YYYY/MM/NNN
  - `orderDetails()`, `orderIdByHash()`, `orderHashById()`
- Testy: `OrderFrontendServiceTest`

**ZMIANA:**
- `front/factory/ShopOrder` → fasada

---

### Etap: Payment Webhook Service

**Cel:** Wyodrębnienie webhooków płatności z `front\controls\ShopOrder`.

**NOWE:**
- `Domain/Payment/PaymentWebhookService.php`:
  - `processTpay(array $params)` — weryfikacja tPay
  - `processPrzelewy24(array $params)` — weryfikacja przez API + walidacja kwoty
  - `processHotpay(array $params)` — walidacja SHA256 hash
  - `private markOrderPaid()` — wspólna logika (update status + email + Apilo sync)
- Testy: `PaymentWebhookServiceTest`

**ZMIANA:**
- `front/controls/ShopOrder` — webhooki stają się thin wrappers

**POPRAWA:** Zamiana `file_put_contents('tpay.txt')` na `\Log::save_log()`

---

### Etap: shop\Order Instance + Apilo Service

**Cel:** Refaktoring `shop\Order` instancyjnych metod + wyodrębnienie integracji Apilo.

**NOWE:**
- `Domain/Integrations/ApiloService.php`:
  - `syncPayment()`, `syncStatus()`, `processQueue()`
  - `private queueSync()`, `private loadQueue()`, `private saveQueue()`
- `Domain/Order/OrderOperationsService.php`:
  - `setAsPaid()`, `setAsUnpaid()`, `updateStatus()`, `sendStatusChangeEmail()`, `resendConfirmationEmail()`
- Testy: `ApiloServiceTest`, `OrderOperationsServiceTest`

**ZMIANA:**
- `shop/Order` instancyjne metody → fasady do `OrderOperationsService`
- Konstruktor i ArrayAccess bez zmian

---

### Etap: Frontend App + Controllers (DI layer)

**Cel:** Stworzenie `front\App` (wzorowanego na `admin\App`) z mapą kontrolerów i DI.

**NOWE:**
- `autoload/front/App.php` — `render()`, `handleAjax()`, `getControllerFactories()` (DI wiring)
- `autoload/front/Controllers/ShopBasketController.php` — DI z `BasketService`, `ProductFrontendService`
- `autoload/front/Controllers/ShopOrderController.php` — DI z `OrderFrontendService`, `PaymentWebhookService`
- `autoload/front/Controllers/ShopClientController.php` — DI z `ClientFrontendService`
- Testy: 3 pliki testowe kontrolerów

**ZMIANA:**
- `front/controls/ShopBasket`, `ShopOrder`, `ShopClient` → delegują do nowych kontrolerów

---

### Etap: Site Layout Engine

**Cel:** Refaktoring `front\view\Site::show()` (~600 linii) na testowalny `LayoutEngine`.

**NOWE:**
- `autoload/front/View/LayoutEngine.php`:
  - `resolveLayout(array $page, array $params)` — wybór layoutu (product > category > article > page > default)
  - `processPatterns(string $html, array $context)` — zamiana tagów szablonowych
  - Osobne prywatne metody: `replaceCategories()`, `replaceProducts()`, `replaceMenus()`, `replaceLanguageTags()`, `replaceBanners()`, `replaceArticles()`, `replaceContainers()`, `replaceMetaTags()`, etc.
- Testy: `LayoutEngineTest`

**ZMIANA:**
- `front/view/Site::show()` → thin wrapper na `LayoutEngine`

---

### Etap: Entry Point Unification

**Cel:** Ujednolicenie bootstrapu `index.php` i `ajax.php`.

**NOWE:**
- `autoload/front/Bootstrap.php` — `init()` (autoloader, config, sesja, DB, lang/settings)
- `autoload/front/PostProcessor.php` — ekstrakcja DOM manipulacji z `index.php` (GTM, Facebook Pixel, WebP, lazy loading)

**ZMIANA:**
- `index.php` → uproszczony: `Bootstrap::init()` → `App::render()` → `PostProcessor::process()`
- `ajax.php` → uproszczony: `Bootstrap::init()` → `App::handleAjax()`

---

### Etap: Legacy Cleanup

**Cel:** Finalne porządki.

**USUNIĘCIE:**
- Martwy kod `eval()` dla `[PHP]` bloków (jeśli nieużywany)
- ~~`class.Cache.php` (legacy file-based)~~ **ZREALIZOWANE** w ver. 0.282
- Pusta klasa `front\view\ShopTransport`

**ZMIANA:**
- Dodanie `@deprecated` do wszystkich metod-fasad w `front/factory/`, `front/controls/`, `shop/`
- PHPDoc do wszystkich nowych klas Domain z `@since`
- Aktualizacja `tests/bootstrap.php`
- BUG FIX: `shop\Search` — typo `shop\Produt` → `shop\Product`
- ~~BUG FIX: `front\factory\Newsletter::newsletter_unsubscribe()` — poprawka SQL~~ **ZREALIZOWANE** w etapie Newsletter Frontend

---

## Podsumowanie

| Etap | Zakres | Priorytet | Nowe klasy Domain | Testy |
|------|--------|-----------|-------------------|-------|
| Settings + Languages | Fundamenty | FUNDAMENT | 2 serwisy | 2 |
| Category Frontend | Kategorie | WYSOKI | 1 serwis | 1 |
| Banners/Menu/Pages/Articles/Layouts | Treści | ŚREDNI | 5 serwisów | 5 |
| Promotion Engine | Promocje | KRYTYCZNY | 1 serwis | 1 |
| Product Frontend | Produkty | KRYTYCZNY | 1 serwis | 1 |
| Client/Auth (security fix) | Klienci | KRYTYCZNY | 1 serwis | 1 |
| Transport/Payment/Coupon | Dostawa/Płatności | WYSOKI | 3 serwisy | 3 |
| Basket Service | Koszyk | WYSOKI | 1 serwis | 1 |
| Product Instance + Cache | Produkt cache | ŚREDNI | 1 loader | 1 |
| Order Creation | Zamówienia | WYSOKI | 1 serwis | 1 |
| Payment Webhooks | Webhooki | WYSOKI | 1 serwis | 1 |
| Order Instance + Apilo | Zamówienie + Apilo | ŚREDNI | 2 serwisy | 2 |
| Frontend App + Controllers | DI layer | WYSOKI | App + 3 kontrolery | 3 |
| Layout Engine | Silnik layoutu | ŚREDNI | 1 engine | 1 |
| Entry Point Unification | Entry points | ŚREDNI | Bootstrap + PostProcessor | 1 |
| Legacy Cleanup | Porządki | NISKI | — | — |

**Łącznie:** 16 etapów, ~24 nowe klasy Domain, ~25 plików testowych

### Wzorce do przestrzegania (z migracji admin)
- Konstruktor DI z `$db` (medoo)
- CacheHandler (Redis) dla cachingu — unifikacja (usunięcie starego `Cache`)
- Fasady w `shop\*` i `front\factory\*` dla kompatybilności wstecznej
- Testy PHPUnit z mockami medoo
- Namespace `\Domain\*` → `autoload/Domain/*/`
- Namespace `\front\Controllers\` → `autoload/front/Controllers/`
- **Klasy Domain sa wspolne dla admin i frontendu** — NIE tworzymy osobnych FrontendService/AdminService. Metody frontendowe (z cache Redis) dodajemy do istniejacych repozytoriow/serwisow Domain. Klasy sa ladowane lazy (instancja tworzona dopiero przy wywolaniu), wiec nie wplywaja na wydajnosc.

### Nazewnictwo plikow
- Nowe klasy: `NazwaKlasy.php` (bez przedrostka `class.`)
- Legacy: `class.NazwaKlasy.php` — zostawiamy do momentu migracji danej klasy
- Autoloader obsluguje oba formaty (probuje `class.X.php`, potem `X.php`)
- Nowe katalogi z duzej litery: `Views/`, `Controllers/` (legacy: `view/`, `controls/`, `factory/`)

### Statyczne vs instancyjne metody
- **Statyczne** — gdy klasa jest bezstanowa (brak konstruktora, brak properties, brak DI). Czyste funkcje: dane wchodzą, wynik wychodzi. Przykład: klasy VIEW (`front\Views\Languages::render($data)`, `front\view\Banners::banners($data)`)
- **Instancyjne** — gdy klasa ma zależności do wstrzyknięcia (repozytoria, serwisy) lub trzyma stan. Przykład: kontrolery z DI (`ShopProductController` z `ProductRepository`, `LanguagesRepository`)

### Weryfikacja po każdym etapie
1. `composer test` (pełny suite PHPUnit)
2. Manualne sprawdzenie frontendu: strona główna, kategoria, produkt, koszyk, zamówienie
3. Sprawdzenie AJAX: dodawanie do koszyka, zmiana transportu, kupon
4. Sprawdzenie webhooków płatności (tPay, Przelewy24, Hotpay)
