<?php

declare(strict_types=1);

namespace App\Controller\Api;

use App\Entity\Device;
use App\Entity\SharedImage;
use App\Entity\User;
use App\Enum\DeviceModel;
use App\Enum\Orientation;
use App\Enum\SharedImageStatus;
use App\Message\RenderImageMessage;
use App\Repository\SharedImageRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[Route('/api/shared-images')]
#[IsGranted('ROLE_USER')]
class SharedImageApiController extends AbstractController
{
    public function __construct(
        private readonly SharedImageRepository $sharedImageRepository,
        private readonly EntityManagerInterface $em,
        private readonly MessageBusInterface    $bus,
    ) {}

    #[Route('', name: 'api_shared_images_list', methods: ['GET'])]
    public function list(Request $request): JsonResponse
    {
        /** @var User $user */
        $user   = $this->getUser();
        $status = $request->query->get('status');
        $page   = max(1, (int) $request->query->get('page', 1));
        $limit  = min(50, max(1, (int) $request->query->get('limit', 20)));

        $statusEnum = $status ? SharedImageStatus::tryFrom($status) : null;

        $result = $this->sharedImageRepository->findForUser($user, $statusEnum, $page, $limit);

        return $this->json([
            'items'      => array_map($this->serialize(...), $result['items']),
            'total'      => $result['total'],
            'page'       => $page,
            'limit'      => $limit,
            'totalPages' => (int) ceil($result['total'] / $limit),
        ]);
    }

    #[Route('/pending-count', name: 'api_shared_images_pending_count', methods: ['GET'])]
    public function pendingCount(): JsonResponse
    {
        /** @var User $user */
        $user = $this->getUser();
        return $this->json(['count' => $this->sharedImageRepository->pendingCountForUser($user)]);
    }

    #[Route('/{id}/approve', name: 'api_shared_images_approve', methods: ['POST'])]
    public function approve(int $id, Request $request): JsonResponse
    {
        $shared = $this->findOwnedShared($id);
        if (!$shared) {
            return $this->json(['error' => 'Not found'], Response::HTTP_NOT_FOUND);
        }

        /** @var User $user */
        $user      = $this->getUser();
        $deviceIds = json_decode($request->getContent(), true)['deviceIds'] ?? [];

        foreach ($deviceIds as $deviceId) {
            $device = $this->em->getRepository(Device::class)->findOneBy(['id' => (int) $deviceId, 'user' => $user]);
            if (!$device) {
                continue;
            }
            $shared->getSourceImage()->approveForDevice($device);
        }

        $shared->setStatus(SharedImageStatus::Approved);
        $this->em->flush();

        // Dispatch renders for any missing rendered assets
        $image = $shared->getSourceImage();
        foreach (DeviceModel::cases() as $model) {
            foreach (Orientation::cases() as $orientation) {
                $this->bus->dispatch(new RenderImageMessage($image->getId(), $model->value, $orientation->value));
            }
        }

        return $this->json($this->serialize($shared));
    }

    #[Route('/{id}/decline', name: 'api_shared_images_decline', methods: ['POST'])]
    public function decline(int $id): JsonResponse
    {
        $shared = $this->findOwnedShared($id);
        if (!$shared) {
            return $this->json(['error' => 'Not found'], Response::HTTP_NOT_FOUND);
        }

        // Revoke approvals if previously approved
        if ($shared->getStatus() === SharedImageStatus::Approved) {
            /** @var User $user */
            $user    = $this->getUser();
            $devices = $this->em->getRepository(Device::class)->findBy(['user' => $user]);
            foreach ($devices as $device) {
                $shared->getSourceImage()->revokeForDevice($device);
            }
        }

        $shared->setStatus(SharedImageStatus::Declined);
        $this->em->flush();

        return $this->json($this->serialize($shared));
    }

    private function findOwnedShared(int $id): ?SharedImage
    {
        return $this->em->getRepository(SharedImage::class)->findOneBy([
            'id'            => $id,
            'recipientUser' => $this->getUser(),
        ]);
    }

    private function serialize(SharedImage $s): array
    {
        return [
            'id'           => $s->getId(),
            'imageId'      => $s->getSourceImage()->getId(),
            'thumbnailUrl' => '/api/images/' . $s->getSourceImage()->getId() . '/thumbnail',
            'sharedBy'     => $s->getSharedBy()->getEmail(),
            'sharedAt'     => $s->getSharedAt()->format(\DateTimeInterface::ATOM),
            'status'       => $s->getStatus()->value,
        ];
    }
}
