using e_suite.API.Common.exceptions;
using e_suite.API.Common.models;
using e_suite.Database.Audit;
using e_suite.Database.Core.Tables.Glossaries;
using eSuite.Core.CustomFields;
using Sequence = e_suite.Database.Core.Tables.Sequences.Sequence;

namespace e_suite.Modules.CustomFieldManagerUnitTests.CustomFieldManagerUnitTests;

public class CreateCustomFieldUnitTest : CustomFieldsTestBase<object>
{
    [SetUp]
    public override async Task Setup() => await base.Setup();

    [Test]
    public async Task CreateCustomField_AutoGenId_SaveChangesId()
    {

        //Arrange
        var customFieldDto = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Text,
            Name = "Test",
            Guid = null,
            MinEntries = 5,
            MaxEntries = 5,
            Parameters = "{\"multiLine\":false}"
        };
        _customFieldManager = new CustomFieldManager(_fakeCustomFieldRepository!, _fakeCustomFieldReferenceRepository, _customFieldHelper, PatchFactoryMock.Object);
        _customFieldRepository.Setup(x => x.CreateAsync(It.IsAny<AuditUserDetails>(), It.Is<CustomField>(x => x.Name == "Test"), It.IsAny<CancellationToken>()));

        //Act
        await _customFieldManager.CreateFieldAsync(auditResult, customFieldDto, default);

