<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Controller;

use Chill\MainBundle\Entity\Notification;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Repository\NotificationRepository;
use Chill\MainBundle\Security\Authorization\NotificationVoter;
use Chill\MainBundle\Serializer\Model\Collection;
use Chill\MainBundle\Serializer\Model\Counter;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\SerializerInterface;

#[Route(path: '/api/1.0/main/notification')]
class NotificationApiController
{
    public function __construct(private readonly EntityManagerInterface $entityManager, private readonly NotificationRepository $notificationRepository, private readonly PaginatorFactory $paginatorFactory, private readonly Security $security, private readonly SerializerInterface $serializer) {}

    #[Route(path: '/{id}/mark/read', name: 'chill_api_main_notification_mark_read', methods: ['POST'])]
    public function markAsRead(Notification $notification): JsonResponse
    {
        return $this->markAs('read', $notification);
    }

    #[Route(path: '/{id}/mark/unread', name: 'chill_api_main_notification_mark_unread', methods: ['POST'])]
    public function markAsUnread(Notification $notification): JsonResponse
    {
        return $this->markAs('unread', $notification);
    }

    #[Route(path: '/my/unread')]
    public function myUnreadNotifications(Request $request): JsonResponse
    {
        $total = $this->notificationRepository->countUnreadByUser($this->security->getUser());

        if ($request->query->getBoolean('countOnly')) {
            return new JsonResponse(
                $this->serializer->serialize(new Counter($total), 'json', ['groups' => ['read']]),
                JsonResponse::HTTP_OK,
                [],
                true
            );
        }
        $paginator = $this->paginatorFactory->create($total);
        $notifications = $this->notificationRepository->findUnreadByUser(
            $this->security->getUser(),
            $paginator->getItemsPerPage(),
            $paginator->getCurrentPageFirstItemNumber()
        );
        $collection = new Collection($notifications, $paginator);

        return new JsonResponse(
            $this->serializer->serialize($collection, 'json', ['groups' => ['read']]),
            JsonResponse::HTTP_OK,
            [],
            true
        );
    }

    private function markAs(string $target, Notification $notification): JsonResponse
    {
        if (!$this->security->isGranted(NotificationVoter::NOTIFICATION_TOGGLE_READ_STATUS, $notification)) {
            throw new AccessDeniedException('Not allowed to toggle read status of notification');
        }

        $user = $this->security->getUser();

        if (!$user instanceof User) {
            throw new \RuntimeException('not possible to mark as read by this user');
        }

        match ($target) {
            'read' => $notification->markAsReadBy($user),
            'unread' => $notification->markAsUnreadBy($user),
            default => throw new \UnexpectedValueException("target not supported: {$target}"),
        };

        $this->entityManager->flush();

        return new JsonResponse(null, JsonResponse::HTTP_ACCEPTED, [], false);
    }

    /**
     * @Route("/mark/allread", name="chill_api_main_notification_mark_allread", methods={"POST"})
     */
    public function markAllRead(): JsonResponse
    {
        $user = $this->security->getUser();

        if (!$user instanceof User) {
            throw new \RuntimeException('Invalid user');
        }

        $modifiedNotificationIds = $this->notificationRepository->markAllNotificationAsReadForUser($user);

        return new JsonResponse($modifiedNotificationIds);
    }

    /**
     * @Route("/mark/undoallread", name="chill_api_main_notification_mark_undoallread", methods={"POST"})
     */
    public function undoAllRead(Request $request): JsonResponse
    {
        $user = $this->security->getUser();

        if (!$user instanceof User) {
            throw new \RuntimeException('Invalid user');
        }

        $ids = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);

        $touchedIds = $this->notificationRepository->markAllNotificationAsUnreadForUser($user, $ids);

        return new JsonResponse($touchedIds);
    }
}
