using MediatR;
using ColaFlow.Modules.ProjectManagement.Application.DTOs;
using ColaFlow.Modules.ProjectManagement.Application.Common.Interfaces;
using ColaFlow.Modules.ProjectManagement.Domain.Repositories;
using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects;
using ColaFlow.Modules.ProjectManagement.Domain.Exceptions;
using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate;

namespace ColaFlow.Modules.ProjectManagement.Application.Commands.CreateTask;

/// <summary>
/// Handler for CreateTaskCommand
/// </summary>
public sealed class CreateTaskCommandHandler(
    IProjectRepository projectRepository,
    IUnitOfWork unitOfWork)
    : IRequestHandler<CreateTaskCommand, TaskDto>
{
    private readonly IProjectRepository _projectRepository = projectRepository ?? throw new ArgumentNullException(nameof(projectRepository));
    private readonly IUnitOfWork _unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));

    public async Task<TaskDto> Handle(CreateTaskCommand request, CancellationToken cancellationToken)
    {
        // Get the project containing the story (Global Query Filter ensures tenant isolation)
        var storyId = StoryId.From(request.StoryId);
        var project = await _projectRepository.GetProjectWithStoryAsync(storyId, cancellationToken);

        if (project == null)
            throw new NotFoundException("Story", request.StoryId);

        // Find the story within the project aggregate
        Story? story = null;
        foreach (var epic in project.Epics)
        {
            story = epic.Stories.FirstOrDefault(s => s.Id.Value == request.StoryId);
            if (story is not null)
                break;
        }

        if (story is null)
            throw new NotFoundException("Story", request.StoryId);

        // Parse priority
        var priority = TaskPriority.FromDisplayName<TaskPriority>(request.Priority);
        var createdById = UserId.From(request.CreatedBy);

        // Create task through story aggregate
        var task = story.CreateTask(request.Title, request.Description, priority, createdById);

        // Set optional fields
        if (request.EstimatedHours.HasValue)
        {
            task.UpdateEstimate(request.EstimatedHours.Value);
        }

        if (request.AssigneeId.HasValue)
        {
            var assigneeId = UserId.From(request.AssigneeId.Value);
            task.AssignTo(assigneeId);
        }

        // Update project (task is part of aggregate)
        _projectRepository.Update(project);
        await _unitOfWork.SaveChangesAsync(cancellationToken);

        // Map to DTO
        return new TaskDto
        {
            Id = task.Id.Value,
            Title = task.Title,
            Description = task.Description,
            StoryId = task.StoryId.Value,
            Status = task.Status.Name,
            Priority = task.Priority.Name,
            AssigneeId = task.AssigneeId?.Value,
            EstimatedHours = task.EstimatedHours,
            ActualHours = task.ActualHours,
            CreatedBy = task.CreatedBy.Value,
            CreatedAt = task.CreatedAt,
            UpdatedAt = task.UpdatedAt
        };
    }
}
