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;

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

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

    public async Task<StoryDto> Handle(CreateStoryCommand request, CancellationToken cancellationToken)
    {
        // Get the project with epic (Global Query Filter ensures tenant isolation)
        var epicId = EpicId.From(request.EpicId);
        var project = await _projectRepository.GetProjectWithEpicAsync(epicId, cancellationToken);

        if (project == null)
            throw new NotFoundException("Epic", request.EpicId);

        // Find the epic
        var epic = project.Epics.FirstOrDefault(e => e.Id.Value == request.EpicId);
        if (epic == null)
            throw new NotFoundException("Epic", request.EpicId);

        // Parse priority
        var priority = request.Priority switch
        {
            "Low" => TaskPriority.Low,
            "Medium" => TaskPriority.Medium,
            "High" => TaskPriority.High,
            "Urgent" => TaskPriority.Urgent,
            _ => TaskPriority.Medium
        };

        // Create story through epic
        var createdById = UserId.From(request.CreatedBy);
        var story = epic.CreateStory(request.Title, request.Description, priority, createdById);

        // Assign if assignee provided
        if (request.AssigneeId.HasValue)
        {
            var assigneeId = UserId.From(request.AssigneeId.Value);
            story.AssignTo(assigneeId);
        }

        // Set estimated hours if provided
        if (request.EstimatedHours.HasValue)
        {
            story.UpdateEstimate(request.EstimatedHours.Value);
        }

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

        // Map to DTO
        return new StoryDto
        {
            Id = story.Id.Value,
            Title = story.Title,
            Description = story.Description,
            EpicId = story.EpicId.Value,
            Status = story.Status.Name,
            Priority = story.Priority.Name,
            AssigneeId = story.AssigneeId?.Value,
            EstimatedHours = story.EstimatedHours,
            ActualHours = story.ActualHours,
            CreatedBy = story.CreatedBy.Value,
            CreatedAt = story.CreatedAt,
            UpdatedAt = story.UpdatedAt,
            Tasks = new List<TaskDto>()
        };
    }
}
