using ColaFlow.Modules.Mcp.Domain.ValueObjects;
using Xunit;

namespace ColaFlow.Modules.Mcp.Tests.Domain;

public class DiffPreviewTests
{
    [Fact]
    public void ForCreate_ValidInput_Success()
    {
        // Arrange & Act
        var diff = DiffPreview.ForCreate(
            entityType: "Epic",
            afterData: "{\"title\": \"New Epic\"}",
            entityKey: "COLA-1"
        );

        // Assert
        Assert.Equal("CREATE", diff.Operation);
        Assert.Equal("Epic", diff.EntityType);
        Assert.Null(diff.EntityId);
        Assert.Equal("COLA-1", diff.EntityKey);
        Assert.Null(diff.BeforeData);
        Assert.Equal("{\"title\": \"New Epic\"}", diff.AfterData);
        Assert.Empty(diff.ChangedFields);
        Assert.True(diff.IsCreate());
        Assert.False(diff.IsUpdate());
        Assert.False(diff.IsDelete());
    }

    [Fact]
    public void ForUpdate_ValidInput_Success()
    {
        // Arrange
        var entityId = Guid.NewGuid();
        var changedFields = new List<DiffField>
        {
            new DiffField("Title", "Title", "Old", "New")
        }.AsReadOnly();

        // Act
        var diff = DiffPreview.ForUpdate(
            entityType: "Story",
            entityId: entityId,
            beforeData: "{\"title\": \"Old\"}",
            afterData: "{\"title\": \"New\"}",
            changedFields: changedFields,
            entityKey: "COLA-2"
        );

        // Assert
        Assert.Equal("UPDATE", diff.Operation);
        Assert.Equal("Story", diff.EntityType);
        Assert.Equal(entityId, diff.EntityId);
        Assert.Equal("COLA-2", diff.EntityKey);
        Assert.Equal("{\"title\": \"Old\"}", diff.BeforeData);
        Assert.Equal("{\"title\": \"New\"}", diff.AfterData);
        Assert.Single(diff.ChangedFields);
        Assert.False(diff.IsCreate());
        Assert.True(diff.IsUpdate());
        Assert.False(diff.IsDelete());
    }

    [Fact]
    public void ForDelete_ValidInput_Success()
    {
        // Arrange
        var entityId = Guid.NewGuid();

        // Act
        var diff = DiffPreview.ForDelete(
            entityType: "Task",
            entityId: entityId,
            beforeData: "{\"title\": \"Task to delete\"}",
            entityKey: "COLA-3"
        );

        // Assert
        Assert.Equal("DELETE", diff.Operation);
        Assert.Equal("Task", diff.EntityType);
        Assert.Equal(entityId, diff.EntityId);
        Assert.Equal("COLA-3", diff.EntityKey);
        Assert.Equal("{\"title\": \"Task to delete\"}", diff.BeforeData);
        Assert.Null(diff.AfterData);
        Assert.Empty(diff.ChangedFields);
        Assert.False(diff.IsCreate());
        Assert.False(diff.IsUpdate());
        Assert.True(diff.IsDelete());
    }

    [Fact]
    public void Create_InvalidOperation_ThrowsException()
    {
        // Act & Assert
        Assert.Throws<ArgumentException>(() => new DiffPreview(
            operation: "INVALID",
            entityType: "Epic",
            entityId: null,
            entityKey: null,
            beforeData: null,
            afterData: "{}"
        ));
    }

    [Fact]
    public void Create_EmptyOperation_ThrowsException()
    {
        // Act & Assert
        Assert.Throws<ArgumentException>(() => new DiffPreview(
            operation: "",
            entityType: "Epic",
            entityId: null,
            entityKey: null,
            beforeData: null,
            afterData: "{}"
        ));
    }

    [Fact]
    public void Create_EmptyEntityType_ThrowsException()
    {
        // Act & Assert
        Assert.Throws<ArgumentException>(() => new DiffPreview(
            operation: "CREATE",
            entityType: "",
            entityId: null,
            entityKey: null,
            beforeData: null,
            afterData: "{}"
        ));
    }

    [Fact]
    public void Create_UpdateWithoutEntityId_ThrowsException()
    {
        // Arrange
        var changedFields = new List<DiffField>
        {
            new DiffField("Title", "Title", "Old", "New")
        }.AsReadOnly();

        // Act & Assert
        Assert.Throws<ArgumentException>(() => new DiffPreview(
            operation: "UPDATE",
            entityType: "Epic",
            entityId: null,
            entityKey: null,
            beforeData: "{}",
            afterData: "{}",
            changedFields: changedFields
        ));
    }

    [Fact]
    public void Create_UpdateWithoutChangedFields_ThrowsException()
    {
        // Act & Assert
        Assert.Throws<ArgumentException>(() => new DiffPreview(
            operation: "UPDATE",
            entityType: "Epic",
            entityId: Guid.NewGuid(),
            entityKey: null,
            beforeData: "{}",
            afterData: "{}",
            changedFields: null
        ));
    }

