<?php

declare(strict_types=1);

namespace App\Entity;

use App\Enum\DeviceModel;
use App\Entity\Image;
use App\Enum\Orientation;
use App\Repository\DeviceRepository;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: DeviceRepository::class)]
class Device
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 17, unique: true)]
    private string $mac = '';

    #[ORM\Column(length: 100)]
    private string $name = '';

    #[ORM\Column(enumType: DeviceModel::class)]
    private DeviceModel $model = DeviceModel::V1;

    #[ORM\Column(enumType: Orientation::class)]
    private Orientation $orientation = Orientation::Landscape;

    /** Minutes between rotation cycles (used when wakeHour is null). */
    #[ORM\Column]
    private int $rotationIntervalMinutes = 1440;

    /** Hour of day (0-23, local time) at which the device should wake; null = use rotationIntervalMinutes. */
    #[ORM\Column(nullable: true)]
    private ?int $wakeHour = null;

    /** IANA timezone for wakeHour scheduling (e.g. 'Europe/Stockholm'). */
    #[ORM\Column(length: 60)]
    private string $timezone = 'UTC';

    /** Number of display cycles before an image may repeat. */
    #[ORM\Column]
    private int $uniquenessWindow = 10;

    #[ORM\ManyToOne(inversedBy: 'devices')]
    #[ORM\JoinColumn(nullable: false)]
    private ?User $user = null;

    #[ORM\Column]
    private \DateTimeImmutable $linkedAt;

    #[ORM\ManyToOne]
    #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
    private ?Image $currentImage = null;

    /** When set, rotation is bypassed and this image is always served. */
    #[ORM\ManyToOne]
    #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
    private ?Image $lockedImage = null;

    #[ORM\Column(nullable: true)]
    private ?\DateTimeImmutable $lastSeenAt = null;

    /**
     * Orientation in effect when currentImage was last served as a 200 response.
     * Used alongside currentImage's id to decide whether a poll can be answered
     * with 304: if the device's orientation has flipped since, the cached image
     * on-device is stale and we must re-send the freshly-rendered .bin.
     */
    #[ORM\Column(nullable: true, enumType: Orientation::class)]
    private ?Orientation $currentImageOrientation = null;

    /**
     * RenderedAsset.rendered_at timestamp at the moment we last served this
     * device its current image. Lets the 304 cache check detect a re-render
     * (e.g. user re-cropped the image) — the image id and orientation may
     * still match, but the bytes have changed and we must re-send.
     */
    #[ORM\Column(nullable: true)]
    private ?\DateTimeImmutable $currentRenderedAt = null;

    public function __construct()
    {
        $this->linkedAt = new \DateTimeImmutable();
    }

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

    public function getMac(): string { return $this->mac; }
    public function setMac(string $mac): static { $this->mac = strtoupper($mac); return $this; }

    public function getName(): string { return $this->name; }
    public function setName(string $name): static { $this->name = $name; return $this; }

    public function getModel(): DeviceModel { return $this->model; }
    public function setModel(DeviceModel $model): static { $this->model = $model; return $this; }

    public function getOrientation(): Orientation { return $this->orientation; }
    public function setOrientation(Orientation $orientation): static { $this->orientation = $orientation; return $this; }

    public function getRotationIntervalMinutes(): int { return $this->rotationIntervalMinutes; }
    public function setRotationIntervalMinutes(int $m): static { $this->rotationIntervalMinutes = $m; return $this; }

    public function getWakeHour(): ?int { return $this->wakeHour; }
    public function setWakeHour(?int $hour): static { $this->wakeHour = ($hour !== null) ? max(0, min(23, $hour)) : null; return $this; }

    public function getTimezone(): string { return $this->timezone; }
    public function setTimezone(string $tz): static { $this->timezone = $tz; return $this; }

    public function getUniquenessWindow(): int { return $this->uniquenessWindow; }
    public function setUniquenessWindow(int $w): static { $this->uniquenessWindow = $w; return $this; }

    public function getUser(): ?User { return $this->user; }
    public function setUser(?User $user): static { $this->user = $user; return $this; }

    public function getLinkedAt(): \DateTimeImmutable { return $this->linkedAt; }
    public function setLinkedAt(\DateTimeImmutable $dt): static { $this->linkedAt = $dt; return $this; }

    public function getCurrentImage(): ?Image { return $this->currentImage; }
    public function setCurrentImage(?Image $image): static { $this->currentImage = $image; return $this; }

    public function getLockedImage(): ?Image { return $this->lockedImage; }
    public function setLockedImage(?Image $image): static { $this->lockedImage = $image; return $this; }

    public function getLastSeenAt(): ?\DateTimeImmutable { return $this->lastSeenAt; }
    public function markSeen(): static { $this->lastSeenAt = new \DateTimeImmutable(); return $this; }

    public function getCurrentImageOrientation(): ?Orientation { return $this->currentImageOrientation; }
    public function setCurrentImageOrientation(?Orientation $o): static { $this->currentImageOrientation = $o; return $this; }

    public function getCurrentRenderedAt(): ?\DateTimeImmutable { return $this->currentRenderedAt; }
    public function setCurrentRenderedAt(?\DateTimeImmutable $t): static { $this->currentRenderedAt = $t; return $this; }
}
