<?php

// ============================================================================
// FOTBALISTA - Entita
// ============================================================================

class Player 
{
    private string $name;
    private int $age;
    private string $position; // 'goalkeeper', 'defender', 'midfielder', 'forward'
    private int $goals;
    private int $yellowCards;
    private int $redCards;
    private bool $isInjured;
    private int $matchesPlayed;
    
    public function __construct(
        string $name,
        int $age,
        string $position,
        int $goals = 0,
        int $yellowCards = 0,
        int $redCards = 0,
        bool $isInjured = false,
        int $matchesPlayed = 0
    ) {
        $this->name = $name;
        $this->age = $age;
        $this->position = $position;
        $this->goals = $goals;
        $this->yellowCards = $yellowCards;
        $this->redCards = $redCards;
        $this->isInjured = $isInjured;
        $this->matchesPlayed = $matchesPlayed;
    }
    
    public function getName(): string { return $this->name; }
    public function getAge(): int { return $this->age; }
    public function getPosition(): string { return $this->position; }
    public function getGoals(): int { return $this->goals; }
    public function getYellowCards(): int { return $this->yellowCards; }
    public function getRedCards(): int { return $this->redCards; }
    public function isInjured(): bool { return $this->isInjured; }
    public function getMatchesPlayed(): int { return $this->matchesPlayed; }
}


// ============================================================================
// ❌ BEZ SPECIFICATION - Všechno v if podmínkách
// ============================================================================

class TeamManagerBad 
{
    public function selectStartingEleven(array $players): array 
    {
        $eligible = [];
        
        foreach ($players as $player) {
            // Obrovský if s mnoha podmínkami
            if (
                !$player->isInjured() 
                && $player->getRedCards() === 0
                && ($player->getYellowCards() < 2 || $player->getPosition() === 'goalkeeper')
                && $player->getAge() >= 18
                && $player->getAge() <= 35
                && $player->getMatchesPlayed() > 5
            ) {
                $eligible[] = $player;
            }
        }
        
        return array_slice($eligible, 0, 11);
    }
    
    public function selectForNationalTeam(array $players): array 
    {
        $eligible = [];
        
        foreach ($players as $player) {
            // Skoro stejné podmínky, ale ne úplně - DUPLICITA!
            if (
                !$player->isInjured()
                && $player->getAge() >= 18
                && $player->getAge() <= 32
                && $player->getGoals() > 10
                && $player->getMatchesPlayed() > 20
            ) {
                $eligible[] = $player;
            }
        }
        
        return $eligible;
    }
}

// PROBLÉMY:
// - Podmínky jsou všude roztroušené
// - Duplicitní kód
// - Těžké testování (musíš testovat celou metodu)
// - Při změně pravidla musíš hledat všechny místa


// ============================================================================
// ✅ SE SPECIFICATION - Pravidla jako objekty
// ============================================================================

interface PlayerSpecification 
{
    public function isSatisfiedBy(Player $player): bool;
}

// Jednotlivé specifikace:

class IsHealthySpecification implements PlayerSpecification 
{
    public function isSatisfiedBy(Player $player): bool 
    {
        return !$player->isInjured();
    }
}

class HasNoRedCardSpecification implements PlayerSpecification 
{
    public function isSatisfiedBy(Player $player): bool 
    {
        return $player->getRedCards() === 0;
    }
}

class IsInAgeRangeSpecification implements PlayerSpecification 
{
    private int $minAge;
    private int $maxAge;
    
    public function __construct(int $minAge, int $maxAge) 
    {
        $this->minAge = $minAge;
        $this->maxAge = $maxAge;
    }
    
    public function isSatisfiedBy(Player $player): bool 
    {
        $age = $player->getAge();
        return $age >= $this->minAge && $age <= $this->maxAge;
    }
}

class HasMinimumMatchesSpecification implements PlayerSpecification 
{
    private int $minMatches;
    
    public function __construct(int $minMatches) 
    {
        $this->minMatches = $minMatches;
    }
    
