# UCL Manager League

Prywatne rozgrywki w formacie Ligi Mistrzów, w których o wyniku meczu decydują
punkty uczestników zdobyte w oficjalnej grze fantasy na `gaming.uefa.com`.

Każdy klub ma przypisanego jednego uczestnika (tryb **Pojedynczo**) albo dwóch,
których punkty się sumują (tryb **Pary**). Terminarz fazy ligowej kopiuje się
z prawdziwej Ligi Mistrzów, a fazę pucharową aplikacja rozstawia sama.

- Specyfikacja: `docs/superpowers/specs/2026-09-03-ucl-manager-league-design.md`
- Plan wdrożenia: `docs/superpowers/plans/2026-09-03-ucl-manager-league.md`

## Wymagania

PHP 8.3+, Composer, MySQL 5.7+.

## Uruchomienie

```bash
composer install
```

W `.env.local` ustaw dostęp do bazy (plik nie trafia do repozytorium):

```
APP_ENV=dev
APP_SECRET=losowy-ciag
DATABASE_URL="mysql://uzytkownik:haslo@127.0.0.1:3306/ucl?serverVersion=5.7&charset=utf8mb4"
```

Potem:

```bash
php bin/console doctrine:database:create --if-not-exists
php bin/console doctrine:migrations:migrate
php bin/console app:admin:create admin@example.com twoje-haslo
symfony server:start -d
```

Testy chodzą na tej samej bazie z sufiksem `_test`. Dane dostępowe muszą trafić
do `.env.test.local`, bo Symfony celowo nie wczytuje `.env.local` w środowisku
testowym:

```bash
php bin/console doctrine:database:create --if-not-exists --env=test  # albo APP_ENV=test
php bin/phpunit
```

Żeby zobaczyć działającą aplikację bez zakładania sezonu:

```bash
php bin/console app:demo:load --mode=pairs
```

Demo rozgrywa cały sezon na losowych punktach, aż do finału, na prawdziwym
składzie bieżącej edycji.

## Prowadzenie sezonu

1. **Załóż rozgrywki** — `/admin`, przycisk „Nowe rozgrywki". Wybierz tryb;
   aplikacja od razu zakłada komplet 13 kolejek.
2. **Dodaj 36 klubów** — zakładka „Kluby". Przycisk „Wstaw skład Ligi Mistrzów"
   wpisuje komplet klubów bieżącej edycji jednym kliknięciem; można też wkleić
   własną listę, po jednej nazwie w wierszu.
3. **Dodaj uczestników** — dwie drogi:
   - *ręcznie* — zakładka „Uczestnicy". Pole *nazwa drużyny na gaming.uefa.com*
     musi zgadzać się co do znaku, bo po nim dopinane są punkty;
   - *kodami zaproszeń* (tylko rozgrywki 1 vs 1) — zakładka „Kody" generuje po
     jednym kodzie na wolny klub. Uczestnik wchodzi na `/<slug>/dolacz`, wpisuje
     kod, dostaje **trzy losowe wolne kluby**, wybiera jeden i podaje nazwę swojej
     drużyny UEFA. Kod jest jednorazowy, a wylosowana trójka zapamiętuje się przy
     pierwszym wejściu — odświeżanie strony jej nie przelosuje. Kluby z trójki nie
     są blokowane dla innych: kto pierwszy zatwierdzi, ten ma, a spóźniony dostaje
     komunikat i wybiera z tego, co zostało.
4. **Wczytaj terminarz fazy ligowej** — zakładka „Terminarz", przycisk
   „Pobierz terminarz Ligi Mistrzów". Ściąga oryginalny terminarz bieżącej
   edycji i **sprawdza go, zanim cokolwiek zapisze**: w każdej kolejce każdy klub
   gra dokładnie raz, żadna para nie powtarza się dwa razy, a bilans meczów
   u siebie i na wyjeździe musi być wyrównany. Terminarz, który tych warunków
   nie spełnia, jest odrzucany w całości — nic nie trafia do bazy.

   To samo z konsoli: `php bin/console app:schedule:import <slug>`.

   Można też wkleić własny terminarz, wierszami `numer kolejki⇥gospodarz⇥gość`.
   Oba importy kasują poprzedni terminarz fazy ligowej, więc da się je powtarzać.

   Adres strony z terminarzem siedzi w `src/Uefa/ClubCatalogue.php`
   (`SCHEDULE_URL`) — podmieniasz go razem ze składem klubów na nowy sezon.
5. **Po każdej kolejce wczytaj punkty** — z widoku kolejki „Wczytaj punkty",
   wklej leaderboard UEFA, obejrzyj **podgląd dopasowania** i dopiero wtedy
   zatwierdź. Podgląd pokazuje, kogo nie znalazł we wklejce i czego nie
   rozpoznał — nie pomijaj go, literówka w nazwie drużyny cicho zeruje komuś
   kolejkę.
6. **Zamknij kolejkę**, a po ósmej kliknij „Wygeneruj następną rundę", żeby
   rozstawić baraże. To samo po każdej rundzie pucharowej.

Herby klubów: `php bin/console app:clubs:logos <slug-rozgrywek>` pobiera je
z serwera obrazów UEFA do `public/images/clubs/`. Komenda nie parsuje żadnej
strony — bierze identyfikator klubu z `src/Uefa/ClubCatalogue.php`, więc
potrzebuje tylko dostępu do `img.uefa.com`. Nazwę pliku można też wpisać ręcznie
w edycji klubu.

**Po losowaniu nowego sezonu** podmień listę w `src/Uefa/ClubCatalogue.php` —
to jedyne miejsce w kodzie, które zna konkretną edycję. Trzyma nazwę klubu,
skrót i identyfikator UEFA (ten sam, który widać w adresie strony klubu na
uefa.com i w adresie herbu).

## Jak liczone są wyniki

Punkty uczestnika za kolejkę (`ManagerRoundScore`) są **jedynym źródłem prawdy**.
Wynik meczu to suma punktów uczestników danego klubu, a cała tabela jest
przeliczana **od zera** przy każdej zmianie. Dlatego import można powtórzyć,
poprawić pojedynczy wynik albo dograć zapomnianego uczestnika — stan zawsze się
zgadza. Przycisk „Przelicz od nowa" w widoku kolejki robi to samo ręcznie.

Kolejność w tabeli: punkty (3/1/0) → różnica punktów fantasy → punkty zdobyte →
nazwa klubu.

## Faza pucharowa

Runda pucharowa to **jeden mecz**, nie dwumecz: UEFA Fantasy publikuje punkty
per matchday, a w fazie pucharowej jeden matchday obejmuje oba spotkania
prawdziwego dwumeczu.

Miejsca 1–8 wchodzą prosto do 1/8 finału, 9–24 grają baraże (9–24, 10–23, …,
16–17), 25–36 odpadają. Rozstawiony nr 1 trafia na zwycięzcę pary PO8, nr 2 na
PO7 i tak dalej. Dalej drabinka jest stała: QF1 = R16-1 z R16-8, QF2 = R16-4
z R16-5, QF3 = R16-2 z R16-7, QF4 = R16-3 z R16-6.

**Remis w meczu pucharowym rozstrzyga rozstawienie** — awansuje klub z lepszym
miejscem w końcowej tabeli fazy ligowej.
