<?php

declare(strict_types=1);

namespace App\Entity;

use App\Enum\GameStatus;
use App\Enum\ThrowRule;
use App\Repository\GameRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;

#[ORM\Entity(repositoryClass: GameRepository::class)]
#[ORM\Table(name: '`game`')]
#[ORM\HasLifecycleCallbacks]
class Game
{
    #[ORM\Id]
    #[ORM\Column(type: Types::GUID, unique: true)]
    private string $id;

    #[ORM\ManyToOne]
    #[ORM\JoinColumn(nullable: false)]
    private GameMode $mode;

    #[ORM\ManyToOne(inversedBy: 'games')]
    #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
    private User $owner;

    #[ORM\Column(enumType: GameStatus::class, length: 20)]
    private GameStatus $status = GameStatus::DRAFT;

    #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
    private ?\DateTimeImmutable $startedAt = null;

    #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
    private ?\DateTimeImmutable $finishedAt = null;

    #[ORM\Column]
    private int $currentPlayerIndex = 0;

    #[ORM\Column]
    private int $currentTurnNumber = 1;

    #[ORM\Column]
    private int $currentDartInTurn = 1;

    #[ORM\Column(enumType: ThrowRule::class, length: 10)]
    private ThrowRule $inRule = ThrowRule::ANY;

    #[ORM\Column(enumType: ThrowRule::class, length: 10)]
    private ThrowRule $outRule = ThrowRule::ANY;

    #[ORM\OneToMany(mappedBy: 'game', targetEntity: GamePlayer::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
    #[ORM\OrderBy(['position' => 'ASC'])]
    private Collection $players;

    #[ORM\OneToMany(mappedBy: 'game', targetEntity: Turn::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
    private Collection $turns;

    #[ORM\OneToMany(mappedBy: 'game', targetEntity: GameEventLog::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
    private Collection $eventLogs;

    #[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
    private \DateTimeImmutable $createdAt;

    #[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
    private \DateTimeImmutable $updatedAt;

    public function __construct(User $owner, GameMode $mode, ThrowRule $inRule = ThrowRule::ANY, ThrowRule $outRule = ThrowRule::ANY)
    {
        $this->id = Uuid::v7()->toRfc4122();
        $this->owner = $owner;
        $this->mode = $mode;
        $this->inRule = $inRule;
        $this->outRule = $outRule;
        $this->players = new ArrayCollection();
        $this->turns = new ArrayCollection();
        $this->eventLogs = new ArrayCollection();
        $this->createdAt = new \DateTimeImmutable();
        $this->updatedAt = new \DateTimeImmutable();
    }

    public function getId(): string
    {
        return $this->id;
    }

    public function getMode(): GameMode
    {
        return $this->mode;
    }

    public function setMode(GameMode $mode): self
    {
        $this->mode = $mode;

        return $this;
    }

    public function getOwner(): User
    {
        return $this->owner;
    }

    public function setOwner(User $owner): self
    {
        $this->owner = $owner;

        return $this;
    }

    public function getStatus(): GameStatus
    {
        return $this->status;
    }

    public function setStatus(GameStatus $status): self
    {
        $this->status = $status;

        return $this;
    }

    public function getStartedAt(): ?\DateTimeImmutable
    {
        return $this->startedAt;
    }

    public function setStartedAt(?\DateTimeImmutable $startedAt): self
    {
        $this->startedAt = $startedAt;

        return $this;
    }

    public function getFinishedAt(): ?\DateTimeImmutable
    {
        return $this->finishedAt;
    }

    public function setFinishedAt(?\DateTimeImmutable $finishedAt): self
    {
        $this->finishedAt = $finishedAt;

        return $this;
    }

    public function getCurrentPlayerIndex(): int
    {
        return $this->currentPlayerIndex;
    }

    public function setCurrentPlayerIndex(int $currentPlayerIndex): self
    {
        $this->currentPlayerIndex = $currentPlayerIndex;

        return $this;
    }

    public function getCurrentTurnNumber(): int
    {
        return $this->currentTurnNumber;
    }

    public function setCurrentTurnNumber(int $currentTurnNumber): self
    {
        $this->currentTurnNumber = $currentTurnNumber;

        return $this;
    }

    public function getCurrentDartInTurn(): int
    {
        return $this->currentDartInTurn;
    }

    public function setCurrentDartInTurn(int $currentDartInTurn): self
    {
        $this->currentDartInTurn = $currentDartInTurn;

        return $this;
    }

    public function getInRule(): ThrowRule
    {
        return $this->inRule;
    }

    public function setInRule(ThrowRule $inRule): self
    {
        $this->inRule = $inRule;

        return $this;
    }

    public function getOutRule(): ThrowRule
    {
        return $this->outRule;
    }

    public function setOutRule(ThrowRule $outRule): self
    {
        $this->outRule = $outRule;

        return $this;
    }

    /** @return Collection<int, GamePlayer> */
    public function getPlayers(): Collection
    {
        return $this->players;
    }

    public function addPlayer(GamePlayer $player): self
    {
        if (!$this->players->contains($player)) {
            $this->players->add($player);
            $player->setGame($this);
        }

        return $this;
    }

    public function removePlayer(GamePlayer $player): self
    {
        $this->players->removeElement($player);

        return $this;
    }

    /** @return Collection<int, Turn> */
    public function getTurns(): Collection
    {
        return $this->turns;
    }

    public function addTurn(Turn $turn): self
    {
        if (!$this->turns->contains($turn)) {
            $this->turns->add($turn);
            $turn->setGame($this);
        }

        return $this;
    }

    public function removeTurn(Turn $turn): self
    {
        $this->turns->removeElement($turn);

        return $this;
    }

    /** @return Collection<int, GameEventLog> */
    public function getEventLogs(): Collection
    {
        return $this->eventLogs;
    }

    public function addEventLog(GameEventLog $eventLog): self
    {
        if (!$this->eventLogs->contains($eventLog)) {
            $this->eventLogs->add($eventLog);
            $eventLog->setGame($this);
        }

        return $this;
    }

    public function getCreatedAt(): \DateTimeImmutable
    {
        return $this->createdAt;
    }

    public function getUpdatedAt(): \DateTimeImmutable
    {
        return $this->updatedAt;
    }

    #[ORM\PrePersist]
    public function onPrePersist(): void
    {
        $now = new \DateTimeImmutable();
        $this->createdAt = $now;
        $this->updatedAt = $now;
    }

    #[ORM\PreUpdate]
    public function onPreUpdate(): void
    {
        $this->updatedAt = new \DateTimeImmutable();
    }
}