    public function isSatisfiedBy(Player $player): bool 
    {
        return $player->getMatchesPlayed() >= $this->minMatches;
    }
}

class HasMinimumGoalsSpecification implements PlayerSpecification 
{
    private int $minGoals;
    
    public function __construct(int $minGoals) 
    {
        $this->minGoals = $minGoals;
    }
    
    public function isSatisfiedBy(Player $player): bool 
    {
        return $player->getGoals() >= $this->minGoals;
    }
}

class IsPositionSpecification implements PlayerSpecification 
{
    private string $position;
    
    public function __construct(string $position) 
    {
        $this->position = $position;
    }
    
    public function isSatisfiedBy(Player $player): bool 
    {
        return $player->getPosition() === $this->position;
    }
}

class HasFewYellowCardsSpecification implements PlayerSpecification 
{
    private int $maxCards;
    
    public function __construct(int $maxCards = 1) 
    {
        $this->maxCards = $maxCards;
    }
    
    public function isSatisfiedBy(Player $player): bool 
    {
        return $player->getYellowCards() <= $this->maxCards;
    }
}


// Kombinace specifikací:

class AndSpecification implements PlayerSpecification 
{
    private array $specifications;
    
    public function __construct(PlayerSpecification ...$specifications) 
    {
        $this->specifications = $specifications;
    }
    
    public function isSatisfiedBy(Player $player): bool 
    {
        foreach ($this->specifications as $spec) {
            if (!$spec->isSatisfiedBy($player)) {
                return false;
            }
        }
        return true;
    }
}

class OrSpecification implements PlayerSpecification 
{
    private array $specifications;
    
    public function __construct(PlayerSpecification ...$specifications) 
    {
        $this->specifications = $specifications;
    }
    
    public function isSatisfiedBy(Player $player): bool 
    {
        foreach ($this->specifications as $spec) {
            if ($spec->isSatisfiedBy($player)) {
                return true;
            }
        }
        return false;
    }
}

class NotSpecification implements PlayerSpecification 
{
    private PlayerSpecification $specification;
    
    public function __construct(PlayerSpecification $specification) 
    {
        $this->specification = $specification;
    }
    
    public function isSatisfiedBy(Player $player): bool 
    {
        return !$this->specification->isSatisfiedBy($player);
    }
}


// ============================================================================
// POUŽITÍ - Teď je to čitelné a znovupoužitelné!
// ============================================================================

class TeamManagerGood 
{
    // Základní sestava pro zápas
    public function selectStartingEleven(array $players): array 
    {
        $eligibleForMatch = new AndSpecification(
            new IsHealthySpecification(),
            new HasNoRedCardSpecification(),
            new HasFewYellowCardsSpecification(1),
            new IsInAgeRangeSpecification(18, 35),
            new HasMinimumMatchesSpecification(5)
        );
        
        $eligible = array_filter($players, fn($p) => $eligibleForMatch->isSatisfiedBy($p));
        return array_slice($eligible, 0, 11);
    }
    
    // Reprezentace
    public function selectForNationalTeam(array $players): array 
    {
        $eligibleForNationalTeam = new AndSpecification(
            new IsHealthySpecification(),
            new IsInAgeRangeSpecification(18, 32),
            new HasMinimumGoalsSpecification(10),
            new HasMinimumMatchesSpecification(20)
        );
        
        return array_filter($players, fn($p) => $eligibleForNationalTeam->isSatisfiedBy($p));
    }
    
    // Hledání náhradníka na útočnou pozici
    public function findStrikerSubstitute(array $players): ?Player 
    {
        $goodStriker = new AndSpecification(
            new IsHealthySpecification(),
            new IsPositionSpecification('forward'),
            new HasMinimumGoalsSpecification(5)
        );
        
        foreach ($players as $player) {
            if ($goodStriker->isSatisfiedBy($player)) {
                return $player;
            }
        }
        
        return null;
    }
    
    // Hráči s rizikem trestu (hodně žlutých)
    public function findPlayersAtRiskOfSuspension(array $players): array 
    {
        $atRisk = new AndSpecification(
            new IsHealthySpecification(),
            new NotSpecification(new HasFewYellowCardsSpecification(1)) // Více než 1 žlutá
        );
        
        return array_filter($players, fn($p) => $atRisk->isSatisfiedBy($p));
    }
    
