public async Task Map_NestedTopics_ReturnsMappedTopic() {

      var mappingService        = new ReverseTopicMappingService(_topicRepository);
      var bindingModel          = new ContentTypeDescriptorTopicBindingModel("Test");

      bindingModel.Attributes.Add(new TextAttributeTopicBindingModel("Attribute1"));
      bindingModel.Attributes.Add(new TextAttributeTopicBindingModel("Attribute2"));
      bindingModel.Attributes.Add(new TextAttributeTopicBindingModel("Attribute3") { DefaultValue = "New Value" });

      var topic                 = TopicFactory.Create("Test", "ContentTypeDescriptor");
      var attributes            = TopicFactory.Create("Attributes", "List", topic);

      var attribute3            = (AttributeDescriptor)TopicFactory.Create("Attribute3", "TextAttribute", attributes);
      var attribute4            = TopicFactory.Create("Attribute4", "TextAttribute", attributes);

      attribute3.DefaultValue   = "Original Value";

      var target                = (ContentTypeDescriptor?)await mappingService.MapAsync(bindingModel, topic).ConfigureAwait(false);

      Assert.AreEqual<int>(3, target.AttributeDescriptors.Count);
      Assert.IsNotNull(target.AttributeDescriptors.GetTopic("Attribute1"));
      Assert.IsNotNull(target.AttributeDescriptors.GetTopic("Attribute2"));
      Assert.IsNotNull(target.AttributeDescriptors.GetTopic("Attribute3"));
      Assert.AreEqual<string>("New Value", target.AttributeDescriptors.GetTopic("Attribute3").DefaultValue);
      Assert.IsNull(target.AttributeDescriptors.GetTopic("Attribute4"));

    }
    public async Task Map_Relationships_ReturnsMappedTopic() {

      var mappingService        = new ReverseTopicMappingService(_topicRepository);
      var bindingModel          = new ContentTypeDescriptorTopicBindingModel("Test");
      var contentTypes          = _topicRepository.GetContentTypeDescriptors();
      var topic                 = (ContentTypeDescriptor)TopicFactory.Create("Test", "ContentTypeDescriptor");

      topic.Relationships.SetTopic("ContentTypes", contentTypes[4]);

      for (var i = 0; i < 3; i++) {
        bindingModel.ContentTypes.Add(
          new() {
            UniqueKey = contentTypes[i].GetUniqueKey()
          }
        );
      }

      var target                = (ContentTypeDescriptor?)await mappingService.MapAsync(bindingModel, topic).ConfigureAwait(false);

      Assert.AreEqual<int>(3, target.PermittedContentTypes.Count);
      Assert.IsTrue(target.PermittedContentTypes.Contains(contentTypes[0]));
      Assert.IsTrue(target.PermittedContentTypes.Contains(contentTypes[1]));
      Assert.IsTrue(target.PermittedContentTypes.Contains(contentTypes[2]));
      Assert.IsFalse(target.PermittedContentTypes.Contains(contentTypes[3]));

    }
    public async Task Map_Existing_ReturnsUpdatedTopic() {

      var mappingService        = new ReverseTopicMappingService(_topicRepository);

      var bindingModel          = new TextAttributeTopicBindingModel() {
        Key                     = "Test",
        ContentType             = "TextAttribute",
        Title                   = null,
        DefaultValue            = "World",
        IsRequired              = false
      };

      var target                = (TextAttribute?)TopicFactory.Create("Test", "TextAttribute");

      target.Title              = "Original Attribute";
      target.DefaultValue       = "Hello";
      target.IsRequired         = true;
      target.IsExtendedAttribute= false;

      target.Attributes.SetValue("Description", "Original Description");

      target                    = (TextAttribute?)await mappingService.MapAsync(bindingModel, target).ConfigureAwait(false);

      Assert.AreEqual<string>("Test", target.Key);
      Assert.AreEqual<string>("TextAttribute", target.ContentType);
      Assert.AreEqual<string>("Test", target.Title); //Should inherit from "Key" since it will be null
      Assert.AreEqual<string>("World", target.DefaultValue);
      Assert.AreEqual<bool>(false, target.IsRequired);
      Assert.AreEqual<bool>(false, target.IsExtendedAttribute);
      Assert.AreEqual<string>("Original Description", target.Attributes.GetValue("Description"));

    }
    public async Task Map_InvalidTopicReferenceType_ThrowsInvalidOperationException() {

      var mappingService        = new ReverseTopicMappingService(_topicRepository);
      var bindingModel          = new InvalidReferenceTypeTopicBindingModel("Test");

      var target = await mappingService.MapAsync(bindingModel).ConfigureAwait(false);

    }
    public async Task Map_ValidRequiredProperty_IsMapped() {

      var mappingService        = new ReverseTopicMappingService(_topicRepository);
      var bindingModel          = new PageTopicBindingModel("Test");

      var target                = await mappingService.MapAsync(bindingModel).ConfigureAwait(false);

    }
    public async Task Map_InvalidParentProperty_ThrowsInvalidOperationException() {

      var mappingService        = new ReverseTopicMappingService(_topicRepository);

      var bindingModel          = new InvalidParentTopicBindingModel("Test") {
        Parent                  = new("Test", "Page")
      };

      var target = await mappingService.MapAsync(bindingModel).ConfigureAwait(false);

    }
    public async Task Map_ExceedsMinimumValue_ThrowsValidationException() {

      var mappingService        = new ReverseTopicMappingService(_topicRepository);

      var bindingModel          = new MinimumLengthPropertyTopicBindingModel("Test") {
        Title                   = "Hello World"
      };

      var target = await mappingService.MapAsync(bindingModel).ConfigureAwait(false);

    }
    public async Task Map_DisabledProperty_IsNotMapped() {

      var mappingService        = new ReverseTopicMappingService(_topicRepository);

      var bindingModel          = new DisabledAttributeTopicBindingModel("Test") {
        UnmappedAttribute       = "Hello World"
      };

      var target = await mappingService.MapAsync(bindingModel).ConfigureAwait(false);

      Assert.IsNull(target.Attributes.GetValue("UnmappedAttribute", null));

    }
    public async Task Map_NullProperty_MapsDefaultValue() {

      var mappingService        = new ReverseTopicMappingService(_topicRepository);

      var bindingModel          = new PageTopicBindingModel("Test") {
        Title                   = "Required Title"
      };

      var target                = await mappingService.MapAsync(bindingModel).ConfigureAwait(false);

      Assert.AreEqual<string>("Default page description", target.Attributes.GetValue("MetaDescription"));

    }
    public async Task Map_AlternateAttributeKey_ReturnsMappedTopic() {

      var mappingService        = new ReverseTopicMappingService(_topicRepository);

      var bindingModel          = new PageTopicBindingModel {
        Key                     = "Test",
        ContentType             = "Page",
        Title                   = "Test Page",
        BrowserTitle            = "Browser Title"
      };

      var target                = await mappingService.MapAsync(bindingModel).ConfigureAwait(false);

      Assert.AreEqual<string>("Browser Title", target.Attributes.GetValue("MetaTitle"));

    }
    public async Task Map_TopicReferences_ReturnsMappedTopic() {

      var mappingService        = new ReverseTopicMappingService(_topicRepository);

      var bindingModel          = new ReferenceTopicBindingModel("Test") {
        DerivedTopic            = new() {
          UniqueKey             = _topicRepository.Load("Root:Configuration:ContentTypes:Attributes:Title").GetUniqueKey()
        }
      };

      var target                = (TopicReferenceAttribute?)await mappingService.MapAsync(bindingModel).ConfigureAwait(false);

      target.DerivedTopic       = _topicRepository.Load(target.Attributes.GetInteger("TopicId", -5));

      Assert.IsNotNull(target.DerivedTopic);
      Assert.AreEqual<string>("TopicReference", target.EditorType);

    }
    public async Task Map_Generic_ReturnsNewTopic() {

      var mappingService        = new ReverseTopicMappingService(_topicRepository);

      var bindingModel          = new TextAttributeTopicBindingModel() {
        Key                     = "Test",
        ContentType             = "TextAttribute",
        Title                   = "Test Attribute",
        DefaultValue            = "Hello",
        IsRequired              = true
      };

      var target                = await mappingService.MapAsync<TextAttribute>(bindingModel).ConfigureAwait(false);

      Assert.AreEqual<string>("Test", target.Key);
      Assert.AreEqual<string>("TextAttribute", target.ContentType);
      Assert.AreEqual<string>("Test Attribute", target.Title);
      Assert.AreEqual<string>("Hello", target.DefaultValue);
      Assert.AreEqual<bool>(true, target.IsRequired);

    }
    public async Task Map_ComplexObject_ReturnsFlattenedTopic() {

      var mappingService        = new ReverseTopicMappingService(_topicRepository);

      var bindingModel          = new MapToParentTopicBindingModel {
        Key                     = "Test",
        ContentType             = "Contact"
      };

      bindingModel.PrimaryContact.Name                          = "Jeremy";
      bindingModel.AlternateContact.Email                       = "*****@*****.**";
      bindingModel.BillingContact.Email                         = "*****@*****.**";

      var target                = (Topic?)await mappingService.MapAsync(bindingModel).ConfigureAwait(false);

      Assert.IsNotNull(target);
      Assert.AreEqual<string>("Jeremy", target.Attributes.GetValue("Name"));
      Assert.AreEqual<string>("*****@*****.**", target.Attributes.GetValue("AlternateEmail"));
      Assert.AreEqual<string>("*****@*****.**", target.Attributes.GetValue("BillingContactEmail"));

    }