using ColaFlow.Modules.Mcp.Application.DTOs.Notifications;
using ColaFlow.Modules.Mcp.Application.EventHandlers;
using ColaFlow.Modules.Mcp.Application.Services;
using ColaFlow.Modules.Mcp.Domain.Entities;
using ColaFlow.Modules.Mcp.Domain.Events;
using ColaFlow.Modules.Mcp.Domain.Repositories;
using ColaFlow.Modules.Mcp.Domain.ValueObjects;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace ColaFlow.Modules.Mcp.Tests.EventHandlers;

public class PendingChangeCreatedNotificationHandlerTests
{
    private readonly Mock<IMcpNotificationService> _mockNotificationService;
    private readonly Mock<IPendingChangeRepository> _mockRepository;
    private readonly Mock<ILogger<PendingChangeCreatedNotificationHandler>> _mockLogger;
    private readonly PendingChangeCreatedNotificationHandler _handler;

    public PendingChangeCreatedNotificationHandlerTests()
    {
        _mockNotificationService = new Mock<IMcpNotificationService>();
        _mockRepository = new Mock<IPendingChangeRepository>();
        _mockLogger = new Mock<ILogger<PendingChangeCreatedNotificationHandler>>();

        _handler = new PendingChangeCreatedNotificationHandler(
            _mockNotificationService.Object,
            _mockRepository.Object,
            _mockLogger.Object);
    }

    [Fact]
    public async Task Handle_SendsNotification_WhenPendingChangeExists()
    {
        // Arrange
        var pendingChangeId = Guid.NewGuid();
        var tenantId = Guid.NewGuid();
        var apiKeyId = Guid.NewGuid();

        var domainEvent = new PendingChangeCreatedEvent(
            pendingChangeId,
            "create_epic",
            "Epic",
            "CREATE",
            tenantId);

        var diff = new DiffPreview(
            "CREATE",
            "Epic",
            null,
            null,
            null,
            "{\"name\":\"Test Epic\"}",
            new List<DiffField>());

        var pendingChange = PendingChange.Create(
            "create_epic",
            diff,
            tenantId,
            apiKeyId,
            12);

        _mockRepository.Setup(r => r.GetByIdAsync(pendingChangeId, It.IsAny<CancellationToken>()))
            .ReturnsAsync(pendingChange);

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

        // Assert
        _mockNotificationService.Verify(
            s => s.NotifyPendingChangeCreatedAsync(
                It.Is<PendingChangeCreatedNotification>(n =>
                    n.PendingChangeId == pendingChangeId &&
                    n.ToolName == "create_epic" &&
                    n.EntityType == "Epic" &&
                    n.Operation == "CREATE" &&
                    n.TenantId == tenantId),
                It.IsAny<CancellationToken>()),
            Times.Once);
    }

    [Fact]
    public async Task Handle_DoesNotSendNotification_WhenPendingChangeNotFound()
    {
        // Arrange
        var pendingChangeId = Guid.NewGuid();
        var tenantId = Guid.NewGuid();

        var domainEvent = new PendingChangeCreatedEvent(
            pendingChangeId,
            "create_epic",
            "Epic",
            "CREATE",
            tenantId);

        _mockRepository.Setup(r => r.GetByIdAsync(pendingChangeId, It.IsAny<CancellationToken>()))
            .ReturnsAsync((PendingChange?)null);

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

        // Assert
        _mockNotificationService.Verify(
            s => s.NotifyPendingChangeCreatedAsync(
                It.IsAny<PendingChangeCreatedNotification>(),
                It.IsAny<CancellationToken>()),
            Times.Never);
    }

    [Fact]
    public async Task Handle_DoesNotThrow_WhenNotificationServiceFails()
    {
        // Arrange
        var pendingChangeId = Guid.NewGuid();
        var tenantId = Guid.NewGuid();
        var apiKeyId = Guid.NewGuid();

        var domainEvent = new PendingChangeCreatedEvent(
            pendingChangeId,
            "create_epic",
            "Epic",
            "CREATE",
            tenantId);

        var diff = new DiffPreview(
            "CREATE",
            "Epic",
            null,
            null,
            null,
            "{\"name\":\"Test Epic\"}",
            new List<DiffField>());

        var pendingChange = PendingChange.Create(
            "create_epic",
            diff,
            tenantId,
            apiKeyId,
            12);

        _mockRepository.Setup(r => r.GetByIdAsync(pendingChangeId, It.IsAny<CancellationToken>()))
            .ReturnsAsync(pendingChange);

        _mockNotificationService.Setup(s => s.NotifyPendingChangeCreatedAsync(
                It.IsAny<PendingChangeCreatedNotification>(),
                It.IsAny<CancellationToken>()))
            .ThrowsAsync(new Exception("SignalR connection failed"));

        // Act & Assert - Should not throw
        await _handler.Handle(domainEvent, CancellationToken.None);
    }
}