    [Fact]
    public void Create_UpdateWithEmptyChangedFields_ThrowsException()
    {
        // Act & Assert
        Assert.Throws<ArgumentException>(() => new DiffPreview(
            operation: "UPDATE",
            entityType: "Epic",
            entityId: Guid.NewGuid(),
            entityKey: null,
            beforeData: "{}",
            afterData: "{}",
            changedFields: new List<DiffField>().AsReadOnly()
        ));
    }

    [Fact]
    public void Create_DeleteWithoutEntityId_ThrowsException()
    {
        // Act & Assert
        Assert.Throws<ArgumentException>(() => new DiffPreview(
            operation: "DELETE",
            entityType: "Epic",
            entityId: null,
            entityKey: null,
            beforeData: "{}",
            afterData: null
        ));
    }

    [Fact]
    public void Create_CreateWithoutAfterData_ThrowsException()
    {
        // Act & Assert
        Assert.Throws<ArgumentException>(() => new DiffPreview(
            operation: "CREATE",
            entityType: "Epic",
            entityId: null,
            entityKey: null,
            beforeData: null,
            afterData: null
        ));
    }

    [Fact]
    public void Equality_SameValues_AreEqual()
    {
        // Arrange
        var diff1 = DiffPreview.ForCreate("Epic", "{\"title\": \"New Epic\"}", "COLA-1");
        var diff2 = DiffPreview.ForCreate("Epic", "{\"title\": \"New Epic\"}", "COLA-1");

        // Act & Assert
        Assert.Equal(diff1, diff2);
        Assert.True(diff1 == diff2);
        Assert.False(diff1 != diff2);
    }

    [Fact]
    public void Equality_DifferentValues_AreNotEqual()
    {
        // Arrange
        var diff1 = DiffPreview.ForCreate("Epic", "{\"title\": \"Epic 1\"}", "COLA-1");
        var diff2 = DiffPreview.ForCreate("Epic", "{\"title\": \"Epic 2\"}", "COLA-2");

        // Act & Assert
        Assert.NotEqual(diff1, diff2);
        Assert.False(diff1 == diff2);
        Assert.True(diff1 != diff2);
    }

    [Fact]
    public void GetSummary_Create_ReturnsFormattedString()
    {
        // Arrange
        var diff = DiffPreview.ForCreate("Epic", "{}", "COLA-1");

        // Act
        var summary = diff.GetSummary();

        // Assert
        Assert.Equal("CREATE Epic (COLA-1)", summary);
    }

    [Fact]
    public void GetSummary_Update_ReturnsFormattedString()
    {
        // Arrange
        var entityId = Guid.NewGuid();
        var changedFields = new List<DiffField>
        {
            new DiffField("Title", "Title", "Old", "New")
        }.AsReadOnly();
        var diff = DiffPreview.ForUpdate("Story", entityId, "{}", "{}", changedFields, "COLA-2");

        // Act
        var summary = diff.GetSummary();

        // Assert
        Assert.Equal("UPDATE Story (COLA-2)", summary);
    }

    [Fact]
    public void GetSummary_Delete_ReturnsFormattedString()
    {
        // Arrange
        var entityId = Guid.NewGuid();
        var diff = DiffPreview.ForDelete("Task", entityId, "{}", "COLA-3");

        // Act
        var summary = diff.GetSummary();

        // Assert
        Assert.Equal("DELETE Task (COLA-3)", summary);
    }

    [Fact]
    public void GetChangedFieldCount_Create_ReturnsZero()
    {
        // Arrange
        var diff = DiffPreview.ForCreate("Epic", "{}");

        // Act
        var count = diff.GetChangedFieldCount();

        // Assert
        Assert.Equal(0, count);
    }

    [Fact]
    public void GetChangedFieldCount_Update_ReturnsCorrectCount()
    {
        // Arrange
        var changedFields = new List<DiffField>
        {
            new DiffField("Title", "Title", "Old", "New"),
            new DiffField("Status", "Status", "Todo", "Done")
        }.AsReadOnly();
        var diff = DiffPreview.ForUpdate("Story", Guid.NewGuid(), "{}", "{}", changedFields);

        // Act
        var count = diff.GetChangedFieldCount();

        // Assert
        Assert.Equal(2, count);
    }

    [Fact]
    public void Operation_LowercaseInput_NormalizedToUppercase()
    {
        // Arrange & Act
        var diff = new DiffPreview(
            operation: "create",
            entityType: "Epic",
            entityId: null,
            entityKey: null,
            beforeData: null,
            afterData: "{}"
        );

        // Assert
        Assert.Equal("CREATE", diff.Operation);
    }
}