        //Assert;
        Assert.Multiple(() =>
        {
            Assert.That(_fakeCustomFieldRepository!.CustomFields, Has.Count.EqualTo(1));
            Assert.That(_fakeCustomFieldRepository!.CustomFields[0].MaxEntries, Is.EqualTo(5));
            Assert.That(_fakeCustomFieldRepository!.CustomFields[0].MinEntries, Is.EqualTo(5));
            Assert.That(_fakeCustomFieldRepository!.CustomFields[0].Name, Is.EqualTo("Test"));
            Assert.That(_fakeCustomFieldRepository!.CustomFields[0].DefaultValue, Is.EqualTo("1"));
        });
    }

    [Test]
    public async Task CreateCustomField_WithSameName_ThrowsException()
    {
        //Arrange
        var guid = new Guid("4aef2005-f7c2-4b9b-bf0c-cd04eab6564c");
        var guidSecond = new Guid("6a94e1b7-5cf7-4441-a2a0-707727a14048");
        _fakeCustomFieldRepository = new FakeCustomFieldRepository();
        var customFieldDto = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Text,
            Name = "Test",
            Guid = guid,
            Parameters = "{\"multiLine\":false}"
        };
        var customFieldDtoSecond = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Text,
            Name = "Test",
            Guid = guidSecond,
            Parameters = "{\"multiLine\":false}"
        };
        
        await _customFieldManager.CreateFieldAsync(auditResult, customFieldDto, default);
        
        //Assert
        Assert.ThrowsAsync<ExistsException>(async () =>
        {
            //Act
            await _customFieldManager.CreateFieldAsync(auditResult, customFieldDtoSecond, default);
        });
    }

    [Test]
    public async Task CreateCustomField_WithPredefinedGuidId_SaveChanges()
    {
        //Arrange
        var guid = new Guid("85ccdb80-912a-4de1-8961-746990e1031b");
        var guidSecond = new Guid("4af24c5c-5bc0-4fa9-aea9-ab5c73e52364");

        var customFieldDto = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Text,
            Name = "Test",
            Guid = guid,
            MinEntries = 10,
            MaxEntries = 10,
            Parameters = "{\"multiLine\":false}"
        };
        var customFieldDtoSecond = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Text,
            Name = "Test2",
            Guid = guidSecond,
            MinEntries = 11,
            MaxEntries = 11,
            Parameters = "{\"multiLine\":false}"
        };

        await _customFieldManager.CreateFieldAsync(auditResult, customFieldDto, default);

        //Act
        await _customFieldManager.CreateFieldAsync(auditResult, customFieldDtoSecond, default);

        //Assert
        Assert.Multiple(() =>
        {
            Assert.That(_fakeCustomFieldRepository.CustomFields.Any(x => x.Guid == guid));
            Assert.That(_fakeCustomFieldRepository.CustomFields.Any(x => x.Guid == guidSecond));
            Assert.That(_fakeCustomFieldRepository.CustomFields[0].MinEntries, Is.EqualTo(10));
            Assert.That(_fakeCustomFieldRepository.CustomFields[0].MaxEntries, Is.EqualTo(10));
            Assert.That(_fakeCustomFieldRepository.CustomFields[1].MaxEntries, Is.EqualTo(11));
            Assert.That(_fakeCustomFieldRepository.CustomFields[1].MaxEntries, Is.EqualTo(11));
        });
    }

    [Test]
    public async Task CreateCustomField_WithSameGuids_ThrowsException()
    {
        //Arrange
        var guid = new Guid("55b2a79b-c007-48ef-a85f-092d9f5db952");
        _fakeCustomFieldRepository = new FakeCustomFieldRepository();

        var customFieldDto = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Text,
            Name = "Test",
            Guid = guid,
            Parameters = "{\"multiLine\":false}"
        };
        var customFieldDtoSecond = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Text,
            Name = "Test2",
            Guid = guid,
            Parameters = "{\"multiLine\":false}"
        };
        
        await _customFieldManager.CreateFieldAsync(auditResult, customFieldDto, default);
        
        //Assert
        Assert.ThrowsAsync<ExistsException>(async () =>
        {
            //Act
            await _customFieldManager.CreateFieldAsync(new AuditUserDetails(), customFieldDtoSecond, default);
        });
    }

    [Test]
    public async Task CreateCustomField_WithFieldTypeGlossaryWithOnlyIdForRefObj_SaveChanges()
    {
        //Arrange
        var glossary = new Glossary
        {
            Id = 1,
            Deleted = false,
            Guid = new Guid("50551979-ceff-4a1e-897d-7abb6d786ea8"),
            Name = "Form Name"
        };
        _fakeCustomFieldReferenceRepository.Glossaries.Add(glossary);
        var customField = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Glossary,
            RefElementId = new GeneralIdRef
            {
                Guid = null,
                Id = 1
            },
            Guid = null,
            Name = "FormGuid",
        };

        //Act
        await _customFieldManager.CreateFieldAsync(auditResult, customField, default);

        //Assert
        Assert.That(_fakeCustomFieldReferenceRepository.CustomFieldGlossaries, Has.Count.EqualTo(1));
    }

    [Test]
    public async Task CreateCustomField_WithFieldTypeSequenceWithOnlyIdForRefObj_SaveChanges()
    {
        //Arrange
        var sequence = new Sequence
        {
            Id = 1,
            Deleted = false,
            Guid = new Guid("1db9c5f7-3ee8-4516-8b27-8c8047c8e04b"),
            Name = "Form Name"
        };
        _fakeCustomFieldReferenceRepository.Sequences.Add(sequence);
        var customField = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Sequence,
            RefElementId = new GeneralIdRef
            {
                Guid = null,
                Id = 1
            },
            Guid = null,
            Name = "FormGuid",
            MaxEntries = 1,
            MinEntries = 1,
        };

        //Act
        await _customFieldManager.CreateFieldAsync(auditResult, customField, default);
        
        //Assert
        Assert.Multiple(() =>
        {
            Assert.That(_fakeCustomFieldReferenceRepository.CustomFieldSequences, Has.Count.EqualTo(1));
            Assert.That(customField.MaxEntries, Is.EqualTo(1));
            Assert.That(customField.MinEntries, Is.EqualTo(1));
        });
    }

    [Test]
    public async Task CreateCustomField_WithFieldTypeSequenceWithRefIdOnlyGuidAutogenForTheCustomField_SaveChanges()
    {
        //Arrange
        var sequenceGuid = new Guid("9e22c2e5-b897-47af-b14a-d2b5903fb609");
        var audit = new AuditUserDetails();
        var createCustomField = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Sequence,
            RefElementId = new GeneralIdRef
            {
                Guid = sequenceGuid,
            },

            Guid = null,
            Name = "FormGuid",
            MaxEntries = 1,
            MinEntries = 1
        };
        var sequence = new Sequence
        {
            Id = 1,
            Guid = sequenceGuid,
            Name = "Test",
            Deleted = false,
        };
        _fakeCustomFieldReferenceRepository.Sequences.Add(sequence);

        //Act
        await _customFieldManager.CreateFieldAsync(audit, createCustomField, default);

        //Assert
        Assert.Multiple(() =>
        {
            Assert.That(_fakeCustomFieldReferenceRepository.CustomFieldSequences, Has.Count.EqualTo(1));
            Assert.That(_fakeCustomFieldReferenceRepository.CustomFieldSequences[0].CustomFieldId, Is.EqualTo(1));
            Assert.That(_fakeCustomFieldRepository!.CustomFields[0].MinEntries, Is.EqualTo(1));
            Assert.That(_fakeCustomFieldRepository!.CustomFields[0].MaxEntries, Is.EqualTo(1));
        });
    }

    [Test]
    public async Task CreateCustomField_WithFieldTypeGlossary_SaveChanges()
    {
        //Arrange
        var glossaryId = new Guid("7c5bd569-babf-45b5-9758-69e196b62446");
        var createCustomField = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Glossary,
            RefElementId = new GeneralIdRef
            {
                Guid = glossaryId,
            },

            Guid = null,
            Name = "FormGuid",
            MinEntries = 1,
            MaxEntries = 1,

        };
        var glossary = new Glossary
        {
            Id = 1,
            Name = "Some",
            Parent = null!,
            Guid = glossaryId
        };
        _fakeCustomFieldReferenceRepository.Glossaries.Add(glossary);

        //Act
        await _customFieldManager.CreateFieldAsync(auditResult, createCustomField, default);

        //Assert
        Assert.That(_fakeCustomFieldReferenceRepository.CustomFieldGlossaries, Has.Count.EqualTo(1));
    }

    [TestCase(0)]
    [TestCase(2)]
    public void CreateCustomField_SequenceWithMinEntriesNotSetToOne_ThrowsException(int minEntries)
    {
        //Arrange
        var sequence = new Sequence
        {
            Id = 1,
            Deleted = false,
            Guid = new Guid("206894df-2b4e-4a6f-98e1-401b539350b4"),
            Name = "Form Name"
        };
        _fakeCustomFieldReferenceRepository.Sequences.Add(sequence);
        var customField = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Sequence,
            RefElementId = new GeneralIdRef
            {
                Guid = null,
                Id = 1
            },
            Guid = null,
            Name = "SequenceTest",
            MaxEntries = 1,
            MinEntries = minEntries,
        };

        //Assert
        var exception = Assert.ThrowsAsync<ArgumentException>(async () =>
        {
            //Act
            await _customFieldManager.CreateFieldAsync(auditResult, customField, default);
        });

        Assert.Multiple(() =>
        {
            Assert.That(exception.ParamName, Is.EqualTo("MinEntries"));
            Assert.That(exception.Message,
                Is.EqualTo("Must be 1 for fields of type Sequence (Parameter 'MinEntries')"));
        });
    }

    [TestCase(0)]
    [TestCase(2)]
    public void CreateCustomField_SequenceWithMaxEntriesNotSetToOne_ThrowsException(int maxEntries)
    {
        //Arrange
        var sequence = new Sequence
        {
            Id = 1,
            Deleted = false,
            Guid = new Guid("ac8e9727-f43b-43f1-8fb9-0b3dbc1f41d9"),
            Name = "Form Name"
        };
        _fakeCustomFieldReferenceRepository.Sequences.Add(sequence);
        var customField = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Sequence,
            RefElementId = new GeneralIdRef
            {
                Guid = null,
                Id = 1
            },
            Guid = null,
            Name = "SequenceTest",
            MaxEntries = maxEntries,
            MinEntries = 1,
        };

        //Assert
        var exception = Assert.ThrowsAsync<ArgumentException>(async() =>
        {
            //Act
            await _customFieldManager.CreateFieldAsync(auditResult, customField, default);
        });

        Assert.Multiple(() =>
        {
            Assert.That(exception.ParamName, Is.EqualTo("MaxEntries"));
            Assert.That(exception.Message,
                Is.EqualTo("Must be 1 for fields of type Sequence (Parameter 'MaxEntries')"));
        });
    }

    [Test]
    public void CreateCustomField_WithFieldTypeNumber_WhenParametersNotSupplied_ThrowsException()
    {
        //Arrange
        var glossaryId = new Guid("7fde94f2-6105-4201-a2f4-45dcf70955c6");
        var createCustomField = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Number,
            
            Guid = null,
            Name = "FormGuid",
            MinEntries = 1,
            MaxEntries = 1,

        };
        var glossary = new Glossary
        {
            Id = 1,
            Name = "Some",
            Parent = null!,
            Guid = glossaryId
        };
        _fakeCustomFieldReferenceRepository.Glossaries.Add(glossary);

        //Assert
        Assert.ThrowsAsync<NullReferenceException>(async () => 
            {
                //Act
                await _customFieldManager.CreateFieldAsync(auditResult, createCustomField, default);
            }
        );
    }

    [Test]
    public async Task CreateCustomField_WithFieldTypeNumber_WhenParametersSupplied_SavesOk()
    {
        //Arrange
        var glossaryId = new Guid("7fde94f2-6105-4201-a2f4-45dcf70955c6");
        var createCustomField = new CreateCustomField
        {
            DefaultValue = "1",
            FieldType = FieldType.Number,

            Guid = null,
            Name = "FormGuid",
            MinEntries = 1,
            MaxEntries = 1,
            Parameters = "{ \"minValue\":\"2.3\", \"maxValue\": \"99.99\", \"step\": \"0.2\" }"
        };
        var glossary = new Glossary
        {
            Id = 1,
            Name = "Some",
            Parent = null!,
            Guid = glossaryId
        };
        _fakeCustomFieldReferenceRepository.Glossaries.Add(glossary);

        //Act
        await _customFieldManager.CreateFieldAsync(auditResult, createCustomField, default);

        //Assert
        Assert.Multiple(() =>
        {
            Assert.That(_fakeCustomFieldReferenceRepository.CustomFieldNumber, Has.Count.EqualTo(1));
            var number = _fakeCustomFieldReferenceRepository.CustomFieldNumber[0];
            Assert.That(number.MinimumValue, Is.EqualTo(2.3));
            Assert.That(number.MaximumValue, Is.EqualTo(99.99));
            Assert.That(number.Step, Is.EqualTo(0.2));
        });
    }
}
