<?php

declare(strict_types=1);

namespace App\Service;

use App\Entity\Device;
use App\Entity\DeviceImageHistory;
use App\Entity\Image;
use App\Enum\RenderStatus;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;

class RotationService
{
    public function __construct(
        private readonly EntityManagerInterface $em,
        private readonly LoggerInterface $logger,
    ) {}

    /**
     * Select the next image for the device, record history, update currentImage.
     * Returns null if no ready images exist in the pool.
     */
    public function advance(Device $device): ?Image
    {
        $pool = $this->readyPool($device);

        if (empty($pool)) {
            $this->logger->info('rotation.no_ready_images', ['device_id' => $device->getId()]);
            return null;
        }

        $window    = min($device->getUniquenessWindow(), count($pool));
        $recentIds = $this->recentImageIds($device, $window);

        $candidates = array_values(array_filter(
            $pool,
            static fn(Image $i) => !in_array($i->getId(), $recentIds, true),
        ));

        if (empty($candidates)) {
            $candidates = $pool;
        }

        usort($candidates, static fn(Image $a, Image $b) => $a->getUploadedAt() <=> $b->getUploadedAt());

        $image = $candidates[0];

        $this->em->persist(new DeviceImageHistory($device, $image));
        $device->setCurrentImage($image);
        $this->em->flush();

        $this->logger->info('rotation.advanced', [
            'device_id'  => $device->getId(),
            'image_id'   => $image->getId(),
            'pool_size'  => count($pool),
            'recent_ids' => $recentIds,
        ]);

        return $image;
    }

    /** @return Image[] */
    private function readyPool(Device $device): array
    {
        return $this->em->createQueryBuilder()
            ->select('i')
            ->from(Image::class, 'i')
            ->join('i.approvedDevices', 'd')
            ->join('i.renderedAssets', 'ra')
            ->where('d = :device')
            ->andWhere('i.deletedAt IS NULL')
            ->andWhere('ra.deviceModel = :model')
            ->andWhere('ra.orientation = :orientation')
            ->andWhere('ra.status = :status')
            ->setParameter('device', $device)
            ->setParameter('model', $device->getModel())
            ->setParameter('orientation', $device->getOrientation())
            ->setParameter('status', RenderStatus::Ready)
            ->getQuery()
            ->getResult();
    }

    /** @return int[] */
    private function recentImageIds(Device $device, int $limit): array
    {
        $rows = $this->em->createQueryBuilder()
            ->select('IDENTITY(h.image) AS image_id')
            ->from(DeviceImageHistory::class, 'h')
            ->where('h.device = :device')
            ->setParameter('device', $device)
            ->orderBy('h.servedAt', 'DESC')
            ->setMaxResults($limit)
            ->getQuery()
            ->getScalarResult();

        return array_column($rows, 'image_id');
    }
}
