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

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

    public async Task<StoryDto> Handle(UpdateStoryCommand request, CancellationToken cancellationToken)
    {
        // Get current tenant ID (Defense in Depth - Layer 2)
        var currentTenantId = _tenantContext.GetCurrentTenantId();

        // Get the project with 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);

        // CRITICAL SECURITY: Explicit TenantId validation (Defense in Depth)
        if (project.TenantId.Value != currentTenantId)
            throw new NotFoundException("Story", request.StoryId);

        // Find the story
        var story = project.Epics
            .SelectMany(e => e.Stories)
            .FirstOrDefault(s => s.Id.Value == request.StoryId);

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

        // Update basic details
        story.UpdateDetails(request.Title, request.Description);

        // Update status if provided
        if (!string.IsNullOrEmpty(request.Status))
        {
            var status = request.Status switch
            {
                "To Do" => WorkItemStatus.ToDo,
                "In Progress" => WorkItemStatus.InProgress,
                "In Review" => WorkItemStatus.InReview,
                "Done" => WorkItemStatus.Done,
                "Blocked" => WorkItemStatus.Blocked,
                _ => throw new DomainException($"Invalid status: {request.Status}")
            };
            story.UpdateStatus(status);
        }

        // Update priority if provided
        if (!string.IsNullOrEmpty(request.Priority))
        {
            var priority = TaskPriority.FromDisplayName<TaskPriority>(request.Priority);
            story.UpdatePriority(priority);
        }

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

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

        // Save changes
        _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 = story.Tasks.Select(t => new TaskDto
            {
                Id = t.Id.Value,
                Title = t.Title,
                Description = t.Description,
                StoryId = t.StoryId.Value,
                Status = t.Status.Name,
                Priority = t.Priority.Name,
                AssigneeId = t.AssigneeId?.Value,
                EstimatedHours = t.EstimatedHours,
                ActualHours = t.ActualHours,
                CreatedBy = t.CreatedBy.Value,
                CreatedAt = t.CreatedAt,
                UpdatedAt = t.UpdatedAt
            }).ToList()
        };
    }
}
