using e_suite.Database.Audit.AuditEngine;
using e_suite.Database.Audit.UnitTests.Helpers;
using e_suite.Database.Audit.UnitTests.Helpers.Tables;
using NUnit.Framework;

namespace e_suite.Database.Audit.UnitTests.AuditEngineCore;

[TestFixture]
public class AddRowUnitTests : AuditEngineCoreTestBase
{
    [Test]
    public async Task AddRow_WhenTableMarkedNoAudit_DoesNotCreateAuditEntry()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserId = 1,
            UserDisplayName = "Testy McTester",
            Comment = "If this gets inserted, something went wrong"
        };

        //Act
        var generalComment = new GeneralComment
        {
            Comment = "This is a test"
        };

        testDBContext.GeneralComments.Add(generalComment);
        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        Assert.That(testDBContext.GeneralComments.Count(), Is.EqualTo(1));
        Assert.That(testDBContext.AuditDetails.Count(), Is.EqualTo(0));
        Assert.That(testDBContext.AuditEntries.Count(), Is.EqualTo(0));
        Assert.That(testDBContext.AuditDrillHierarchies.Count(), Is.EqualTo(0));
    }

    [TestCase("")]
    [TestCase("Test Comment")]
    public async Task AddRow_WhenTableNotMarkedNoAudit_CreatesAuditEntry(string auditComment)
    {
        //Arrange
        TestNow = new DateTimeOffset(2022, 06, 15, 18, 50, 52, 32, TimeSpan.Zero);

        var auditUserDetails = new AuditUserDetails
        {
            UserId = 1,
            UserDisplayName = "Testy McTester",
            Comment = auditComment
        };

        var recordedComment = new RecordedComment
        {
            Id = 42,
            Comment = "This is a test"
        };

        //Act
        testDBContext.RecordedComments.Add(recordedComment);
        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        Assert.That(testDBContext.RecordedComments.Count(), Is.EqualTo(1));
        
        Assert.That(testDBContext.AuditDetails.Count(), Is.EqualTo(1));

        var auditDetail = testDBContext.AuditDetails.First();
        Assert.That(auditDetail.UserDisplayName, Is.EqualTo(auditUserDetails.UserDisplayName));
        Assert.That(auditDetail.UserId, Is.EqualTo(auditUserDetails.UserId));
        Assert.That(auditDetail.Comment, Is.EqualTo(auditUserDetails.Comment));
        Assert.That(auditDetail.DateTime, Is.EqualTo(TestNow));
        Assert.That(auditDetail.Id, Is.EqualTo(1));
        Assert.That(auditDetail.Type, Is.EqualTo(AuditType.Create.ToString()));
        Assert.That(auditDetail.Fields, Is.EqualTo( $"{{\"{nameof(recordedComment.Id)}\":{{\"NewValue\":{recordedComment.Id}}},\"{nameof(recordedComment.Comment)}\":{{\"NewValue\":\"{recordedComment.Comment}\"}}}}" ));

        //Ensure that the AuditEntry is saved properly.
        Assert.That(testDBContext.AuditEntries.Count(), Is.EqualTo(1));
        var auditEntry = testDBContext.AuditEntries.First();
        Assert.That(auditEntry.Id, Is.EqualTo(1));
        Assert.That(auditEntry.EntityName, Is.EqualTo(typeof(RecordedComment).FullName));
        Assert.That(auditEntry.DisplayName, Is.EqualTo(string.Empty));
        Assert.That(auditEntry.AuditLogId, Is.EqualTo(auditDetail.Id));
        Assert.That(auditEntry.PrimaryKey, Is.EqualTo("{\"Id\":42}"));
        Assert.That(auditEntry.IsPrimary, Is.True);

        Assert.That(testDBContext.AuditDrillHierarchies.Count(), Is.EqualTo(0));
    }

    [Test]
    public async Task AddRow_SaveChangesNoAuditCalled_AuditingBypassedCompletely()
    {
        //Arrange
        //Act
        var recordedComment = new RecordedComment
        {
            Comment = "This is a test"
        };

        testDBContext.RecordedComments.Add(recordedComment);
        await testDBContext.NoAuditSaveChangesAsync();

        //Assert
        Assert.That(testDBContext.RecordedComments.Count(), Is.EqualTo(1));

        Assert.That(testDBContext.AuditDetails.Count(), Is.EqualTo(0));
        Assert.That(testDBContext.AuditEntries.Count(), Is.EqualTo(0));
        Assert.That(testDBContext.AuditDrillHierarchies.Count(), Is.EqualTo(0));
    }

    [TestCase("")]
    [TestCase("Test Comment")]
    public async Task AddRow_WhenFieldHasAuditName_DisplayNameFilledIn(string auditComment)
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserId = 1,
            UserDisplayName = "Testy McTester",
            Comment = auditComment
        };

        //Act
        var entryWithName = new EntryWithName
        {
            Name = "TestName"
        };

        testDBContext.EntriesWithName.Add(entryWithName);
        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        Assert.That(testDBContext.AuditEntries.Count(), Is.EqualTo(1));
        var auditEntry = testDBContext.AuditEntries.First();
        Assert.That(auditEntry.DisplayName, Is.EqualTo(entryWithName.Name));
    }

    [TestCase("")]
    [TestCase("Test Comment")]
    public async Task AddRow_WhenFieldHasRedactAudit_FieldValueRedacted(string auditComment)
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserId = 1,
            UserDisplayName = "Testy McTester",
            Comment = auditComment
        };

        //Act
        var secretValue = new SecretValue
        {
            Id = 10, 
            Name = "TestName",
            Secret = "I'm not telling you"
        };

        testDBContext.SecretValues.Add(secretValue);
        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        var auditDetail = testDBContext.AuditDetails.First();
        Assert.That(auditDetail.UserDisplayName, Is.EqualTo(auditUserDetails.UserDisplayName));
        Assert.That(auditDetail.UserId, Is.EqualTo(auditUserDetails.UserId));
        Assert.That(auditDetail.Comment, Is.EqualTo(auditUserDetails.Comment));

        Assert.That(auditDetail.Id, Is.EqualTo(1));
        Assert.That(auditDetail.Type, Is.EqualTo(AuditType.Create.ToString()));
        Assert.That(auditDetail.Fields, Is.EqualTo($"{{\"{nameof(SecretValue.Id)}\":{{\"NewValue\":{secretValue.Id}}},\"{nameof(secretValue.Name)}\":{{\"NewValue\":\"{secretValue.Name}\"}},\"{nameof(secretValue.Secret)}\":{{\"NewValue\":\"<Redacted>\"}}}}"));
    }

    [TestCase("")]
    [TestCase("Test Comment")]
    public async Task AddRow_WhenKeyFieldNotAssigned_FieldNotIncludedInAuditRecord(string auditComment)
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserId = 1,
            UserDisplayName = "Testy McTester",
            Comment = auditComment
        };

        //Act
        var secretValue = new SecretValue
        {
            Name = "TestName",
            Secret = "I'm not telling you"
        };

        testDBContext.SecretValues.Add(secretValue);
        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        var auditDetail = testDBContext.AuditDetails.First();
        Assert.That(auditDetail.UserDisplayName, Is.EqualTo(auditUserDetails.UserDisplayName));
        Assert.That(auditDetail.UserId, Is.EqualTo(auditUserDetails.UserId));
        Assert.That(auditDetail.Comment, Is.EqualTo(auditUserDetails.Comment));

        Assert.That(auditDetail.Id, Is.EqualTo(1));
        Assert.That(auditDetail.Type, Is.EqualTo(AuditType.Create.ToString()));
        Assert.That(auditDetail.Fields, Is.EqualTo($"{{\"{nameof(secretValue.Name)}\":{{\"NewValue\":\"{secretValue.Name}\"}},\"{nameof(secretValue.Secret)}\":{{\"NewValue\":\"<Redacted>\"}}}}"));
    }

    [TestCase("")]
    [TestCase("Test Comment")]
    public async Task AddRow_WhenFieldHasAuditParentWithNullId_OnlyPrimaryAuditEntrySaved(string auditComment)
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserId = 1,
            UserDisplayName = "Testy McTester",
            Comment = auditComment
        };

        //Act
        var childValue = new ChildValue
        {
            Name = "TestName",
            ParentId = null
        };

        testDBContext.ChildValues.Add(childValue);
        
        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        var auditDetail = testDBContext.AuditDetails.First();
        Assert.That(auditDetail.UserDisplayName, Is.EqualTo(auditUserDetails.UserDisplayName));
        Assert.That(auditDetail.UserId, Is.EqualTo(auditUserDetails.UserId));
        Assert.That(auditDetail.Comment, Is.EqualTo(auditUserDetails.Comment));

        Assert.That(auditDetail.Id, Is.EqualTo(1));
        Assert.That(auditDetail.Type, Is.EqualTo(AuditType.Create.ToString()));

        Assert.That(testDBContext.AuditEntries.Count(), Is.EqualTo(1));

        var auditEntries = testDBContext.AuditEntries.ToList();
        var childAuditEntry = auditEntries[0];
        Assert.That(childAuditEntry.Id, Is.EqualTo(1));
        Assert.That(childAuditEntry.EntityName, Is.EqualTo(typeof(ChildValue).FullName));
        Assert.That(childAuditEntry.DisplayName, Is.EqualTo(childValue.Name));
        Assert.That(childAuditEntry.AuditLogId, Is.EqualTo(auditDetail.Id));
        Assert.That(childAuditEntry.PrimaryKey, Is.EqualTo($"{{\"Id\":{childValue.Id}}}"));
        Assert.That(childAuditEntry.IsPrimary, Is.True);
        
        Assert.That(testDBContext.AuditDrillHierarchies.Count(), Is.EqualTo(0));
    }

    [TestCase("")]
    [TestCase("Test Comment")]
    public async Task AddRow_ChildValueHasParent_BothParentAndChildKeysAreRecorded(string auditComment)
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserId = 1,
            UserDisplayName = "Testy McTester",
            Comment = auditComment
        };

        var parentValue = new ParentValue
        {
            Id = 100,
            Name = "Parent Entry"
        };

        testDBContext.ParentValues.Add(parentValue);
        await testDBContext.NoAuditSaveChangesAsync();

        //Act
        var childValue = new ChildValue
        {
            Id = 1000,
            Name = "TestName",
            ParentId = parentValue.Id
        };

        testDBContext.ChildValues.Add(childValue);
        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        Assert.That(testDBContext.AuditDetails.Count(), Is.EqualTo(1));
        var auditDetail = testDBContext.AuditDetails.First();
        Assert.That(auditDetail.UserDisplayName, Is.EqualTo(auditUserDetails.UserDisplayName));
        Assert.That(auditDetail.UserId, Is.EqualTo(auditUserDetails.UserId));
        Assert.That(auditDetail.Comment, Is.EqualTo(auditUserDetails.Comment));

        Assert.That(auditDetail.Id, Is.EqualTo(1));
        Assert.That(auditDetail.Type, Is.EqualTo(AuditType.Create.ToString()));

        Assert.That(testDBContext.AuditEntries.Count(), Is.EqualTo(2));

        var auditEntries = testDBContext.AuditEntries.ToList();
        var childAuditEntry = auditEntries[0];
        Assert.That(childAuditEntry.Id, Is.EqualTo(1));
        Assert.That(childAuditEntry.EntityName, Is.EqualTo(typeof(ChildValue).FullName));
        Assert.That(childAuditEntry.DisplayName, Is.EqualTo(childValue.Name));
        Assert.That(childAuditEntry.AuditLogId, Is.EqualTo(auditDetail.Id));
        Assert.That(childAuditEntry.PrimaryKey, Is.EqualTo($"{{\"Id\":{childValue.Id}}}"));
        Assert.That(childAuditEntry.IsPrimary, Is.True);

        var parentAuditEntry = auditEntries[1];
        Assert.That(parentAuditEntry.Id, Is.EqualTo(2));
        Assert.That(parentAuditEntry.EntityName, Is.EqualTo(typeof(ParentValue).FullName));
        Assert.That(parentAuditEntry.DisplayName, Is.EqualTo(parentValue.Name));
        Assert.That(parentAuditEntry.AuditLogId, Is.EqualTo(auditDetail.Id));
        Assert.That(parentAuditEntry.PrimaryKey, Is.EqualTo($"{{\"Id\":{parentValue.Id}}}"));
        Assert.That(parentAuditEntry.IsPrimary, Is.False);

        Assert.That(testDBContext.AuditDrillHierarchies.Count(), Is.EqualTo(1));
        var auditDrillHierarchies = testDBContext.AuditDrillHierarchies.ToList();
        Assert.That(auditDrillHierarchies[0].ChildAuditDrillDownEntityId, Is.EqualTo(1));
        Assert.That(auditDrillHierarchies[0].ParentAuditDrillDownEntityId, Is.EqualTo(2));
    }

    [TestCase("")]
    [TestCase("Test Comment")]
    public async Task AddRow_WhenFieldHasAuditParentWithId_OnlyPrimaryAndParentIdsEntered(string auditComment)
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserId = 1,
            UserDisplayName = "Testy McTester",
            Comment = auditComment
        };

        var grandParentValue = new GrandParentValue
        {
            Id = 10000,
            Name = "GrandParent Entry"
        };

        var parentValue = new ParentValue
        {
            Id = 100,
            Name = "Parent Entry",
            GrandParentId = grandParentValue.Id
        };

        testDBContext.GrandParentValues.Add(grandParentValue);
        testDBContext.ParentValues.Add(parentValue);
        await testDBContext.NoAuditSaveChangesAsync();

        //Act
        var childValue = new ChildValue
        {
            Id = 1000,
            Name = "TestName",
            ParentId = parentValue.Id
        };

        testDBContext.ChildValues.Add(childValue);
        await testDBContext.SaveChangesAsync(auditUserDetails);
        
        //Assert
        Assert.That(testDBContext.AuditDetails.Count(), Is.EqualTo(1));
        var auditDetail = testDBContext.AuditDetails.First();
        Assert.That(auditDetail.UserDisplayName, Is.EqualTo(auditUserDetails.UserDisplayName));
        Assert.That(auditDetail.UserId, Is.EqualTo(auditUserDetails.UserId));
        Assert.That(auditDetail.Comment, Is.EqualTo(auditUserDetails.Comment));

        Assert.That(auditDetail.Id, Is.EqualTo(1));
        Assert.That(auditDetail.Type, Is.EqualTo(AuditType.Create.ToString()));

        Assert.That(testDBContext.AuditEntries.Count(), Is.EqualTo(3));

        var auditEntries = testDBContext.AuditEntries.ToList();
        var childAuditEntry = auditEntries[0];
        Assert.That(childAuditEntry.Id, Is.EqualTo(1));
        Assert.That(childAuditEntry.EntityName, Is.EqualTo(typeof(ChildValue).FullName));
        Assert.That(childAuditEntry.DisplayName, Is.EqualTo(childValue.Name));
        Assert.That(childAuditEntry.AuditLogId, Is.EqualTo(auditDetail.Id));
        Assert.That(childAuditEntry.PrimaryKey, Is.EqualTo($"{{\"Id\":{childValue.Id}}}"));
        Assert.That(childAuditEntry.IsPrimary, Is.True);

        var parentAuditEntry = auditEntries[1];
        Assert.That(parentAuditEntry.Id, Is.EqualTo(2));
        Assert.That(parentAuditEntry.EntityName, Is.EqualTo(typeof(ParentValue).FullName));
        Assert.That(parentAuditEntry.DisplayName, Is.EqualTo(parentValue.Name));
        Assert.That(parentAuditEntry.AuditLogId, Is.EqualTo(auditDetail.Id));
        Assert.That(parentAuditEntry.PrimaryKey, Is.EqualTo($"{{\"Id\":{parentValue.Id}}}"));
        Assert.That(parentAuditEntry.IsPrimary, Is.False);

        var grandParentAuditEntry = auditEntries[2];
        Assert.That(grandParentAuditEntry.Id, Is.EqualTo(3));
        Assert.That(grandParentAuditEntry.EntityName, Is.EqualTo(typeof(GrandParentValue).FullName));
        Assert.That(grandParentAuditEntry.DisplayName, Is.EqualTo(grandParentValue.Name));
        Assert.That(grandParentAuditEntry.AuditLogId, Is.EqualTo(auditDetail.Id));
        Assert.That(grandParentAuditEntry.PrimaryKey, Is.EqualTo($"{{\"Id\":{grandParentValue.Id}}}"));
        Assert.That(grandParentAuditEntry.IsPrimary, Is.False);

        Assert.That(testDBContext.AuditDrillHierarchies.Count(), Is.EqualTo(2));
        var auditDrillHierarchies = testDBContext.AuditDrillHierarchies.ToList();
        Assert.That(auditDrillHierarchies[0].ChildAuditDrillDownEntityId, Is.EqualTo(1));
        Assert.That(auditDrillHierarchies[0].ParentAuditDrillDownEntityId, Is.EqualTo(2));

        Assert.That(auditDrillHierarchies[1].ChildAuditDrillDownEntityId, Is.EqualTo(2));
        Assert.That(auditDrillHierarchies[1].ParentAuditDrillDownEntityId, Is.EqualTo(3));
    }


    [TestCase("")]
    [TestCase("Test Comment")]
    public async Task AddRow_TableHasSelfReferenceAndReferenceNull_OnlyPrimaryAndParentIdsEntered(string auditComment)
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserId = 1,
            UserDisplayName = "Testy McTester",
            Comment = auditComment
        };

        //Act
        var leafSimpleTreeValue = new SimpleTreeValue
        { 
            Id = 30000,
            Name = "Leaf Level item",
            ParentId = null
        };

        testDBContext.SimpleTreeValues.Add(leafSimpleTreeValue);
        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        Assert.That(testDBContext.SimpleTreeValues.Count(), Is.EqualTo(1));

        Assert.That(testDBContext.AuditDetails.Count(), Is.EqualTo(1));
        var auditDetail = testDBContext.AuditDetails.First();
        Assert.That(auditDetail.UserDisplayName, Is.EqualTo(auditUserDetails.UserDisplayName));
        Assert.That(auditDetail.UserId, Is.EqualTo(auditUserDetails.UserId));
        Assert.That(auditDetail.Comment, Is.EqualTo(auditUserDetails.Comment));

        Assert.That(auditDetail.Id, Is.EqualTo(1));
        Assert.That(auditDetail.Type, Is.EqualTo(AuditType.Create.ToString()));

        Assert.That(testDBContext.AuditEntries.Count(), Is.EqualTo(1));

        var auditEntries = testDBContext.AuditEntries.ToList();
        var childAuditEntry = auditEntries[0];
        Assert.That(childAuditEntry.Id, Is.EqualTo(1));
        Assert.That(childAuditEntry.EntityName, Is.EqualTo(typeof(SimpleTreeValue).FullName));
        Assert.That(childAuditEntry.DisplayName, Is.EqualTo(leafSimpleTreeValue.Name));
        Assert.That(childAuditEntry.AuditLogId, Is.EqualTo(auditDetail.Id));
        Assert.That(childAuditEntry.PrimaryKey, Is.EqualTo($"{{\"Id\":{leafSimpleTreeValue.Id}}}"));
        Assert.That(childAuditEntry.IsPrimary, Is.True);
        
        Assert.That(testDBContext.AuditDrillHierarchies.Count(), Is.EqualTo(0));
    }

    [TestCase("")]
    [TestCase("Test Comment")]
    public async Task AddRow_TableHasSelfReferenceWithSingleAncestor_BothValuesAreRecorded(string auditComment)
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserId = 1,
            UserDisplayName = "Testy McTester",
            Comment = auditComment
        };

        var rootSimpleTreeValue = new SimpleTreeValue
        {
            Id = 3000,
            Name = "Leaf Level item",
            ParentId = null
        };

        testDBContext.SimpleTreeValues.Add(rootSimpleTreeValue);
        await testDBContext.NoAuditSaveChangesAsync();

        //Act
        var leafSimpleTreeValue = new SimpleTreeValue
        {
            Id = 30000,
            Name = "Leaf Level item",
            ParentId = rootSimpleTreeValue.Id
        };

        testDBContext.SimpleTreeValues.Add(leafSimpleTreeValue);
        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        Assert.That(testDBContext.SimpleTreeValues.Count(), Is.EqualTo(2));

        Assert.That(testDBContext.AuditDetails.Count(), Is.EqualTo(1));
        var auditDetail = testDBContext.AuditDetails.First();
        Assert.That(auditDetail.UserDisplayName, Is.EqualTo(auditUserDetails.UserDisplayName));
        Assert.That(auditDetail.UserId, Is.EqualTo(auditUserDetails.UserId));
        Assert.That(auditDetail.Comment, Is.EqualTo(auditUserDetails.Comment));

        Assert.That(auditDetail.Id, Is.EqualTo(1));
        Assert.That(auditDetail.Type, Is.EqualTo(AuditType.Create.ToString()));

        Assert.That(testDBContext.AuditEntries.Count(), Is.EqualTo(2));

        var auditEntries = testDBContext.AuditEntries.ToList();
        var childAuditEntry = auditEntries[0];
        Assert.That(childAuditEntry.Id, Is.EqualTo(1));
        Assert.That(childAuditEntry.EntityName, Is.EqualTo(typeof(SimpleTreeValue).FullName));
        Assert.That(childAuditEntry.DisplayName, Is.EqualTo(leafSimpleTreeValue.Name));
        Assert.That(childAuditEntry.AuditLogId, Is.EqualTo(auditDetail.Id));
        Assert.That(childAuditEntry.PrimaryKey, Is.EqualTo($"{{\"Id\":{leafSimpleTreeValue.Id}}}"));
        Assert.That(childAuditEntry.IsPrimary, Is.True);

        var parentAuditEntry = auditEntries[1];
        Assert.That(parentAuditEntry.Id, Is.EqualTo(2));
        Assert.That(parentAuditEntry.EntityName, Is.EqualTo(typeof(SimpleTreeValue).FullName));
        Assert.That(parentAuditEntry.DisplayName, Is.EqualTo(rootSimpleTreeValue.Name));
        Assert.That(parentAuditEntry.AuditLogId, Is.EqualTo(auditDetail.Id));
        Assert.That(parentAuditEntry.PrimaryKey, Is.EqualTo($"{{\"Id\":{rootSimpleTreeValue.Id}}}"));
        Assert.That(parentAuditEntry.IsPrimary, Is.False);

        Assert.That(testDBContext.AuditDrillHierarchies.Count(), Is.EqualTo(1));
        var auditDrillHierarchies = testDBContext.AuditDrillHierarchies.ToList();
        Assert.That(auditDrillHierarchies[0].ChildAuditDrillDownEntityId, Is.EqualTo(1));
        Assert.That(auditDrillHierarchies[0].ParentAuditDrillDownEntityId, Is.EqualTo(2));
    }

    [TestCase("")]
    [TestCase("Test Comment")]
    public async Task AddRow_TableHasSelfReferenceWithTwoAncestors_BothValuesAreRecorded(string auditComment)
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserId = 1,
            UserDisplayName = "Testy McTester",
            Comment = auditComment
        };

        var rootSimpleTreeValue = new SimpleTreeValue
        {
            Id = 300,
            Name = "Leaf Level item",
            ParentId = null
        };


        var level1SimpleTreeValue = new SimpleTreeValue
        {
            Id = 3000,
            Name = "Level 1 item",
            ParentId = rootSimpleTreeValue.Id
        };

        testDBContext.SimpleTreeValues.Add(rootSimpleTreeValue);
        testDBContext.SimpleTreeValues.Add(level1SimpleTreeValue);
        await testDBContext.NoAuditSaveChangesAsync();

        //Act
        var leafSimpleTreeValue = new SimpleTreeValue
        {
            Id = 30000,
            Name = "Leaf Level item",
            ParentId = level1SimpleTreeValue.Id
        };

        testDBContext.SimpleTreeValues.Add(leafSimpleTreeValue);
        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        Assert.That(testDBContext.SimpleTreeValues.Count(), Is.EqualTo(3));

        Assert.That(testDBContext.AuditDetails.Count(), Is.EqualTo(1));
        var auditDetail = testDBContext.AuditDetails.First();
        Assert.That(auditDetail.UserDisplayName, Is.EqualTo(auditUserDetails.UserDisplayName));
        Assert.That(auditDetail.UserId, Is.EqualTo(auditUserDetails.UserId));
        Assert.That(auditDetail.Comment, Is.EqualTo(auditUserDetails.Comment));

        Assert.That(auditDetail.Id, Is.EqualTo(1));
        Assert.That(auditDetail.Type, Is.EqualTo(AuditType.Create.ToString()));

        Assert.That(testDBContext.AuditEntries.Count(), Is.EqualTo(3));

        var auditEntries = testDBContext.AuditEntries.ToList();
        var childAuditEntry = auditEntries[0];
        Assert.That(childAuditEntry.Id, Is.EqualTo(1));
        Assert.That(childAuditEntry.EntityName, Is.EqualTo(typeof(SimpleTreeValue).FullName));
        Assert.That(childAuditEntry.DisplayName, Is.EqualTo(leafSimpleTreeValue.Name));
        Assert.That(childAuditEntry.AuditLogId, Is.EqualTo(auditDetail.Id));
        Assert.That(childAuditEntry.PrimaryKey, Is.EqualTo($"{{\"Id\":{leafSimpleTreeValue.Id}}}"));
        Assert.That(childAuditEntry.IsPrimary, Is.True);

        var parentAuditEntry = auditEntries[1];
        Assert.That(parentAuditEntry.Id, Is.EqualTo(2));
        Assert.That(parentAuditEntry.EntityName, Is.EqualTo(typeof(SimpleTreeValue).FullName));
        Assert.That(parentAuditEntry.DisplayName, Is.EqualTo(level1SimpleTreeValue.Name));
        Assert.That(parentAuditEntry.AuditLogId, Is.EqualTo(auditDetail.Id));
        Assert.That(parentAuditEntry.PrimaryKey, Is.EqualTo($"{{\"Id\":{level1SimpleTreeValue.Id}}}"));
        Assert.That(parentAuditEntry.IsPrimary, Is.False);

        var grandParentAuditEntry = auditEntries[2];
        Assert.That(grandParentAuditEntry.Id, Is.EqualTo(3));
        Assert.That(grandParentAuditEntry.EntityName, Is.EqualTo(typeof(SimpleTreeValue).FullName));
        Assert.That(grandParentAuditEntry.DisplayName, Is.EqualTo(rootSimpleTreeValue.Name));
        Assert.That(grandParentAuditEntry.AuditLogId, Is.EqualTo(auditDetail.Id));
        Assert.That(grandParentAuditEntry.PrimaryKey, Is.EqualTo($"{{\"Id\":{rootSimpleTreeValue.Id}}}"));
        Assert.That(grandParentAuditEntry.IsPrimary, Is.False);

        Assert.That(testDBContext.AuditDrillHierarchies.Count(), Is.EqualTo(2));
        var auditDrillHierarchies = testDBContext.AuditDrillHierarchies.ToList();
        Assert.That(auditDrillHierarchies[0].ChildAuditDrillDownEntityId, Is.EqualTo(1));
        Assert.That(auditDrillHierarchies[0].ParentAuditDrillDownEntityId, Is.EqualTo(2));

        Assert.That(auditDrillHierarchies[1].ChildAuditDrillDownEntityId, Is.EqualTo(2));
        Assert.That(auditDrillHierarchies[1].ParentAuditDrillDownEntityId, Is.EqualTo(3));
    }

    [TestCase("")]
    public async Task AddRow_RowContainsEnum_EnumValueAuditedProperly(string auditComment)
    {
        //Arrange
        TestNow = new DateTimeOffset(2022, 06, 15, 18, 50, 52, 32, TimeSpan.Zero);

        var auditUserDetails = new AuditUserDetails
        {
            UserId = 1,
            UserDisplayName = "Testy McTester",
            Comment = auditComment
        };

        var recordedComment = new TableWithEnums
        {
            Id = 1,
            RawEnum = TestEnum.Item1
        };

        //Act
        testDBContext.TableWithEnums.Add(recordedComment);
        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        Assert.That(testDBContext.TableWithEnums.Count(), Is.EqualTo(1));

        Assert.That(testDBContext.AuditDetails.Count(), Is.EqualTo(1));

        var auditDetail = testDBContext.AuditDetails.First();
        Assert.That(auditDetail.Id, Is.EqualTo(1));
        Assert.That(auditDetail.Fields, Is.EqualTo($"{{\"{nameof(recordedComment.Id)}\":{{\"NewValue\":{recordedComment.Id}}},\"{nameof(recordedComment.RawEnum)}\":{{\"NewValue\":0,\"NewDisplayName\":\"Item1\"}}}}"));
    }

    [TestCase("")]
    public async Task AddRow_RowContainsEnumWithDisplayValue_EnumValueAuditedProperly(string auditComment)
    {
        //Arrange
        TestNow = new DateTimeOffset(2022, 06, 15, 18, 50, 52, 32, TimeSpan.Zero);

        var auditUserDetails = new AuditUserDetails
        {
            UserId = 1,
            UserDisplayName = "Testy McTester",
            Comment = auditComment
        };

        var recordedComment = new TableWithDisplayEnums
        {
            Id = 1
        };

        //Act
        testDBContext.TableWithDisplayEnums.Add(recordedComment);
        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        Assert.That(testDBContext.TableWithDisplayEnums.Count(), Is.EqualTo(1));

        Assert.That(testDBContext.AuditDetails.Count(), Is.EqualTo(1));

        var auditDetail = testDBContext.AuditDetails.First();
        Assert.That(auditDetail.Id, Is.EqualTo(1));

        Assert.That(auditDetail.Fields, Is.EqualTo($"{{\"{nameof(recordedComment.Id)}\":{{\"NewValue\":{recordedComment.Id}}},\"{nameof(recordedComment.DisplayEnum)}\":{{\"NewValue\":0,\"NewDisplayName\":\"Item 1\"}}}}"));
    }
}