using System.Security.Claims; using HelpDesk.Data.Repositories; using HelpDesk.Models.DTOs; using HelpDesk.Models.Entities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace HelpDesk.API.Controllers; [ApiController] [Route("api/kb")] [Authorize] public class KnowledgeBaseController : ControllerBase { private readonly IKnowledgeBaseRepository _kbRepository; public KnowledgeBaseController(IKnowledgeBaseRepository kbRepository) { _kbRepository = kbRepository; } // ===== Articles ===== [HttpGet("articles")] [Authorize(Policy = "Agent")] public async Task>> GetAllArticles() { var articles = await _kbRepository.GetAllArticlesAsync(); return Ok(articles.Select(MapArticleToDto)); } [HttpGet("articles/published")] [AllowAnonymous] public async Task>> GetPublishedArticles() { var articles = await _kbRepository.GetPublishedArticlesAsync(); return Ok(articles.Select(MapArticleToDto)); } [HttpGet("search")] [AllowAnonymous] public async Task>> SearchArticles([FromQuery] string q) { if (string.IsNullOrWhiteSpace(q)) return BadRequest(new { error = "Search query is required." }); var articles = await _kbRepository.SearchArticlesAsync(q); return Ok(articles.Select(MapArticleToDto)); } [HttpGet("articles/{id}")] public async Task> GetArticle(Guid id) { var article = await _kbRepository.GetArticleByIdAsync(id); if (article == null) return NotFound(new { error = "Article not found." }); return Ok(MapArticleToDto(article)); } [HttpPost("articles")] [Authorize(Policy = "Agent")] public async Task> CreateArticle([FromBody] CreateArticleRequest request) { if (string.IsNullOrWhiteSpace(request.Title)) return BadRequest(new { error = "Title is required." }); if (string.IsNullOrWhiteSpace(request.Content)) return BadRequest(new { error = "Content is required." }); var userId = GetUserId(); if (userId == null) return Unauthorized(); var article = new KnowledgeBaseArticle { Title = request.Title, Content = request.Content, CategoryId = request.CategoryId, IsPublished = request.IsPublished, AuthorId = userId.Value, CreatedAt = DateTime.UtcNow }; var created = await _kbRepository.CreateArticleAsync(article); var full = await _kbRepository.GetArticleByIdAsync(created.Id); return CreatedAtAction(nameof(GetArticle), new { id = created.Id }, MapArticleToDto(full!)); } [HttpPut("articles/{id}")] [Authorize(Policy = "Agent")] public async Task> UpdateArticle(Guid id, [FromBody] UpdateArticleRequest request) { var article = await _kbRepository.GetArticleByIdAsync(id); if (article == null) return NotFound(new { error = "Article not found." }); if (request.Title != null) article.Title = request.Title; if (request.Content != null) article.Content = request.Content; if (request.CategoryId.HasValue) article.CategoryId = request.CategoryId.Value; if (request.IsPublished.HasValue) article.IsPublished = request.IsPublished.Value; var updated = await _kbRepository.UpdateArticleAsync(article); if (updated == null) return StatusCode(500, new { error = "Failed to update article." }); var full = await _kbRepository.GetArticleByIdAsync(id); return Ok(MapArticleToDto(full!)); } [HttpDelete("articles/{id}")] [Authorize(Policy = "Admin")] public async Task DeleteArticle(Guid id) { var deleted = await _kbRepository.DeleteArticleAsync(id); if (!deleted) return NotFound(new { error = "Article not found." }); return NoContent(); } // ===== Categories ===== [HttpGet("categories")] public async Task>> GetCategories() { var categories = await _kbRepository.GetAllCategoriesAsync(); return Ok(categories.Select(MapCategoryToDto)); } [HttpGet("categories/{id}")] public async Task> GetCategory(Guid id) { var category = await _kbRepository.GetCategoryByIdAsync(id); if (category == null) return NotFound(new { error = "Category not found." }); return Ok(MapCategoryToDto(category)); } [HttpPost("categories")] [Authorize(Policy = "Admin")] public async Task> CreateCategory([FromBody] CreateCategoryRequest request) { if (string.IsNullOrWhiteSpace(request.Name)) return BadRequest(new { error = "Name is required." }); var category = new KnowledgeBaseCategory { Name = request.Name, Description = request.Description, SortOrder = request.SortOrder, CreatedAt = DateTime.UtcNow }; var created = await _kbRepository.CreateCategoryAsync(category); return CreatedAtAction(nameof(GetCategory), new { id = created.Id }, MapCategoryToDto(created)); } [HttpPut("categories/{id}")] [Authorize(Policy = "Admin")] public async Task> UpdateCategory(Guid id, [FromBody] CreateCategoryRequest request) { var category = await _kbRepository.GetCategoryByIdAsync(id); if (category == null) return NotFound(new { error = "Category not found." }); category.Name = request.Name; category.Description = request.Description; category.SortOrder = request.SortOrder; var updated = await _kbRepository.UpdateCategoryAsync(category); if (updated == null) return StatusCode(500, new { error = "Failed to update category." }); return Ok(MapCategoryToDto(updated)); } [HttpDelete("categories/{id}")] [Authorize(Policy = "Admin")] public async Task DeleteCategory(Guid id) { var deleted = await _kbRepository.DeleteCategoryAsync(id); if (!deleted) return NotFound(new { error = "Category not found." }); return NoContent(); } 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 ArticleDto MapArticleToDto(KnowledgeBaseArticle a) => new() { Id = a.Id, Title = a.Title, Content = a.Content, IsPublished = a.IsPublished, CategoryId = a.CategoryId, CategoryName = a.Category?.Name ?? "Unknown", AuthorId = a.AuthorId, AuthorName = a.Author != null ? $"{a.Author.FirstName} {a.Author.LastName}" : "Unknown", ViewCount = a.ViewCount, CreatedAt = a.CreatedAt, UpdatedAt = a.UpdatedAt }; private static CategoryDto MapCategoryToDto(KnowledgeBaseCategory c) => new() { Id = c.Id, Name = c.Name, Description = c.Description, SortOrder = c.SortOrder, ArticleCount = c.Articles?.Count ?? 0, CreatedAt = c.CreatedAt }; }