<?php
namespace App\Controller\Ai;
use App\Repository\AcademicSearchHistoryRepository;
use App\Service\Ai\PluginAuthResolver;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
/**
* Historique de recherche académique pour l'utilisateur courant.
*
* Endpoints (Phase 19+) :
* - GET /api/ai/academic/history?source=arxiv → 10 dernières
* - DELETE /api/ai/academic/history/{id} → supprime un item
* - DELETE /api/ai/academic/history?source=arxiv → vide une source
* - DELETE /api/ai/academic/history → vide tout
*
* Auth : cookie session OU JWT Bearer (cf. PluginAuthResolver).
*/
#[Route('/api/ai/academic')]
class AcademicSearchHistoryController extends AbstractController
{
/** Sources autorisées (anti-injection). */
private const ALLOWED_SOURCES = ['arxiv', 'openalex', 'crossref', 'pubmed', 'hal', 'semantic-scholar'];
public function __construct(
private readonly AcademicSearchHistoryRepository $repo,
private readonly PluginAuthResolver $authResolver,
) {}
#[Route('/history', name: 'ai_academic_history_list', methods: ['GET'], options: ['expose' => true])]
public function list(Request $request): JsonResponse
{
$user = $this->authResolver->resolve($request);
if (!$user) return new JsonResponse(['error' => 'unauthorized'], 401);
$source = (string) $request->query->get('source', '');
if (!in_array($source, self::ALLOWED_SOURCES, true)) {
return new JsonResponse(['error' => 'invalid_source', 'allowed' => self::ALLOWED_SOURCES], 400);
}
$limit = min(50, max(1, (int) $request->query->get('limit', 10)));
$entries = $this->repo->findRecent($user, $source, $limit);
return new JsonResponse([
'source' => $source,
'history' => array_map(fn($e) => [
'id' => $e->getId(),
'query' => $e->getQuery(),
'hitCount' => $e->getHitCount(),
'lastUsedAt' => $e->getLastUsedAt()->format(\DateTimeInterface::ATOM),
'createdAt' => $e->getCreatedAt()->format(\DateTimeInterface::ATOM),
], $entries),
]);
}
#[Route('/history/{id}', name: 'ai_academic_history_delete', methods: ['DELETE'], requirements: ['id' => '\d+'], options: ['expose' => true])]
public function delete(int $id, Request $request): JsonResponse
{
$user = $this->authResolver->resolve($request);
if (!$user) return new JsonResponse(['error' => 'unauthorized'], 401);
$ok = $this->repo->deleteForUser($id, $user);
if (!$ok) return new JsonResponse(['error' => 'not_found'], 404);
return new JsonResponse(['deleted' => true]);
}
#[Route('/history', name: 'ai_academic_history_clear', methods: ['DELETE'], options: ['expose' => true])]
public function clear(Request $request): JsonResponse
{
$user = $this->authResolver->resolve($request);
if (!$user) return new JsonResponse(['error' => 'unauthorized'], 401);
$source = $request->query->get('source');
if ($source !== null && !in_array($source, self::ALLOWED_SOURCES, true)) {
return new JsonResponse(['error' => 'invalid_source'], 400);
}
$count = $this->repo->clearForUser($user, $source);
return new JsonResponse(['deleted' => $count]);
}
}