using Asp.Versioning; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using TarninaHub.Application.Abstractions; using TarninaHub.Application.Dtos; using TarninaHub.Application.Mappings; using TarninaHub.Application.Security; using TarninaHub.Domain.Rules; namespace TarninaHub.Controllers; [ApiController] [ApiVersion("1.0")] [Route("api/ideas")] [Authorize] public class IdeasController : ControllerBase { private readonly IIdeaService _ideaService; private readonly IIdeaAssistant _ideaAssistant; public IdeasController(IIdeaService ideaService, IIdeaAssistant ideaAssistant) { _ideaService = ideaService; _ideaAssistant = ideaAssistant; } [HttpPost] public async Task> Create( CreateIdeaRequest request, CancellationToken cancellationToken) { var userId = User.GetUserId(); if (string.IsNullOrWhiteSpace(userId)) { return Unauthorized(); } var created = await _ideaService.CreateAsync(userId, request, cancellationToken); return CreatedAtAction(nameof(GetById), new { ideaId = created.Id }, created); } [HttpGet] public async Task>> ListMine(CancellationToken cancellationToken) { var userId = User.GetUserId(); if (string.IsNullOrWhiteSpace(userId)) { return Unauthorized(); } var ideas = await _ideaService.ListForAuthorAsync(userId, cancellationToken); return Ok(ideas); } [HttpGet("{ideaId}")] public async Task> GetById(string ideaId, CancellationToken cancellationToken) { var idea = await _ideaService.GetAsync(ideaId, cancellationToken); if (idea is null) { return NotFound(); } if (!IsOwner(idea) && !IsStaff()) { return Forbid(); } return Ok(idea); } [HttpPut("{ideaId}")] public async Task> Update( string ideaId, UpdateIdeaRequest request, CancellationToken cancellationToken) { var current = await _ideaService.GetAsync(ideaId, cancellationToken); if (current is null) { return NotFound(); } if (!IsOwner(current)) { return Forbid(); } if (!string.IsNullOrWhiteSpace(request.Status)) { var from = IdeaStatusWire.Parse(current.Status); var to = IdeaStatusWire.Parse(request.Status); if (to is null || from is null || !IdeaWorkflow.CanTransition(from.Value, to.Value)) { ModelState.AddModelError( nameof(request.Status), $"Transition from '{current.Status}' to '{request.Status}' is not allowed."); return ValidationProblem(ModelState); } } var updated = await _ideaService.UpdateAsync(ideaId, request, cancellationToken); return updated is null ? NotFound() : Ok(updated); } [HttpGet("{ideaId}/card")] public async Task> GetCard(string ideaId, CancellationToken cancellationToken) { var idea = await _ideaService.GetAsync(ideaId, cancellationToken); if (idea is null) { return NotFound(); } if (!IsOwner(idea) && !IsStaff()) { return Forbid(); } var card = await _ideaService.GetCardAsync(ideaId, cancellationToken); return card is null ? NotFound() : Ok(card); } [HttpPut("{ideaId}/card")] public async Task> UpdateCard( string ideaId, IdeaCardRequest request, CancellationToken cancellationToken) { var idea = await _ideaService.GetAsync(ideaId, cancellationToken); if (idea is null) { return NotFound(); } if (!IsOwner(idea)) { return Forbid(); } var card = await _ideaService.UpdateCardAsync(ideaId, request, cancellationToken); return card is null ? NotFound() : Ok(card); } [HttpPost("{ideaId}/assist")] public async Task> Assist( string ideaId, IdeaAssistRequest request, CancellationToken cancellationToken) { var idea = await _ideaService.GetAsync(ideaId, cancellationToken); if (idea is null) { return NotFound(); } if (!IsOwner(idea) && !IsStaff()) { return Forbid(); } var assist = await _ideaAssistant.AssistAsync(ideaId, request, cancellationToken); return assist is null ? NotFound() : Ok(assist); } private bool IsOwner(IdeaResponse idea) => string.Equals(idea.AuthorId, User.GetUserId(), StringComparison.Ordinal); private bool IsStaff() => User.IsInRole(RoleNames.Operator) || User.IsInRole(RoleNames.Manager) || User.IsInRole(RoleNames.Admin); }