File size: 5,517 Bytes
f0d69b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47e77e5
f0d69b8
47e77e5
f0d69b8
 
47e77e5
f0d69b8
 
 
 
 
 
 
7f6afc2
f0d69b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7f6afc2
f0d69b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64a3c1d
 
 
47e77e5
f0d69b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ecb51fa
f0d69b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using TaskTrackingSystem.Database.AppDbContextModels;
using TaskTrackingSystem.Shared;
using TaskTrackingSystem.Shared.Models.Comment;
using TaskTrackingSystem.WebApi.Infrastructure;

namespace TaskTrackingSystem.WebApi.Features.Task
{
    [Route("api/[controller]")]
    [ApiController]
    [Authorize]
    public class TaskDetailsController : ControllerBase
    {
        private readonly AppDbContext _db;
        private readonly TaskTrackingSystem.WebApi.Features.Notification.FirebaseNotificationService _notificationService;

        public TaskDetailsController(AppDbContext db, TaskTrackingSystem.WebApi.Features.Notification.FirebaseNotificationService notificationService)
        {
            _db = db;
            _notificationService = notificationService;
        }

        // ─── COMMENTS ────────────────────────────────────────────────────────

        [HttpGet("tasks/{taskId}/comments")]
        public async Task<ActionResult<IEnumerable<CommentDto>>> GetComments(long taskId)
        {
            var taskExists = await _db.Tasks.AnyAsync(t => t.Id == taskId && !t.IsDeleted && !t.IsArchived);
            if (!taskExists)
            {
                return NotFound(new { message = $"Task with ID {taskId} not found." });
            }

            var comments = await _db.Comments
                .Include(c => c.User)
                .ThenInclude(u => u.Role)
                .Where(c => c.TaskId == taskId && !c.IsDeleted)
                .OrderBy(c => c.CreatedAt)
                .Select(c => new CommentDto
                {
                    Id = c.Id,
                    TaskId = c.TaskId,
                    UserId = c.UserId,
                    UserFullName = $"{c.User.FirstName} {c.User.LastName}",
                    UserRoleName = c.User.Role != null ? c.User.Role.Name : "User",
                    Message = c.Message,
                    CreatedAt = c.CreatedAt ?? DateTime.UtcNow,
                    UpdatedAt = c.UpdatedAt
                })
                .ToListAsync();

            return Ok(comments);
        }

        [HttpPost("tasks/{taskId}/comments")]
        public async Task<ActionResult<Result<CommentDto>>> CreateComment(long taskId, [FromBody] CreateCommentDto dto)
        {
            if (string.IsNullOrWhiteSpace(dto.Message))
            {
                return BadRequest(Result<CommentDto>.Failure("Comment message cannot be empty.", 400));
            }

            var task = await _db.Tasks.FirstOrDefaultAsync(t => t.Id == taskId && !t.IsDeleted && !t.IsArchived);
            if (task == null)
            {
                return NotFound(Result<CommentDto>.Failure($"Task with ID {taskId} not found.", 404));
            }

            var userId = User.GetUserId();
            var user = await _db.Users
                .Include(u => u.Role)
                .FirstOrDefaultAsync(u => u.Id == userId);

            if (user == null)
            {
                return Unauthorized();
            }

            var comment = new Comment
            {
                TaskId = taskId,
                UserId = userId,
                Message = dto.Message.Trim(),
                CreatedAt = DateTime.UtcNow,
                CreatedBy = userId,
                IsDeleted = false
            };

            _db.Comments.Add(comment);
            await _db.SaveChangesAsync();

            var actorName = $"{user.FirstName} {user.LastName}";
            await _notificationService.NotifyCommentAddedAsync(task, userId, actorName, comment.Message);
            await _notificationService.NotifyMentionedAsync(task, userId, actorName, comment.Message);

            var resultDto = new CommentDto
            {
                Id = comment.Id,
                TaskId = comment.TaskId,
                UserId = comment.UserId,
                UserFullName = $"{user.FirstName} {user.LastName}",
                UserRoleName = user.Role != null ? user.Role.Name : "User",
                Message = comment.Message,
                CreatedAt = comment.CreatedAt ?? DateTime.UtcNow
            };

            return StatusCode(201, Result<CommentDto>.Success(resultDto, 201));
        }

        [HttpDelete("comments/{commentId}")]
        public async Task<ActionResult<Result>> DeleteComment(long commentId)
        {
            var comment = await _db.Comments.FirstOrDefaultAsync(c => c.Id == commentId && !c.IsDeleted);
            if (comment == null)
            {
                return NotFound(Result.Failure("Comment not found.", 404));
            }

            var userId = User.GetUserId();
            var isAdmin = await DataScopeAuthorization.IsAdminScopeAsync(_db, User.GetRoleId());

            // Only comment creator or Admin can delete
            if (comment.UserId != userId && !isAdmin)
            {
                return Forbid();
            }

            comment.IsDeleted = true;
            comment.UpdatedAt = DateTime.UtcNow;
            comment.UpdatedBy = userId;

            _db.Comments.Update(comment);
            await _db.SaveChangesAsync();

            return Ok(Result.Success(200));
        }
    }
}