    // Mladé talenty
    public function findYoungTalents(array $players): array 
    {
        $youngTalent = new AndSpecification(
            new IsInAgeRangeSpecification(16, 21),
            new HasMinimumGoalsSpecification(3),
            new IsHealthySpecification()
        );
        
        return array_filter($players, fn($p) => $youngTalent->isSatisfiedBy($p));
    }
    
    // Brankáři NEBO obránci kteří jsou zdraví
    public function findDefensivePlayers(array $players): array 
    {
        $defensive = new AndSpecification(
            new IsHealthySpecification(),
            new OrSpecification(
                new IsPositionSpecification('goalkeeper'),
                new IsPositionSpecification('defender')
            )
        );
        
        return array_filter($players, fn($p) => $defensive->isSatisfiedBy($p));
    }
}


// ============================================================================
// PRAKTICKÁ UKÁZKA
// ============================================================================

// Vytvoř hráče:
$players = [
    new Player('Petr Čech', 42, 'goalkeeper', 0, 0, 0, false, 200),
    new Player('Tomáš Souček', 28, 'midfielder', 15, 3, 0, false, 150),
    new Player('Patrik Schick', 27, 'forward', 25, 1, 0, false, 100),
    new Player('Vladimír Coufal', 31, 'defender', 2, 5, 1, false, 120), // červená karta!
    new Player('Antonín Barák', 28, 'midfielder', 12, 2, 0, true, 90), // zraněný!
    new Player('Adam Hložek', 21, 'forward', 8, 0, 0, false, 50),
    new Player('David Zima', 22, 'defender', 1, 1, 0, false, 60),
];

$manager = new TeamManagerGood();

echo "=== ZÁKLADNÍ SESTAVA ===\n";
$starting = $manager->selectStartingEleven($players);
foreach ($starting as $player) {
    echo "- {$player->getName()} ({$player->getPosition()})\n";
}
// Výstup:
// - Petr Čech (goalkeeper)
// - Tomáš Souček (midfielder)
// - Patrik Schick (forward)
// - Adam Hložek (forward)
// - David Zima (defender)

echo "\n=== HRÁČI V RIZIKU TRESTU ===\n";
$atRisk = $manager->findPlayersAtRiskOfSuspension($players);
foreach ($atRisk as $player) {
    echo "- {$player->getName()} - {$player->getYellowCards()} žlutých\n";
}
// Výstup:
// - Tomáš Souček - 3 žlutých
// - Vladimír Coufal - 5 žlutých

echo "\n=== MLADÉ TALENTY ===\n";
$talents = $manager->findYoungTalents($players);
foreach ($talents as $player) {
    echo "- {$player->getName()} ({$player->getAge()} let, {$player->getGoals()} gólů)\n";
}
// Výstup:
// - Adam Hložek (21 let, 8 gólů)


// ============================================================================
// VÝHODY SPECIFICATION PATTERNU
// ============================================================================

/*
✅ ZNOVUPOUŽITELNOST
   - IsHealthySpecification můžeš použít všude
   - Nemusíš psát stejnou podmínku 10x

✅ TESTOVATELNOST
   - Každou specifikaci otestuješ samostatně
   - Jednodušší unit testy

✅ ČITELNOST
   - new IsHealthySpecification() je jasné
   - Místo: !$player->isInjured() && $player->getRedCards() === 0 && ...

✅ KOMBINOVATELNOST
   - AndSpecification, OrSpecification, NotSpecification
   - Stavíš složitá pravidla z jednoduchých

✅ ZMĚNY NA JEDNOM MÍSTĚ
   - Změníš pravidlo "zdravý" = změna v IsHealthySpecification
   - Automaticky se změní všude

✅ BUSINESS LOGIKA JAKO OBJEKTY
   - Pravidla můžeš ukládat do DB
   - Můžeš je posílat mezi službami
*/