---
phase: 06-integrations-refactoring
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - autoload/Domain/Integrations/ApiloRepository.php
  - tests/Unit/Domain/Integrations/ApiloRepositoryTest.php
autonomous: true
---

<objective>
## Goal
Wyekstrahować wszystkie metody Apilo z `IntegrationsRepository` do nowej klasy `ApiloRepository` — non-breaking (IntegrationsRepository pozostaje bez zmian do planu 06-02).

## Purpose
`IntegrationsRepository` ma 875 linii z czego ~650 to logika Apilo (OAuth, keepalive, fetchList, produkty). Po ekstrakcji każda klasa będzie mieć jedną odpowiedzialność, zgodnie z zasadami projektu (jedna klasa = jedna odpowiedzialność, max ~50 linii na metodę).

## Output
- Nowy plik: `autoload/Domain/Integrations/ApiloRepository.php` (~650 linii)
- Nowy plik testów: `tests/Unit/Domain/Integrations/ApiloRepositoryTest.php`
- `IntegrationsRepository` bez zmian (backward compatible)
</objective>

<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md

## Source Files
@autoload/Domain/Integrations/IntegrationsRepository.php
@tests/Unit/Domain/Integrations/IntegrationsRepositoryTest.php
</context>

<acceptance_criteria>

## AC-1: ApiloRepository zawiera wszystkie metody Apilo
```gherkin
Given plik autoload/Domain/Integrations/ApiloRepository.php istnieje
When przeglądamy jego publiczne metody
Then klasa ma: apiloAuthorize, apiloGetAccessToken, apiloKeepalive,
     apiloIntegrationStatus, apiloFetchList, apiloFetchListResult,
     apiloProductSearch, apiloCreateProduct
```

## AC-2: ApiloRepository ma własny dostęp do DB (DI przez konstruktor)
```gherkin
Given ApiloRepository(db: $mdb) jest tworzona
When wywoływana jest dowolna metoda apilo*
Then używa $db do zapytań bez zależności od IntegrationsRepository
```

## AC-3: IntegrationsRepository nie zmieniona (backward compatible)
```gherkin
Given istniejące testy IntegrationsRepositoryTest przechodzą
When uruchamiane jest ./test.ps1
Then wszystkie 817+ testów green, brak nowych błędów
```

## AC-4: Testy ApiloRepository pokrywają kluczowe metody
```gherkin
Given nowy plik ApiloRepositoryTest.php
When uruchamiane jest ./test.ps1
Then testy dla: apiloGetAccessToken, apiloKeepalive, apiloIntegrationStatus,
     apiloFetchListResult, apiloFetchList (invalid type), prywatnych helperów przechodzą
```

</acceptance_criteria>

<tasks>

<task type="auto">
  <name>Task 1: Utwórz ApiloRepository — ekstrakcja metod Apilo</name>
  <files>autoload/Domain/Integrations/ApiloRepository.php</files>
  <action>
    Utwórz nowy plik `autoload/Domain/Integrations/ApiloRepository.php`.

    Namespace: `Domain\Integrations`

    Klasa ma:
    - `private $db;`
    - `private const SETTINGS_TABLE = 'pp_shop_apilo_settings';`
    - Konstruktor: `public function __construct($db)`

    Przenieś (skopiuj) z IntegrationsRepository **bez modyfikacji logiki**:
    - Metody publiczne: `apiloAuthorize`, `apiloGetAccessToken`, `apiloKeepalive`,
      `apiloIntegrationStatus`, `apiloFetchList`, `apiloFetchListResult`,
      `apiloProductSearch`, `apiloCreateProduct`
    - Metody prywatne: `refreshApiloAccessToken`, `shouldRefreshAccessToken`,
      `isFutureDate`, `normalizeApiloMapList`, `isMapListShape`, `extractApiloErrorMessage`

    Dostosowania niezbędne po przeniesieniu:
    - Wszędzie gdzie metody apilo* wewnętrznie wołają `$this->getSettings('apilo')` —
      zamień na `$this->db->select(self::SETTINGS_TABLE, ['name', 'value'])` i mapuj
      na `[$row['name'] => $row['value']]` (ta sama logika co w IntegrationsRepository::getSettings)
    - Wszędzie gdzie wołają `$this->saveSetting('apilo', ...)` — zamień na bezpośrednie
      `$this->db->update(self::SETTINGS_TABLE, ['value' => $value], ['name' => $name])`
      i `$this->db->insert(self::SETTINGS_TABLE, ['name' => $name, 'value' => $value])`
      z `count()` przed jak w saveSetting (dokładna kopia logiki)

    Unikaj: dziedziczenia z IntegrationsRepository, jakichkolwiek zależności poza $db.
    PHP < 8.0: brak match, named args, union types, str_contains.
  </action>
  <verify>
    php -l autoload/Domain/Integrations/ApiloRepository.php zwraca "No syntax errors"
    Klasa ma dokładnie 8 publicznych metod apilo* + 6 prywatnych helperów.
  </verify>
  <done>AC-1 i AC-2 spełnione</done>
