<?php

namespace App\Controller;

use App\Repository\DeviceRepository;
use App\Security\DeviceCookieFactory;
use App\Security\DeviceCredentialService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\Routing\Attribute\Route;

final class DevicePairingController
{
    public function __construct(
        private readonly DeviceRepository $devices,
        private readonly DeviceCredentialService $credentials,
        private readonly DeviceCookieFactory $cookies,
        private readonly EntityManagerInterface $entityManager,
        private readonly RateLimiterFactory $devicePairingLimiter,
    ) {
    }

    #[Route('/api/v1/device-pairings/claim', name: 'api_device_pairing_claim', methods: ['POST'])]
    public function __invoke(Request $request): JsonResponse
    {
        $limit = $this->devicePairingLimiter->create($request->getClientIp() ?? 'unknown')->consume();
        if (!$limit->isAccepted()) {
            return new JsonResponse(['error' => 'pairing_rate_limited'], 429, [
                'Retry-After' => (string) max(1, $limit->getRetryAfter()->getTimestamp() - time()),
            ]);
        }

        try {
            $payload = $request->toArray();
        } catch (\Throwable) {
            $payload = [];
        }

        $code = (string) ($payload['code'] ?? '');
        if (1 !== preg_match('/^\d{6}$/', $code)) {
            return new JsonResponse(['error' => 'invalid_pairing_code'], 422);
        }

        $device = $this->devices->findPendingByPairingDigest($this->credentials->pairingDigest($code));
        $now = new \DateTimeImmutable();
        if (null === $device || null === $device->getPairingExpiresAt() || $device->getPairingExpiresAt() <= $now || $device->getPairingAttempts() >= 5) {
            if (null !== $device) {
                $device->recordPairingFailure();
                $this->entityManager->flush();
            }

            return new JsonResponse(['error' => 'invalid_pairing_code'], 422);
        }

        $credential = $this->credentials->createCredential();
        $device->activate($credential['digest'], $credential['expiresAt']);
        $this->entityManager->flush();

        $response = new JsonResponse([
            'device' => [
                'id' => $device->getId(),
                'label' => $device->getLabel(),
                'status' => $device->getStatus()->value,
            ],
        ], Response::HTTP_CREATED);
        $response->headers->setCookie($this->cookies->create($credential['token'], $credential['expiresAt']));

        return $response;
    }
}
