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 LastUpdatedUnitTests : AuditEngineCoreTestBase
{
    [Test]
    public async Task NewRow_WhenAdded_SetsLastUpdatedToNow()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            Comment = string.Empty
        };

        var entry = new TableWithLastUpdated
        {
            Name = "TestName"
        };

        //Act
        testDBContext.TableWithLastUpdateds.Add(entry);
        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        var editedItem = testDBContext.TableWithLastUpdateds.Single(x => x.Name == entry.Name);

        Assert.That(editedItem.LastUpdated, Is.EqualTo(TestNow));

        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("{\"Deleted\":{\"NewValue\":false},\"Name\":{\"NewValue\":\"TestName\"}}"));

    }

    [Test]
    public async Task ExistingRow_WhenUpdated_SetsLastUpdatedToNow()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            Comment = string.Empty
        };

        var entry = new TableWithLastUpdated
        {
            Name = "TestName"
        };

        testDBContext.TableWithLastUpdateds.Add(entry);
        await testDBContext.NoAuditSaveChangesAsync();

        //Act
        var itemToEdit = testDBContext.TableWithLastUpdateds.Single( x => x.Name == entry.Name);
        itemToEdit.Name = "Updated";

        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        var editedItem = testDBContext.TableWithLastUpdateds.Single(x => x.Name == itemToEdit.Name);

        Assert.That(editedItem.LastUpdated, Is.EqualTo(TestNow));

        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.Update.ToString()));
        Assert.That(auditDetail.Fields, Is.EqualTo("{\"Name\":{\"OldValue\":\"TestName\",\"NewValue\":\"Updated\"}}"));
    }

    [Test]
    public async Task ExistingRow_WhenSoftDeleted_SetsLastUpdatedToNow()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            Comment = string.Empty
        };

        var entry = new TableWithLastUpdated
        {
            Name = "TestName"
        };

        testDBContext.TableWithLastUpdateds.Add(entry);
        await testDBContext.NoAuditSaveChangesAsync();

        //Act
        var itemToEdit = testDBContext.TableWithLastUpdateds.Single(x => x.Name == entry.Name);
        itemToEdit.Deleted = true;

        await testDBContext.SaveChangesAsync(auditUserDetails);

        //Assert
        var editedItem = testDBContext.TableWithLastUpdateds.Single(x => x.Name == itemToEdit.Name);

        Assert.That(editedItem.LastUpdated, Is.EqualTo(TestNow));

        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.Delete.ToString()));
        Assert.That(auditDetail.Fields, Is.EqualTo(string.Empty));
    }

    [Test]
    public async Task ExistingChildRow_WhenSoftDeleted_SetsParentLastUpdatedToNow()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            Comment = string.Empty
        };

        var entry = new TableWithLastUpdated
        {
            Name = "TestName"
        };

        testDBContext.TableWithLastUpdateds.Add(entry);

        var child = new ChildTableWithLastUpdated
        {
            ParentId = entry.Id,
            Parent = entry,
            Name = "Child row",
        };

        testDBContext.ChildTableWithLastUpdateds.Add(child);

        await testDBContext.NoAuditSaveChangesAsync();

        //Act
        var itemToEdit = testDBContext.ChildTableWithLastUpdateds.Single(x => x.Name == child.Name);
        itemToEdit.Deleted = true;

        await testDBContext.SaveChangesAsync(auditUserDetails);

        var editedItem = testDBContext.TableWithLastUpdateds.Single(x => x.Name == entry.Name);

        Assert.That(editedItem.LastUpdated, Is.EqualTo(TestNow));
    }

    [Test]
    public async Task ExistingChildRow_WhenPurged_SetsParentLastUpdatedToNow()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            Comment = string.Empty
        };

        var entry = new TableWithLastUpdated
        {
            Name = "TestName"
        };

        testDBContext.TableWithLastUpdateds.Add(entry);

        var child = new ChildTableWithLastUpdated
        {
            ParentId = entry.Id,
            Parent = entry,
            Name = "Child row",
        };

        testDBContext.ChildTableWithLastUpdateds.Add(child);

        await testDBContext.NoAuditSaveChangesAsync();

        //Act
        var itemToEdit = testDBContext.ChildTableWithLastUpdateds.Single(x => x.Name == child.Name);
        testDBContext.ChildTableWithLastUpdateds.Remove(itemToEdit);

        await testDBContext.SaveChangesAsync(auditUserDetails);

        var editedItem = testDBContext.TableWithLastUpdateds.Single(x => x.Name == entry.Name);

        Assert.That(editedItem.LastUpdated, Is.EqualTo(TestNow));
    }
}