using FluentAssertions;
using Moq;
using ColaFlow.Modules.ProjectManagement.Application.Commands.DeleteStory;
using ColaFlow.Modules.ProjectManagement.Application.Common.Interfaces;
using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate;
using ColaFlow.Modules.ProjectManagement.Domain.Repositories;
using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects;
using ColaFlow.Modules.ProjectManagement.Domain.Exceptions;

namespace ColaFlow.Application.Tests.Commands.DeleteStory;

public class DeleteStoryCommandHandlerTests
{
    private readonly Mock<IProjectRepository> _projectRepositoryMock;
    private readonly Mock<IUnitOfWork> _unitOfWorkMock;
    private readonly Mock<ITenantContext> _tenantContextMock;
    private readonly DeleteStoryCommandHandler _handler;
    private readonly Guid _tenantId;

    public DeleteStoryCommandHandlerTests()
    {
        _projectRepositoryMock = new Mock<IProjectRepository>();
        _unitOfWorkMock = new Mock<IUnitOfWork>();
        _tenantContextMock = new Mock<ITenantContext>();
        _tenantId = Guid.NewGuid();

        _tenantContextMock.Setup(x => x.GetCurrentTenantId()).Returns(_tenantId);
        _handler = new DeleteStoryCommandHandler(_projectRepositoryMock.Object, _unitOfWorkMock.Object, _tenantContextMock.Object);
    }

    [Fact]
    public async Task Should_Delete_Story_Successfully()
    {
        // Arrange
        var userId = UserId.Create();
        var project = Project.Create(TenantId.Create(_tenantId), "Test Project", "Description", "TST", userId);
        var epic = project.CreateEpic("Test Epic", "Epic Description", userId);
        var story = epic.CreateStory("Story to Delete", "Description", TaskPriority.Medium, userId);
        var storyId = story.Id;

        _projectRepositoryMock
            .Setup(x => x.GetProjectWithStoryAsync(storyId, It.IsAny<CancellationToken>()))
            .ReturnsAsync(project);

        var command = new DeleteStoryCommand { StoryId = storyId.Value };

        // Act
        await _handler.Handle(command, CancellationToken.None);

        // Assert
        epic.Stories.Should().BeEmpty();
        _projectRepositoryMock.Verify(x => x.Update(project), Times.Once);
        _unitOfWorkMock.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once);
    }

    [Fact]
    public async Task Should_Fail_When_Story_Not_Found()
    {
        // Arrange
        var storyId = StoryId.Create();
        _projectRepositoryMock
            .Setup(x => x.GetProjectWithStoryAsync(storyId, It.IsAny<CancellationToken>()))
            .ReturnsAsync((Project?)null);

        var command = new DeleteStoryCommand { StoryId = storyId.Value };

        // Act
        Func<Task> act = async () => await _handler.Handle(command, CancellationToken.None);

        // Assert
        await act.Should().ThrowAsync<NotFoundException>()
            .WithMessage("*Story*");
    }

    [Fact]
    public async Task Should_Fail_When_Story_Has_Tasks()
    {
        // Arrange
        var userId = UserId.Create();
        var project = Project.Create(TenantId.Create(_tenantId), "Test Project", "Description", "TST", userId);
        var epic = project.CreateEpic("Test Epic", "Epic Description", userId);
        var story = epic.CreateStory("Story with Tasks", "Description", TaskPriority.Medium, userId);

        // Add a task to the story
        story.CreateTask("Task 1", "Task Description", TaskPriority.High, userId);

        _projectRepositoryMock
            .Setup(x => x.GetProjectWithStoryAsync(story.Id, It.IsAny<CancellationToken>()))
            .ReturnsAsync(project);

        var command = new DeleteStoryCommand { StoryId = story.Id.Value };

        // Act
        Func<Task> act = async () => await _handler.Handle(command, CancellationToken.None);

        // Assert
        await act.Should().ThrowAsync<DomainException>()
            .WithMessage("*cannot delete*story*tasks*");
    }
}
