<?php
/**
 * Copyright since 2025 InPost S.A.
 *
 * For the full license information, please view the LICENSE file bundled with the module.
 *
 * @author InPost S.A.
 * @copyright since 2025 InPost S.A.
 * @license MIT
 */

declare(strict_types=1);

namespace InPost\International\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use InPost\International\Api\Pickup\Request\CreatePickupOrderRequest;
use InPost\International\Api\Pickup\Response\CreatePickupOrderResponse;
use InPost\International\PickupOrder\Exception\PickupOrderException;
use InPost\International\PickupOrder\PickupOrderRepository;

if (!defined('_PS_VERSION_')) {
    exit;
}

/**
 * @final
 *
 * @ORM\Entity(repositoryClass=PickupOrderRepository::class)
 * @ORM\Table(name=PickupOrder::TABLE_NAME)
 * @ORM\HasLifecycleCallbacks
 */
class PickupOrder
{
    /**
     * @internal
     */
    public const TABLE_NAME = _DB_PREFIX_ . 'inpost_intl_pickup_order';

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

    /**
     * @var PickupAddress
     *
     * @ORM\ManyToOne(targetEntity=PickupAddress::class, inversedBy="pickupOrders")
     */
    private $pickupAddress;

    /**
     * @var DateRange
     *
     * @ORM\Embedded(class=DateRange::class)
     */
    private $pickupTime;

    /**
     * @var string
     *
     * @ORM\Column(type="guid")
     */
    private $uuid;

    /**
     * @var string
     *
     * @ORM\Column(type="string")
     */
    private $trackingNumber;

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

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

    /**
     * @var Collection<int, Shipment>
     *
     * @ORM\OneToMany(targetEntity=Shipment::class, mappedBy="pickupOrder")
     */
    private $shipments;

    private function __construct(PickupAddress $pickupAddress, DateRange $pickupTime, string $uuid, string $trackingNumber, ?array $references, Shipment ...$shipments)
    {
        if ([] === $shipments) {
            throw new PickupOrderException('Pickup order must have at least one shipment.');
        }

        $this->pickupAddress = $pickupAddress;
        $this->pickupTime = $pickupTime;
        $this->uuid = $uuid;
        $this->trackingNumber = $trackingNumber;
        $this->references = $references;

        $this->shipments = new ArrayCollection($shipments);
        foreach ($shipments as $shipment) {
            $shipment->setPickupOrder($this);
        }
    }

    public static function create(PickupAddress $pickupAddress, CreatePickupOrderRequest $request, CreatePickupOrderResponse $response, Shipment ...$shipments): self
    {
        if (null !== $request->getReferences()) {
            $references = iterator_to_array($request->getReferences());
        } else {
            $references = null;
        }

        return new self(
            $pickupAddress,
            new DateRange($request->getPickupTime()->getFrom(), $request->getPickupTime()->getTo()),
            $response->getId(),
            $response->getCarrierReference()->getTrackingNumber(),
            $references,
            ...$shipments
        );
    }

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

    public function getPickupAddress(): PickupAddress
    {
        return $this->pickupAddress;
    }

    public function getPickupTime(): DateRange
    {
        return $this->pickupTime;
    }

    public function getUuid(): string
    {
        return $this->uuid;
    }

    public function getTrackingNumber(): string
    {
        return $this->trackingNumber;
    }

    /**
     * @return array<string, string>
     */
    public function getReferences(): array
    {
        return $this->references ?? [];
    }

    /**
     * @return Shipment[]
     */
    public function getShipments(): array
    {
        return $this->shipments->toArray();
    }

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

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

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