using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using UniVerse.Application.DTOs.Common;
using UniVerse.Application.DTOs.Reviews;
using UniVerse.Application.Interfaces;
using System.Security.Claims;
namespace UniVerse.Api.Controllers;
/// Отзывы студентов на лекции с LLM-анализом и модерацией.
[ApiController]
[Route("api/v1/reviews")]
[Authorize]
[Produces("application/json")]
public class ReviewsController : ControllerBase
{
private readonly IReviewService _reviews;
public ReviewsController(IReviewService reviews) => _reviews = reviews;
private int CurrentUserId => int.Parse(
User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub") ?? "0");
/// Создать отзыв к лекции.
///
/// Только Student. После создания отзыв помещается в очередь LLM-анализа
/// (статус `Pending`). LLM оценивает содержательность и начисляет монеты
/// скрытно от пользователя.
///
/// ID лекции, оценка (Like/Neutral/Dislike) и текст отзыва.
/// Отзыв создан и поставлен в очередь на LLM-анализ.
/// Требуется аутентификация.
/// Требуется роль Student.
/// Лекция не найдена.
/// Студент уже оставил отзыв к этой лекции.
[Authorize(Roles = "Student")]
[HttpPost]
[ProducesResponseType(typeof(ReviewDto), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task> Create([FromBody] CreateReviewRequest req) =>
CreatedAtAction(nameof(Get), new { id = 0 }, await _reviews.CreateAsync(CurrentUserId, req));
/// Получить список всех отзывов.
/// Только Admin. Возвращает все отзывы независимо от LLM-статуса.
/// Параметры пагинации.
/// Список всех отзывов (пагинированный).
/// Требуется аутентификация.
/// Требуется роль Admin.
[Authorize(Roles = "Admin")]
[HttpGet]
[ProducesResponseType(typeof(PagedResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task List([FromQuery] PaginationRequest pagination) =>
Ok(await _reviews.GetAllAsync(pagination));
/// Получить отзыв по ID.
/// Только Admin или Teacher.
/// ID отзыва.
/// Данные отзыва (включая LLM-статус и сентимент).
/// Требуется аутентификация.
/// Требуется роль Admin или Teacher.
/// Отзыв не найден.
[Authorize(Roles = "Admin,Teacher")]
[HttpGet("{id:int}")]
[ProducesResponseType(typeof(ReviewDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task> Get(int id) => Ok(await _reviews.GetByIdAsync(id));
/// Обновить отзыв.
///
/// Разрешено любому авторизованному пользователю, но сервис проверяет владельца.
/// Изменение текста сбрасывает LLM-статус в `Pending` (повторный анализ).
///
/// ID отзыва.
/// Новая оценка и/или текст.
/// Обновлённые данные отзыва.
/// Требуется аутентификация.
/// Отзыв принадлежит другому пользователю.
/// Отзыв не найден.
[HttpPut("{id:int}")]
[ProducesResponseType(typeof(ReviewDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task> Update(int id, [FromBody] UpdateReviewRequest req) =>
Ok(await _reviews.UpdateAsync(id, CurrentUserId, req));
/// Удалить отзыв.
/// Владелец может удалить свой отзыв. Admin может удалить любой.
/// ID отзыва.
/// Отзыв удалён.
/// Требуется аутентификация.
/// Нет прав на удаление (не владелец и не Admin).
/// Отзыв не найден.
[HttpDelete("{id:int}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task Delete(int id)
{
await _reviews.DeleteAsync(id, CurrentUserId, User.IsInRole("Admin"));
return NoContent();
}
/// Получить список отзывов, ожидающих LLM-анализа.
/// Только Admin. Используется для мониторинга очереди обработки.
/// Параметры пагинации.
/// Список отзывов со статусом Pending (пагинированный).
/// Требуется аутентификация.
/// Требуется роль Admin.
[Authorize(Roles = "Admin")]
[HttpGet("pending")]
[ProducesResponseType(typeof(PagedResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task Pending([FromQuery] PaginationRequest pagination) =>
Ok(await _reviews.GetPendingAsync(pagination));
/// Запустить повторный LLM-анализ отзыва.
///
/// Только Admin. Сбрасывает статус отзыва на `Pending` и ставит его
/// в очередь на повторную обработку фоновым сервисом.
///
/// ID отзыва.
/// Повторный анализ запланирован.
/// Требуется аутентификация.
/// Требуется роль Admin.
/// Отзыв не найден.
[Authorize(Roles = "Admin")]
[HttpPost("{id:int}/reanalyze")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task Reanalyze(int id)
{
await _reviews.ReanalyzeAsync(id);
return NoContent();
}
}