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;
using ColaFlow.Modules.ProjectManagement.Domain.Events;

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

/// <summary>
/// Handler for CreateSprintCommand
/// </summary>
public sealed class CreateSprintCommandHandler(
    IProjectRepository projectRepository,
    IApplicationDbContext context,
    IUnitOfWork unitOfWork,
    IMediator mediator)
    : IRequestHandler<CreateSprintCommand, SprintDto>
{
    private readonly IProjectRepository _projectRepository = projectRepository ?? throw new ArgumentNullException(nameof(projectRepository));
    private readonly IApplicationDbContext _context = context ?? throw new ArgumentNullException(nameof(context));
    private readonly IUnitOfWork _unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
    private readonly IMediator _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));

    public async Task<SprintDto> Handle(CreateSprintCommand request, CancellationToken cancellationToken)
    {
        // Verify project exists (Global Query Filter ensures tenant isolation)
        var projectId = ProjectId.From(request.ProjectId);
        var project = await _projectRepository.GetByIdAsync(projectId, cancellationToken);

        if (project == null)
            throw new NotFoundException("Project", request.ProjectId);

        // Create sprint
        var createdById = UserId.From(request.CreatedBy);
        var sprint = Sprint.Create(
            project.TenantId,
            projectId,
            request.Name,
            request.Goal,
            request.StartDate,
            request.EndDate,
            createdById
        );

        // Add to context
        await _context.Sprints.AddAsync(sprint, cancellationToken);
        await _unitOfWork.SaveChangesAsync(cancellationToken);

        // Publish domain event
        await _mediator.Publish(new SprintCreatedEvent(sprint.Id.Value, sprint.Name, projectId.Value), cancellationToken);

        // Map to DTO
        return new SprintDto
        {
            Id = sprint.Id.Value,
            ProjectId = sprint.ProjectId.Value,
            ProjectName = project.Name,
            Name = sprint.Name,
            Goal = sprint.Goal,
            StartDate = sprint.StartDate,
            EndDate = sprint.EndDate,
            Status = sprint.Status.Name,
            TotalTasks = 0,
            CompletedTasks = 0,
            TotalStoryPoints = 0,
            RemainingStoryPoints = 0,
            TaskIds = new List<Guid>(),
            CreatedAt = sprint.CreatedAt,
            CreatedBy = sprint.CreatedBy.Value,
            UpdatedAt = sprint.UpdatedAt
        };
    }
}