</task>

<task type="auto">
  <name>Task 2: Utwórz ApiloRepositoryTest</name>
  <files>tests/Unit/Domain/Integrations/ApiloRepositoryTest.php</files>
  <action>
    Utwórz `tests/Unit/Domain/Integrations/ApiloRepositoryTest.php`.

    Namespace: `Tests\Unit\Domain\Integrations`
    Klasa extends `PHPUnit\Framework\TestCase`

    Przenieś (skopiuj) z IntegrationsRepositoryTest wszystkie testy dotyczące metod Apilo:
    - `testApiloGetAccessTokenReturnsNullWithoutSettings`
    - `testShouldRefreshAccessTokenReturnsFalseForFarFutureDate`
    - `testShouldRefreshAccessTokenReturnsTrueForNearExpiryDate`
    - `testApiloFetchListThrowsForInvalidType`
    - `testApiloFetchListResultReturnsDetailedErrorWhenConfigMissing`
    - `testApiloIntegrationStatusReturnsMissingConfigMessage`
    - `testNormalizeApiloMapListRejectsErrorPayload`
    - `testNormalizeApiloMapListAcceptsIdNameList`

    Dostosuj w skopiowanych testach:
    - Zmień `new IntegrationsRepository($this->mockDb)` → `new ApiloRepository($this->mockDb)`
    - Use statement: `use Domain\Integrations\ApiloRepository;`
    - setUp: `$this->repository = new ApiloRepository($this->mockDb);`

    Uwaga: w testach mockujących `select` z `pp_shop_apilo_settings` — sprawdź czy
    ApiloRepository używa dokładnie tej samej tabeli i struktury zapytania co IntegrationsRepository.
    Jeśli zmieniło się wywołanie (np. bezpośrednie select zamiast przez getSettings),
    dostosuj expect() w testach.

    Nie usuwaj tych testów z IntegrationsRepositoryTest — zostają tam do planu 06-02.
  </action>
  <verify>
    ./test.ps1 tests/Unit/Domain/Integrations/ApiloRepositoryTest.php — wszystkie testy green
    ./test.ps1 — pełna suite green (817+ testów, brak regresji)
  </verify>
  <done>AC-3 i AC-4 spełnione</done>
</task>

</tasks>

<boundaries>

## DO NOT CHANGE
- `autoload/Domain/Integrations/IntegrationsRepository.php` — bez żadnych zmian w tym planie
- `tests/Unit/Domain/Integrations/IntegrationsRepositoryTest.php` — tylko dodajemy, nie usuwamy
- Żadne kontrolery, App.php, cron.php — migracja konsumentów to plan 06-02
- Żadne zmiany logiki biznesowej — czysta ekstrakcja, zero refaktoringu logiki

## SCOPE LIMITS
- Ten plan tworzy tylko nową klasę + testy. Konsumenci nadal używają IntegrationsRepository.
- Nie zmieniamy nazw metod, sygnatur, zachowania.
- Nie optymalizujemy kodu Apilo podczas przenoszenia.

</boundaries>

<verification>
Before declaring plan complete:
- [ ] php -l autoload/Domain/Integrations/ApiloRepository.php — no syntax errors
- [ ] ApiloRepository ma 8 publicznych metod: apiloAuthorize, apiloGetAccessToken,
      apiloKeepalive, apiloIntegrationStatus, apiloFetchList, apiloFetchListResult,
      apiloProductSearch, apiloCreateProduct
- [ ] ./test.ps1 tests/Unit/Domain/Integrations/ApiloRepositoryTest.php — all green
- [ ] ./test.ps1 — full suite green, żadna regresja w IntegrationsRepositoryTest
- [ ] IntegrationsRepository.php nie został zmodyfikowany
</verification>

<success_criteria>
- ApiloRepository.php istnieje z pełnym zestawem metod Apilo
- ApiloRepositoryTest.php istnieje z testami dla kluczowych metod
- Pełna suite testów green (817+ testów)
- IntegrationsRepository niezmieniony (backward compatible)
</success_criteria>

<output>
After completion, create `.paul/phases/06-integrations-refactoring/06-01-SUMMARY.md`
</output>
