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.UpdateTaskStatus;

/// <summary>
/// Handler for UpdateTaskStatusCommand
/// </summary>
public sealed class UpdateTaskStatusCommandHandler(
    IProjectRepository projectRepository,
    IUnitOfWork unitOfWork)
    : IRequestHandler<UpdateTaskStatusCommand, 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(UpdateTaskStatusCommand request, CancellationToken cancellationToken)
    {
        // Get the project containing the task (Global Query Filter ensures tenant isolation)
        var taskId = TaskId.From(request.TaskId);
        var project = await _projectRepository.GetProjectWithTaskAsync(taskId, cancellationToken);

        if (project == null)
            throw new NotFoundException("Task", request.TaskId);

        // Find the task within the project aggregate
        WorkTask? task = null;
        foreach (var epic in project.Epics)
        {
            foreach (var story in epic.Stories)
            {
                task = story.Tasks.FirstOrDefault(t => t.Id.Value == request.TaskId);
                if (task != null)
                    break;
            }
            if (task != null)
                break;
        }

        if (task == null)
            throw new NotFoundException("Task", request.TaskId);

        // Parse and validate new status
        var newStatus = WorkItemStatus.FromDisplayName<WorkItemStatus>(request.NewStatus);

        // Validate status transition (business rule)
        ValidateStatusTransition(task.Status, newStatus);

        // Update task status
        task.UpdateStatus(newStatus);

        // Update project
        _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
        };
    }

    private void ValidateStatusTransition(WorkItemStatus currentStatus, WorkItemStatus newStatus)
    {
        // Business rule: Can't move from Done back to ToDo
        if (currentStatus == WorkItemStatus.Done && newStatus == WorkItemStatus.ToDo)
        {
            throw new DomainException("Cannot move a completed task back to ToDo. Please create a new task instead.");
        }

        // Business rule: Blocked can be moved to any status
        // Business rule: Any status can be moved to Blocked
        // All other transitions are allowed
    }
}
