<?php

declare(strict_types=1);

namespace Gls\GlsPoland\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gls\GlsPoland\AdePlus\Common\Id;
use Gls\GlsPoland\AdePlus\Pickup\ConsignmentIdsBinding;
use Gls\GlsPoland\Repository\ConsignmentRepository;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
 * @ORM\Entity(repositoryClass=ConsignmentRepository::class)
 * @ORM\Table(name=Consignment::TABLE_NAME)
 * @ORM\HasLifecycleCallbacks()
 *
 * @UniqueEntity({"prepBoxId", "sandbox"})
 * @UniqueEntity({"consignmentId", "sandbox"})
 */
class Consignment
{
    /**
     * @internal
     */
    public const TABLE_NAME = _DB_PREFIX_ . 'gls_poland_consignment';

    /**
     * @var int|null
     *
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @var int
     *
     * @ORM\Column(type="integer", name="prep_box_id")
     */
    private $preparingBoxId;

    /**
     * @var bool
     *
     * @ORM\Column(type="boolean")
     */
    private $sandbox;

    /**
     * @var int
     *
     * @ORM\Column(type="integer", name="id_order")
     */
    private $orderId;

    /**
     * @var int|null
     *
     * @ORM\Column(type="integer", nullable=true)
     */
    private $pickupId;

    /**
     * @var int|null
     *
     * @ORM\Column(type="integer", nullable=true)
     */
    private $consignmentId;

    /**
     * @var string|null
     *
     * @ORM\Column(type="string", name="reference", length=25, nullable=true)
     */
    private $references;

    /**
     * @var string|null
     *
     * @ORM\Column(type="string", length=40, nullable=true)
     */
    private $email;

    /**
     * @var string|null
     *
     * @ORM\Column(type="string", length=20, nullable=true)
     */
    private $phone;

    /**
     * @var \DateTimeImmutable|null
     *
     * @ORM\Column(type="datetime_immutable")
     */
    private $createdAt;

    /**
     * @var Collection<int, Parcel>
     *
     * @ORM\OneToMany(targetEntity=Parcel::class, mappedBy="consignment", orphanRemoval=true, cascade={"persist", "remove"}, fetch="EAGER")
     */
    private $parcels;

    public function __construct(Id $consignmentId, bool $sandbox, int $orderId, ?string $references, ?string $email, ?string $phone, Parcel ...$parcels)
    {
        if ([] === $parcels) {
            throw new \LogicException('Consignment must contain at least one parcel.');
        }

        $this->preparingBoxId = $consignmentId->getId();
        $this->orderId = $orderId;
        $this->sandbox = $sandbox;
        $this->references = $references;
        $this->email = $email;
        $this->phone = $phone;
        $this->parcels = new ArrayCollection();
        foreach ($parcels as $parcel) {
            $this->addParcel($parcel);
        }
    }

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

    public function isSandbox(): bool
    {
        return $this->sandbox;
    }

    public function getPreparingBoxId(): int
    {
        return $this->preparingBoxId;
    }

    public function getAdeConsignmentId(): int
    {
        return $this->consignmentId ?? $this->preparingBoxId;
    }

    public function getOrderId(): int
    {
        return $this->orderId;
    }

    public function getPickupId(): ?int
    {
        return $this->pickupId;
    }

    public function setPickupId(Id $pickupId, ConsignmentIdsBinding $binding): self
    {
        if ($this->preparingBoxId !== $binding->getPreparingBoxId()) {
            throw new \InvalidArgumentException('Preparing box consignment IDs do not match.');
        }

        $this->pickupId = $pickupId->getId();
        $this->consignmentId = $binding->getPickupId();

        return $this;
    }

    public function getReferences(): ?string
    {
        return $this->references;
    }

    public function setReferences(?string $references): self
    {
        $this->references = $references;

        return $this;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(?string $email): self
    {
        $this->email = $email;

        return $this;
    }

    public function getPhone(): ?string
    {
        return $this->phone;
    }

    public function setPhone(?string $phone): self
    {
        $this->phone = $phone;

        return $this;
    }

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

    /**
     * @return Collection<int, Parcel>
     */
    public function getParcels(): Collection
    {
        return $this->parcels;
    }

    private function addParcel(Parcel $parcel): void
    {
        if (!$this->parcels->contains($parcel)) {
            $this->parcels->add($parcel);
            $parcel->setConsignment($this);
        }
    }

    public function hasParcelNumbers(): bool
    {
        return !$this->parcels->isEmpty() && null !== $this->parcels->first()->getNumber();
    }

    /**
     * @ORM\PrePersist()
     */
    public function onPrePersist(): void
    {
        if (null !== $this->createdAt) {
            return;
        }

        $this->createdAt = new \DateTimeImmutable();
    }
}
