using e_suite.API.Common.models;
using e_suite.Database.Audit;
using e_suite.Database.Core.Extensions;
using e_suite.Database.Core.Tables.CustomFields;
using eSuite.Core.Miscellaneous;
using GlossariesManager.UnitTests.Helpers;
using Moq;
using NUnit.Framework;

namespace GlossariesManager.UnitTests;

[TestFixture]
public class AddGlossaryEntryUnitTests : GlossariesManagerTestBase
{
    [SetUp]
    public override async Task Setup()
    {
        await base.Setup();
    }

    [Test]
    public Task AddGlossary_WhenAddingNewItem_AddsItemCorrectly()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            UserId = 666
        };

        var glossaryItem = new NewGlossaryItem
        {
            Name = "test root item",
        };

        //Act
        GlossariesManager.AddGlossaryItem(auditUserDetails, glossaryItem, default);

        //Assert
        Assert.Multiple(() =>
        {
            Assert.That(GlossariesManagerRepository.Glossaries.Count, Is.EqualTo(1));
            Assert.That(GlossariesManagerRepository.Glossaries[0].Guid, Is.Not.Null);
        });

        return Task.CompletedTask;
    }

    [Test]
    public Task AddGlossary_WhenParentIsNull_AddsNewRootItem()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            UserId = 666
        };

        var glossaryItem = new NewGlossaryItem
        {
            Name = "test root item",
            Parent = null!
        };

        //Act
        GlossariesManager.AddGlossaryItem(auditUserDetails, glossaryItem, default);

        //Assert
        Assert.Multiple(() =>
        {
            Assert.That(GlossariesManagerRepository.Glossaries.Count, Is.EqualTo(1));
            Assert.That(GlossariesManagerRepository.Glossaries[0].Guid, Is.Not.Null);
        });
        return Task.CompletedTask;
    }

    [Test]
    public Task AddGlossary_WhenParentIsMissing_ThrowsExpectedException()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            UserId = 666
        };

        var glossaryItem = new NewGlossaryItem
        {
            Name = "test root item",
            Parent = new GeneralIdRef
            {
                Guid = new Guid("{E94BA53F-6802-4045-BE8F-CA1047CB34F9}")
            }
        };

        //Act & Assert
        Assert.Multiple(() =>
        {
            Assert.ThrowsAsync<NullReferenceException>(async () =>
                await GlossariesManager.AddGlossaryItem(auditUserDetails, glossaryItem, default));
            Assert.That(GlossariesManagerRepository.Glossaries.Count, Is.EqualTo(0));
        });

        return Task.CompletedTask;
    }


    [Test]
    public Task AddGlossary_WhenParentIsPresent_AddsNewItem()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            UserId = 666
        };

        var parentGlossaryItem = new NewGlossaryItem
        {
            Name = "test root item",
            Parent = null!,
            Guid = new Guid("{C2F67C75-FE0C-4388-B474-C1B22BEEF443}")
        };

        GlossariesManager.AddGlossaryItem(auditUserDetails, parentGlossaryItem, default);

        var glossaryItem = new NewGlossaryItem
        {
            Name = "Test Child Item",
            Parent = new GeneralIdRef
            {
                Guid = parentGlossaryItem.Guid.Value
            }
        };

        //Act
        GlossariesManager.AddGlossaryItem(auditUserDetails, glossaryItem, default);

        //Assert
        Assert.Multiple(() =>
        {
            Assert.That(GlossariesManagerRepository.Glossaries.Count, Is.EqualTo(2));
            Assert.That(GlossariesManagerRepository.Glossaries[1].Guid, Is.Not.Null);
        });
        return Task.CompletedTask;
    }

    [Test]
    public async Task AddGlossary_WhenItemAlreadyExists_ThrowsExpectedException()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            UserId = 666
        };

        var glossaryItem = new NewGlossaryItem
        {
            Name = "test root item",
            Guid = new Guid("{E94BA53F-6802-4045-BE8F-CA1047CB34F9}")
        };

        //Act
        await GlossariesManager.AddGlossaryItem(auditUserDetails, glossaryItem, default);

        //Assert
        Assert.ThrowsAsync<ArgumentException>(async () => await GlossariesManager.AddGlossaryItem(auditUserDetails, glossaryItem, default));
    }

    [Test]
    public async Task AddGlossary_WhenItemDefinesCustomFields_AddsNewItem()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            UserId = 666
        };
            
        var glossaryItem = new NewGlossaryItem
        {
            Name = "test root item",
            Guid = new Guid("{E94BA53F-6802-4045-BE8F-CA1047CB34F9}"),
            ChildCustomFieldDefinition =
            [
                new()
                {
                    Guid = new Guid("{AA4D6C9C-EFE5-495B-993E-97C977EDF9D7}")
                },

                new()
                {
                    Guid = new Guid("{67E85B6D-B25D-4AA7-8148-0B52BA694967}")
                }
            ]
        };

        //Act
        await GlossariesManager.AddGlossaryItem(auditUserDetails, glossaryItem, default);

        //Assert
        Assert.Multiple(() =>
        {
            Assert.That(GlossariesManagerRepository.Glossaries.Count, Is.EqualTo(1));
            Assert.That(GlossariesManagerRepository.GlossaryCustomFields.Count, Is.EqualTo(2));

            Assert.That(GlossariesManagerRepository.GlossaryCustomFields[0].CustomFieldId,
                Is.EqualTo(GlossariesManagerRepository.CustomFields[0].Id));
            Assert.That(GlossariesManagerRepository.GlossaryCustomFields[0].GlossaryId,
                Is.EqualTo(GlossariesManagerRepository.Glossaries[0].Id));

            Assert.That(GlossariesManagerRepository.GlossaryCustomFields[1].CustomFieldId,
                Is.EqualTo(GlossariesManagerRepository.CustomFields[1].Id));
            Assert.That(GlossariesManagerRepository.GlossaryCustomFields[1].GlossaryId,
                Is.EqualTo(GlossariesManagerRepository.Glossaries[0].Id));
        });
    }

    [Test]
    public void AddGlossary_WhenNoParentWithCustomFieldValues_ThrowsExpectedException()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            UserId = 666
        };

        var glossaryItem = new NewGlossaryItem
        {
            Name = "test root item",
            Guid = new Guid("{E94BA53F-6802-4045-BE8F-CA1047CB34F9}"),
            CustomFieldValues =
            [
                new()
                {
                    Id = new GeneralIdRef
                    {
                        Guid = new Guid("{ECFF9FD6-F853-45C4-8505-EC28AD7EEC17}")
                    }
                }
            ]
        };

        //Act & Assert
        Assert.ThrowsAsync<NullReferenceException>(async () => await GlossariesManager.AddGlossaryItem(auditUserDetails, glossaryItem, default));
    }

    [Test]
    public async Task AddGlossary_WhenItemHasCustomFieldValuesAndDefinitionNotFound_ThrowsExpectedException()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            UserId = 666
        };

        var rootGlossaryItem = new NewGlossaryItem
        {
            Name = "test root item",
            Guid = new Guid("{E94BA53F-6802-4045-BE8F-CA1047CB34F9}"),
            ChildCustomFieldDefinition =
            [
                new()
                {
                    Guid = new Guid("{AA4D6C9C-EFE5-495B-993E-97C977EDF9D7}")
                },

                new()
                {
                    Guid = new Guid("{67E85B6D-B25D-4AA7-8148-0B52BA694967}")
                }
            ]
        };

        await GlossariesManager.AddGlossaryItem(auditUserDetails, rootGlossaryItem, default);

        var childGlossaryItem = new NewGlossaryItem
        {
            Parent = new GeneralIdRef { Guid = rootGlossaryItem.Guid },
            Name = "Test Item 1",
            CustomFieldValues =
            [
                new()
                {
                    Id = new GeneralIdRef { Guid = new Guid("{699B9144-FBA5-406E-BF40-ED5F51D3E018}") },
                    Values =
                    [
                        new()
                        {
                            Value = "Hello World",
                            DisplayValue = "Hello World"
                        }
                    ]
                }
            ]
        };

        CustomFieldValidatorMock
            .Setup(x => x.ValidateFields(It.IsAny<AuditUserDetails>(), It.IsAny<IEnumerable<CustomFieldValues>>(),
                It.IsAny<IReadOnlyCollection<e_suite.Database.Core.Tables.CustomFields.CustomField>>(),
                It.IsAny<CancellationToken>())).Throws(new NullReferenceException());

        //Act & Assert
        Assert.ThrowsAsync<NullReferenceException>(async () => await GlossariesManager.AddGlossaryItem(auditUserDetails, childGlossaryItem, default));
    }

    [Test]
    public async Task AddGlossary_WhenItemHasCustomFieldValues_SavesValueCorrectly()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            UserId = 666
        };

        var customField = GlossariesManagerRepository.CustomFields[0];

        var rootGlossaryItem = new NewGlossaryItem
        {
            Name = "test root item",
            Guid = new Guid("{E94BA53F-6802-4045-BE8F-CA1047CB34F9}"),
            ChildCustomFieldDefinition = [customField.ToGeneralIdRef()!]
        };

        await GlossariesManager.AddGlossaryItem(auditUserDetails, rootGlossaryItem, default);
        
        var glossary = GlossariesManagerRepository.Glossaries[0];
        glossary.CustomFieldDefinitions = 
        [
            new()
            {
                Id = 1,
                Glossary = glossary,
                GlossaryId = glossary.Id,
                CustomField = customField,
                CustomFieldId = customField.Id
            }
        ];

        var customFieldValue = new CustomFieldValue
        {
            Value = "Hello World",
            DisplayValue = "Hello World"
        };

        var childGlossaryItem = new NewGlossaryItem
        {
            Parent = new GeneralIdRef { Guid = rootGlossaryItem.Guid },
            Name = "Test Item 1",
            Guid = new Guid("4c76ee61-31d4-4a1c-a6dc-c3187fe9eb79"),
            CustomFieldValues =
            [
                new()
                {
                    Id = customField.ToGeneralIdRef()!,
                    Values = [customFieldValue]
                }
            ]
        };

        var validatedFields = new List<ValidatedCustomFieldValue>
        {
            new()
            {
                CustomField = customField,
                CustomFieldId = customField.Id,
                DisplayValue = customFieldValue.DisplayValue,
                Value = customFieldValue.Value.ToString()!
            }
        };

        CustomFieldValidatorMock.Setup( x => x.ValidateFields(It.IsAny<AuditUserDetails>(),
                It.IsAny<IEnumerable<CustomFieldValues>>(),
                It.IsAny<IReadOnlyCollection<CustomField>>(),
                It.IsAny<CancellationToken>()
                )
            )
            .Returns(Task.FromResult(validatedFields.AsEnumerable()));


        //Act
        await GlossariesManager.AddGlossaryItem(auditUserDetails, childGlossaryItem, default);

        //Assert
        Assert.Multiple(() =>
        {
            Assert.That(GlossariesManagerRepository.Glossaries.Count, Is.EqualTo(2));
            Assert.That(GlossariesManagerRepository.Glossaries[0].Name, Is.EqualTo(rootGlossaryItem.Name));
            Assert.That(GlossariesManagerRepository.Glossaries[0].Guid, Is.EqualTo(rootGlossaryItem.Guid));
            Assert.That(GlossariesManagerRepository.Glossaries[1].Name, Is.EqualTo(childGlossaryItem.Name));
            Assert.That(GlossariesManagerRepository.Glossaries[1].Guid, Is.EqualTo(childGlossaryItem.Guid));

            Assert.That(GlossariesManagerRepository.GlossaryCustomFieldValues.Count, Is.EqualTo(1));
            Assert.That(GlossariesManagerRepository.GlossaryCustomFieldValues[0].DisplayValue, Is.EqualTo(customFieldValue.DisplayValue));
            Assert.That(GlossariesManagerRepository.GlossaryCustomFieldValues[0].Value, Is.EqualTo(customFieldValue.Value));
        });
    }

    [Test]
    public async Task AddGlossary_WhenInsertsValueInToNonExistentCustomField_ThrowsExpectedError()
    {
        //Arrange
        var auditUserDetails = new AuditUserDetails
        {
            UserDisplayName = "Testy McTester",
            UserId = 666
        };

        var rootGlossaryItem = new NewGlossaryItem
        {
            Name = "test root item",
            Guid = new Guid("{E94BA53F-6802-4045-BE8F-CA1047CB34F9}"),
            ChildCustomFieldDefinition =
            [
                new()
                {
                    Guid = new Guid("{67E85B6D-B25D-4AA7-8148-0B52BA694967}")
                }
            ]
        };

        await GlossariesManager.AddGlossaryItem(auditUserDetails, rootGlossaryItem, default);

        var customField = GlossariesManagerRepository.CustomFields[0];

        var glossary = GlossariesManagerRepository.Glossaries[0];
        glossary.CustomFieldDefinitions = 
        [
            new()
            {
                Id = 1,
                Glossary = glossary,
                GlossaryId = glossary.Id,
                CustomField = customField,
                CustomFieldId = customField.Id
            }
        ];

        var childGlossaryItem = new NewGlossaryItem
        {
            Parent = new GeneralIdRef { Guid = rootGlossaryItem.Guid },
            Name = "Test Item 1",
            CustomFieldValues =
            [
                new()
                {
                    Id = new GeneralIdRef { Guid = rootGlossaryItem.ChildCustomFieldDefinition[0].Guid },
                    Values =
                    [
                        new()
                        {
                            Value = "Hello World",
                            DisplayValue = "Hello World"
                        }
                    ]
                }
            ]
        };

        CustomFieldValidatorMock
            .Setup(x => x.ValidateFields(It.IsAny<AuditUserDetails>(), It.IsAny<IEnumerable<CustomFieldValues>>(),
                It.IsAny<IReadOnlyCollection<e_suite.Database.Core.Tables.CustomFields.CustomField>>(),
                It.IsAny<CancellationToken>())).Throws(new NullReferenceException());

        //Act and Assert
        Assert.ThrowsAsync<NullReferenceException>(async () =>
            await GlossariesManager.AddGlossaryItem(auditUserDetails, childGlossaryItem, default));
    }
}