using System.Security.Claims; using HelpDesk.Data.Repositories; using HelpDesk.Models.DTOs; using HelpDesk.Models.Entities; using HelpDesk.Models.Enums; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace HelpDesk.API.Controllers; [ApiController] [Route("api/[controller]")] [Authorize] public class TicketsController : ControllerBase { private readonly ITicketRepository _ticketRepository; private readonly IUserRepository _userRepository; public TicketsController(ITicketRepository ticketRepository, IUserRepository userRepository) { _ticketRepository = ticketRepository; _userRepository = userRepository; } [HttpGet] public async Task> GetTickets( [FromQuery] TicketStatus? status, [FromQuery] TicketPriority? priority, [FromQuery] Guid? assigneeId, [FromQuery] Guid? requesterId, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) { var userId = GetUserId(); if (userId == null) return Unauthorized(); var user = await _userRepository.GetByIdAsync(userId.Value); if (user == null) return Unauthorized(); if (user.Role == UserRole.Customer) requesterId = userId.Value; var tickets = await _ticketRepository.GetAllAsync(status, priority, assigneeId, requesterId, page, pageSize); var totalCount = await _ticketRepository.GetCountAsync(status, priority, assigneeId, requesterId); return Ok(new TicketListResponse { Items = tickets.Select(MapToDto).ToList(), TotalCount = totalCount, Page = page, PageSize = pageSize }); } [HttpGet("stats")] public async Task> GetStats() { return Ok(new TicketStatsDto { TotalTickets = await _ticketRepository.GetTotalCountAsync(), OpenTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.Open), InProgressTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.InProgress), PendingTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.Pending), ResolvedTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.Resolved), ClosedTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.Closed) }); } [HttpGet("{id}")] public async Task> GetTicket(Guid id) { var ticket = await _ticketRepository.GetByIdAsync(id); if (ticket == null) return NotFound(new { error = "Ticket not found." }); var userId = GetUserId(); if (userId == null) return Unauthorized(); var user = await _userRepository.GetByIdAsync(userId.Value); if (user?.Role == UserRole.Customer && ticket.RequesterId != userId.Value) return Forbid(); return Ok(MapToDto(ticket)); } [HttpPost] public async Task> CreateTicket([FromBody] CreateTicketRequest request) { if (string.IsNullOrWhiteSpace(request.Title)) return BadRequest(new { error = "Title is required." }); if (string.IsNullOrWhiteSpace(request.Description)) return BadRequest(new { error = "Description is required." }); var userId = GetUserId(); if (userId == null) return Unauthorized(); var ticket = new Ticket { Title = request.Title, Description = request.Description, Priority = request.Priority, RequesterId = userId.Value, GroupId = request.GroupId, SlaPolicyId = request.SlaPolicyId, TicketNumber = $"TK-{DateTime.UtcNow:yyyyMMddHHmmss}", Status = TicketStatus.Open, CreatedAt = DateTime.UtcNow }; var created = await _ticketRepository.CreateAsync(ticket); var full = await _ticketRepository.GetByIdAsync(created.Id); return CreatedAtAction(nameof(GetTicket), new { id = created.Id }, MapToDto(full!)); } [HttpPut("{id}")] public async Task> UpdateTicket(Guid id, [FromBody] UpdateTicketRequest request) { var ticket = await _ticketRepository.GetByIdAsync(id); if (ticket == null) return NotFound(new { error = "Ticket not found." }); if (!string.IsNullOrWhiteSpace(request.Title)) ticket.Title = request.Title; if (!string.IsNullOrWhiteSpace(request.Description)) ticket.Description = request.Description; if (request.Priority.HasValue) ticket.Priority = request.Priority.Value; var updated = await _ticketRepository.UpdateAsync(ticket); if (updated == null) return StatusCode(500, new { error = "Failed to update ticket." }); var full = await _ticketRepository.GetByIdAsync(id); return Ok(MapToDto(full!)); } [HttpDelete("{id}")] [Authorize(Policy = "Admin")] public async Task DeleteTicket(Guid id) { var deleted = await _ticketRepository.DeleteAsync(id); if (!deleted) return NotFound(new { error = "Ticket not found." }); return NoContent(); } [HttpPut("{id}/status")] [Authorize(Policy = "Agent")] public async Task UpdateStatus(Guid id, [FromBody] UpdateStatusRequest request) { var ticket = await _ticketRepository.GetByIdAsync(id); if (ticket == null) return NotFound(new { error = "Ticket not found." }); ticket.Status = request.Status; await _ticketRepository.UpdateAsync(ticket); return Ok(new { message = "Status updated.", status = request.Status.ToString() }); } [HttpPut("{id}/assign")] [Authorize(Policy = "Agent")] public async Task AssignTicket(Guid id, [FromBody] AssignTicketRequest request) { var ticket = await _ticketRepository.GetByIdAsync(id); if (ticket == null) return NotFound(new { error = "Ticket not found." }); if (request.AssigneeId.HasValue) { var assignee = await _userRepository.GetByIdAsync(request.AssigneeId.Value); if (assignee == null) return BadRequest(new { error = "Assignee not found." }); if (assignee.Role != UserRole.Agent && assignee.Role != UserRole.Admin) return BadRequest(new { error = "Assignee must be an agent or admin." }); } ticket.AssigneeId = request.AssigneeId; if (request.AssigneeId.HasValue && ticket.Status == TicketStatus.Open) ticket.Status = TicketStatus.InProgress; await _ticketRepository.UpdateAsync(ticket); return Ok(new { message = "Ticket assigned.", assigneeId = request.AssigneeId }); } private Guid? GetUserId() { var claim = User.FindFirst("userId") ?? User.FindFirst(ClaimTypes.NameIdentifier); return claim != null && Guid.TryParse(claim.Value, out var id) ? id : null; } private static TicketDto MapToDto(Ticket t) => new() { Id = t.Id, Title = t.Title, Description = t.Description, Status = t.Status, Priority = t.Priority, TicketNumber = t.TicketNumber, RequesterId = t.RequesterId, RequesterName = t.Requester != null ? $"{t.Requester.FirstName} {t.Requester.LastName}" : "Unknown", AssigneeId = t.AssigneeId, AssigneeName = t.Assignee != null ? $"{t.Assignee.FirstName} {t.Assignee.LastName}" : null, GroupId = t.GroupId, GroupName = t.Group?.Name, SlaPolicyId = t.SlaPolicyId, SlaPolicyName = t.SlaPolicy?.Name, CreatedAt = t.CreatedAt, UpdatedAt = t.UpdatedAt, ResolvedAt = t.ResolvedAt, CommentCount = t.Comments?.Count ?? 0 }; }