package com.sap.olingo.jpa.metadata.core.edm.mapper.impl;

import static com.sap.olingo.jpa.metadata.core.edm.mapper.exception.ODataJPAModelException.MessageKeys.PROPERTY_REQUIRED_UNKNOWN;
import static com.sap.olingo.jpa.processor.core.util.Assertions.assertListEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

import jakarta.persistence.Table;
import jakarta.persistence.metamodel.EntityType;

import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.provider.CsdlAnnotation;
import org.apache.olingo.commons.api.edm.provider.CsdlNavigationProperty;
import org.apache.olingo.commons.api.edm.provider.CsdlProperty;
import org.apache.olingo.commons.api.edm.provider.annotation.CsdlCollection;
import org.apache.olingo.commons.api.edm.provider.annotation.CsdlConstantExpression;
import org.apache.olingo.commons.api.edm.provider.annotation.CsdlConstantExpression.ConstantExpressionType;
import org.apache.olingo.commons.api.edm.provider.annotation.CsdlExpression;
import org.apache.olingo.server.api.uri.UriResourceProperty;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.reflections8.Reflections;

import com.sap.olingo.jpa.metadata.api.JPAEdmMetadataPostProcessor;
import com.sap.olingo.jpa.metadata.core.edm.annotation.EdmEnumeration;
import com.sap.olingo.jpa.metadata.core.edm.extension.vocabularies.Applicability;
import com.sap.olingo.jpa.metadata.core.edm.extension.vocabularies.ODataNavigationPath;
import com.sap.olingo.jpa.metadata.core.edm.extension.vocabularies.ODataPathNotFoundException;
import com.sap.olingo.jpa.metadata.core.edm.extension.vocabularies.ODataPropertyPath;
import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAAssociationAttribute;
import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAAssociationPath;
import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAAttribute;
import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPACollectionAttribute;
import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAEntityType;
import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAEtagValidator;
import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAInheritanceType;
import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAOnConditionItem;
import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAPath;
import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAProtectionInfo;
import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAStructuredType;
import com.sap.olingo.jpa.metadata.core.edm.mapper.exception.ODataJPAModelException;
import com.sap.olingo.jpa.metadata.core.edm.mapper.extension.IntermediateEntityTypeAccess;
import com.sap.olingo.jpa.metadata.core.edm.mapper.extension.IntermediateNavigationPropertyAccess;
import com.sap.olingo.jpa.metadata.core.edm.mapper.extension.IntermediatePropertyAccess;
import com.sap.olingo.jpa.metadata.core.edm.mapper.extension.IntermediateReferenceList;
import com.sap.olingo.jpa.metadata.core.edm.mapper.util.AnnotationTestHelper;
import com.sap.olingo.jpa.metadata.odata.v4.core.terms.ExampleProperties;
import com.sap.olingo.jpa.metadata.odata.v4.core.terms.Terms;
import com.sap.olingo.jpa.metadata.odata.v4.general.Aliases;
import com.sap.olingo.jpa.metadata.odata.v4.provider.JavaBasedCoreAnnotationsProvider;
import com.sap.olingo.jpa.processor.core.errormodel.TeamWithTransientError;
import com.sap.olingo.jpa.processor.core.testmodel.ABCClassification;
import com.sap.olingo.jpa.processor.core.testmodel.AdministrativeDivision;
import com.sap.olingo.jpa.processor.core.testmodel.AdministrativeDivisionDescription;
import com.sap.olingo.jpa.processor.core.testmodel.AnnotationsParent;
import com.sap.olingo.jpa.processor.core.testmodel.AssociationOneToOneSource;
import com.sap.olingo.jpa.processor.core.testmodel.BestOrganization;
import com.sap.olingo.jpa.processor.core.testmodel.BusinessPartner;
import com.sap.olingo.jpa.processor.core.testmodel.BusinessPartnerProtected;
import com.sap.olingo.jpa.processor.core.testmodel.BusinessPartnerRole;
import com.sap.olingo.jpa.processor.core.testmodel.Collection;
import com.sap.olingo.jpa.processor.core.testmodel.CollectionDeep;
import com.sap.olingo.jpa.processor.core.testmodel.CurrentUser;
import com.sap.olingo.jpa.processor.core.testmodel.CurrentUserQueryExtension;
import com.sap.olingo.jpa.processor.core.testmodel.DeepProtectedExample;
import com.sap.olingo.jpa.processor.core.testmodel.DummyToBeIgnored;
import com.sap.olingo.jpa.processor.core.testmodel.EmptyQueryExtensionProvider;
import com.sap.olingo.jpa.processor.core.testmodel.EntityTypeOnly;
import com.sap.olingo.jpa.processor.core.testmodel.InheritanceByJoinAccount;
import com.sap.olingo.jpa.processor.core.testmodel.InheritanceByJoinCompoundSub;
import com.sap.olingo.jpa.processor.core.testmodel.InheritanceByJoinCompoundSuper;
import com.sap.olingo.jpa.processor.core.testmodel.InheritanceByJoinCurrentAccount;
import com.sap.olingo.jpa.processor.core.testmodel.InheritanceByJoinLockedSavingAccount;
import com.sap.olingo.jpa.processor.core.testmodel.InheritanceByJoinSavingAccount;
import com.sap.olingo.jpa.processor.core.testmodel.JoinRelation;
import com.sap.olingo.jpa.processor.core.testmodel.Organization;
import com.sap.olingo.jpa.processor.core.testmodel.Person;
import com.sap.olingo.jpa.processor.core.testmodel.PersonDeepProtected;
import com.sap.olingo.jpa.processor.core.testmodel.PersonDeepProtectedHidden;
import com.sap.olingo.jpa.processor.core.testmodel.PersonImage;
import com.sap.olingo.jpa.processor.core.testmodel.RestrictedEntityUnrestrictedSource;
import com.sap.olingo.jpa.processor.core.testmodel.SalesTeam;
import com.sap.olingo.jpa.processor.core.testmodel.Singleton;
import com.sap.olingo.jpa.processor.core.testmodel.TransientRefComplex;
import com.sap.olingo.jpa.processor.core.testmodel.TransientRefIgnore;
import com.sap.olingo.jpa.processor.core.testmodel.UnionMembership;
import com.sap.olingo.jpa.processor.core.util.TestDataConstants;

class IntermediateEntityTypeTest extends TestMappingRoot {
  private IntermediateSchema schema;
  private IntermediateSchema errorSchema;
  private IntermediateAnnotationInformation annotationInfo;
  private IntermediateReferences references;

  @BeforeEach
  void setup() throws ODataJPAModelException {
    IntermediateModelElement.setPostProcessor(new DefaultEdmPostProcessor());
    final Reflections reflections = mock(Reflections.class);
    when(reflections.getTypesAnnotatedWith(EdmEnumeration.class)).thenReturn(new HashSet<>(Arrays.asList(
        ABCClassification.class)));
    references = mock(IntermediateReferences.class);
    annotationInfo = new IntermediateAnnotationInformation(new ArrayList<>(), references);
    schema = new IntermediateSchema(new JPADefaultEdmNameBuilder(PUNIT_NAME), emf.getMetamodel(), reflections,
        annotationInfo);
    errorSchema = new IntermediateSchema(new JPADefaultEdmNameBuilder(ERROR_PUNIT), errorEmf.getMetamodel(),
        reflections, annotationInfo);
  }

  @Test
  void checkEntityTypeCanBeCreated() {

    assertNotNull(new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema));
  }

  @Test
  void checkEntityTypeIgnoreSet() throws ODataJPAModelException {

    final IntermediateStructuredType<DummyToBeIgnored> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(DummyToBeIgnored.class), schema);
    et.getEdmItem();
    assertTrue(et.ignore());
  }

  @Test
  void checkGetAllProperties() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertEquals(TestDataConstants.NO_DEC_ATTRIBUTES_BUSINESS_PARTNER.value, et.getEdmItem()
        .getProperties()
        .size(), "Wrong number of entities");
  }

  @Test
  void checkGetPropertyByNameNotNull() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertNotNull(et.getEdmItem().getProperty("Type"));
  }

  @Test
  void checkGetPropertyByNameCorrectEntity() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertEquals("Type", et.getEdmItem().getProperty("Type").getName());
  }

  @Test
  void checkGetPropertyByNameCorrectEntityID() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertEquals("ID", et.getEdmItem().getProperty("ID").getName());
  }

  @Test
  void checkGetPathByNameCorrectEntityID() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertEquals("ID", et.getPath("ID").getLeaf().getExternalName());
  }

  @Test
  void checkGetPathByNameIgnore() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertNull(et.getPath("CustomString2"));
  }

  @Test
  void checkGetDescriptionPropertyByName() throws ODataJPAModelException {
    final IntermediateStructuredType<Organization> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(Organization.class), schema);
    assertNotNull(et.getAttribute("locationName"));
    assertNotNull(et.getBaseType().getEdmItem().getProperty("LocationName"));
  }

  @Test
  void checkGetDescriptionPropertyFromComplexByName() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertTrue(et.getAttribute("address").isPresent());
    assertTrue(et.getAttribute("address").get().getStructuredType().getAttribute("countryName").isPresent());
    // Trigger cascade of building the edm items
    assertNotNull(et.getEdmItem().getProperty("Address"));
    final IntermediateDescriptionProperty act = (IntermediateDescriptionProperty) et.getAttribute("address").get()
        .getStructuredType().getAttribute("countryName").get();
    assertNotNull(act.getDescriptionAttribute());
    assertNotNull(act.getTargetEntity());
    assertNotNull(act.getLocaleFieldName());
    assertNotNull(act.getFixedValueAssignment());
  }

  @Test
  void checkGetPathByNameIgnoreComplexType() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertNull(et.getPath("Address/RegionCodePublisher"));
  }

  @Test
  void checkGetInheritedAttributeByNameCorrectEntityID() throws ODataJPAModelException {
    final IntermediateStructuredType<Person> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(PUNIT_NAME),
        getEntityType(Person.class), schema);
    assertEquals("ID", et.getPath("ID").getLeaf().getExternalName());
  }

  @Test
  void checkGetAllNavigationProperties() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertEquals(1, et.getEdmItem().getNavigationProperties().size(), "Wrong number of entities");
  }

  @Test
  void checkGetNavigationPropertyByNameNotNull() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertNotNull(et.getEdmItem().getNavigationProperty("Roles"));
  }

  @Test
  void checkGetNavigationPropertyByNameCorrectEntity() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertEquals("Roles", et.getEdmItem().getNavigationProperty("Roles").getName());
  }

  @Test
  void checkGetAssociationOfComplexTypeByNameCorrectEntity() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertEquals("Address/AdministrativeDivision", et.getAssociationPath("Address/AdministrativeDivision").getAlias());
  }

  @Test
  void checkGetAssociationOfComplexTypeByNameJoinColumns() throws ODataJPAModelException {
    int actCount = 0;
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    for (final JPAOnConditionItem item : et.getAssociationPath("Address/AdministrativeDivision").getJoinColumnsList()) {
      if (item.getLeftPath().getAlias().equals("Address/Region")) {
        assertEquals("DivisionCode", item.getRightPath().getAlias());
        actCount++;
      }
      if (item.getLeftPath().getAlias().equals("Address/RegionCodeID")) {
        assertEquals("CodeID", item.getRightPath().getAlias());
        actCount++;
      }
      if (item.getLeftPath().getAlias().equals("Address/RegionCodePublisher")) {
        assertEquals("CodePublisher", item.getRightPath().getAlias());
        actCount++;
      }
    }
    assertEquals(3, actCount, "Not all join columns found");
  }

  @Test
  void checkGetAssociationOfMappedByWithVirtualProperty() throws ODataJPAModelException {

    final IntermediateStructuredType<AssociationOneToOneSource> et = new IntermediateEntityType<>(
        new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(AssociationOneToOneSource.class), schema);
    final JPAAssociationPath act = et.getAssociationPath("DefaultTarget");
    assertEquals(1, act.getJoinColumnsList().size());
    final JPAOnConditionItem actColumn = act.getJoinColumnsList().get(0);
    assertEquals("ID", actColumn.getRightPath().getAlias());
    assertEquals("Defaulttarget_id", actColumn.getLeftPath().getAlias());
  }

  @Test
  void checkGetPropertiesSkipIgnored() throws ODataJPAModelException {
    final PostProcessorSetIgnore postProcessorDouble = new PostProcessorSetIgnore();
    IntermediateModelElement.setPostProcessor(postProcessorDouble);

    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertEquals(TestDataConstants.NO_DEC_ATTRIBUTES_BUSINESS_PARTNER.value - 1, et.getEdmItem()
        .getProperties().size(), "Wrong number of entities");
  }

  @Test
  void checkGetIsAbstract() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertTrue(et.getEdmItem().isAbstract());
  }

  @Test
  void checkGetIsNotAbstract() throws ODataJPAModelException {
    final IntermediateStructuredType<Organization> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(Organization.class), schema);
    assertFalse(et.getEdmItem().isAbstract());
  }

  @Test
  void checkGetHasBaseType() throws ODataJPAModelException {
    final IntermediateStructuredType<Organization> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(Organization.class), schema);
    assertEquals(PUNIT_NAME + ".BusinessPartner", et.getEdmItem().getBaseType());
  }

  @Test
  void checkGetKeyProperties() throws ODataJPAModelException {
    final IntermediateEntityType<BusinessPartnerRole> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartnerRole.class), schema);
    assertEquals(2, et.getEdmItem().getKey().size(), "Wrong number of key properties");

  }

  @Test
  void checkGetAllAttributes() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartnerRole> et = new IntermediateEntityType<>(
        new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(BusinessPartnerRole.class), schema);
    assertEquals(2, et.getPathList().size(), "Wrong number of entities");
  }

  @Test
  void checkGetAllAttributesWithBaseType() throws ODataJPAModelException {
    final IntermediateStructuredType<Organization> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(Organization.class), schema);
    final int exp =
        TestDataConstants.NO_ATTRIBUTES_BUSINESS_PARTNER.value
            + TestDataConstants.NO_ATTRIBUTES_POSTAL_ADDRESS.value
            + TestDataConstants.NO_ATTRIBUTES_COMMUNICATION_DATA.value
            + 2 * TestDataConstants.NO_ATTRIBUTES_CHANGE_INFO.value
            + TestDataConstants.NO_ATTRIBUTES_ORGANIZATION.value;
    assertEquals(exp, et.getPathList().size(), "Wrong number of entities");
  }

  @Test
  void checkGetAllAttributesWithBaseTypeFields() throws ODataJPAModelException {
    final IntermediateStructuredType<Organization> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(Organization.class), schema);

    assertNotNull(et.getPath("Type"));
    assertNotNull(et.getPath("Name1"));
    assertNotNull(et.getPath("Address" + JPAPath.PATH_SEPARATOR + "Region"));
    assertNotNull(et.getPath("AdministrativeInformation" + JPAPath.PATH_SEPARATOR
        + "Created" + JPAPath.PATH_SEPARATOR + "By"));
  }

  @Test
  void checkGetAllAttributeIDWithBaseType() throws ODataJPAModelException {
    final IntermediateStructuredType<Organization> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(Organization.class), schema);
    assertEquals("ID", et.getPath("ID").getAlias());
  }

  @Test
  void checkGetKeyAttributeFromEmbeddedId() throws ODataJPAModelException {
    final IntermediateEntityType<AdministrativeDivisionDescription> et = new IntermediateEntityType<>(
        new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(AdministrativeDivisionDescription.class), schema);

    assertTrue(et.getAttribute("codePublisher").isPresent());
    assertEquals("CodePublisher", et.getAttribute("codePublisher").get().getExternalName());
  }

  @Test
  void checkGetKeyAttributeFromEmbeddedIdFromUriResource() throws ODataJPAModelException {
    final IntermediateEntityType<AdministrativeDivisionDescription> et = new IntermediateEntityType<>(
        new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(AdministrativeDivisionDescription.class), schema);

    final UriResourceProperty uriResourceItem = mock(UriResourceProperty.class);
    final EdmProperty edmProperty = mock(EdmProperty.class);
    when(uriResourceItem.getProperty()).thenReturn(edmProperty);
    when(edmProperty.getName()).thenReturn("CodePublisher");

    final Optional<JPAAttribute> act = et.getAttribute(uriResourceItem);

    assertTrue(act.isPresent());
    assertEquals("CodePublisher", act.get().getExternalName());
  }

  @Test
  void checkGetKeyWithBaseType() throws ODataJPAModelException {
    final IntermediateEntityType<Organization> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(Organization.class), schema);
    assertEquals(1, et.getKey().size());
  }

  @Test
  void checkGetKeyFromMappedSuperclass() throws ODataJPAModelException {
    final IntermediateEntityType<SalesTeam> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(SalesTeam.class), schema);
    assertEquals(1, et.getKey().size());
  }

  @Test
  void checkEmbeddedIdResolvedProperties() throws ODataJPAModelException {
    final IntermediateEntityType<AdministrativeDivisionDescription> et = new IntermediateEntityType<>(
        new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(AdministrativeDivisionDescription.class), schema);
    assertEquals(5, et.getEdmItem().getProperties().size());
  }

  @Test
  void checkEmbeddedIdResolvedKey() throws ODataJPAModelException {
    final IntermediateEntityType<AdministrativeDivisionDescription> et = new IntermediateEntityType<>(
        new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(AdministrativeDivisionDescription.class), schema);
    assertEquals(4, et.getEdmItem().getKey().size());
  }

  @Test
  void checkEmbeddedIdResolvedKeyInternal() throws ODataJPAModelException {
    final IntermediateEntityType<AdministrativeDivisionDescription> et = new IntermediateEntityType<>(
        new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(AdministrativeDivisionDescription.class), schema);
    assertEquals(4, et.getKey().size());
  }

  @Test
  void checkEmbeddedIdResolvedKeyCorrectOrder() throws ODataJPAModelException {
    final IntermediateEntityType<AdministrativeDivisionDescription> et = new IntermediateEntityType<>(
        new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(AdministrativeDivisionDescription.class), schema);

    assertEquals("CodePublisher", et.getEdmItem().getKey().get(0).getName());
    assertEquals("CodeID", et.getEdmItem().getKey().get(1).getName());
    assertEquals("DivisionCode", et.getEdmItem().getKey().get(2).getName());
    assertEquals("Language", et.getEdmItem().getKey().get(3).getName());
  }

  @Test
  void checkCompoundResolvedKeyCorrectOrder() throws ODataJPAModelException {
    final IntermediateEntityType<AdministrativeDivision> et = new IntermediateEntityType<>(
        new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(AdministrativeDivision.class), schema);

    assertEquals("CodePublisher", et.getKey().get(0).getExternalName());
    assertEquals("CodeID", et.getKey().get(1).getExternalName());
    assertEquals("DivisionCode", et.getKey().get(2).getExternalName());
  }

  @Test
  void checkEmbeddedIdResolvedPath() throws ODataJPAModelException {
    final JPAStructuredType et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(
        AdministrativeDivisionDescription.class), schema);
    assertEquals(5, et.getPathList().size());
  }

  @Test
  void checkEmbeddedIdResolvedPathCodeId() throws ODataJPAModelException {
    final JPAStructuredType et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(
        AdministrativeDivisionDescription.class), schema);
    assertEquals(2, et.getPath("CodeID").getPath().size());
  }

  @Test
  void checkEmbeddedIdResolvedKeyPath() throws ODataJPAModelException {
    final JPAEntityType et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(
        AdministrativeDivisionDescription.class), schema);
    assertEquals(1, et.getKeyPath().size());
  }

  @Test
  void checkEmbeddedIdInheritedWithMappingKeyPath() throws ODataJPAModelException {
    final JPAEntityType et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(
        InheritanceByJoinCompoundSub.class), schema);

    assertEquals(3, et.getKey().size());
    var divisionKey = et.getKey().stream().filter(key -> "DivisionCode".equals(key.getExternalName())).findFirst();
    assertNotNull(divisionKey);
    assertEquals("\"PartCode\"", ((IntermediateSimpleProperty) divisionKey.get()).dbFieldName);

    assertEquals(3, et.getKeyPath().size());
    var divisionCode = et.getKeyPath().stream().filter(path -> "DivisionCode".equals(path.getAlias()))
        .findFirst();
    assertTrue(divisionCode.isPresent());
    assertEquals("\"PartCode\"", divisionCode.get().getDBFieldName());
  }

  @Test
  void checkInheritedWithMappingKeyPath() throws ODataJPAModelException {
    final JPAEntityType et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(
        InheritanceByJoinLockedSavingAccount.class), schema);

    assertEquals(1, et.getKey().size());
    var key = et.getKey().get(0);
    assertNotNull(key);
    assertEquals("\"AccountId\"", ((IntermediateSimpleProperty) key).dbFieldName);

    assertEquals(1, et.getKeyPath().size());
    var keyPath = et.getKeyPath().get(0);
    assertEquals("\"AccountId\"", keyPath.getDBFieldName());
  }

  @Test
  void checkHasStreamNoProperties() throws ODataJPAModelException {
    final IntermediateEntityType<PersonImage> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(PersonImage.class), schema);
    assertEquals(2, et.getEdmItem().getProperties().size());
  }

  @Test
  void checkHasStreamTrue() throws ODataJPAModelException {
    final IntermediateEntityType<PersonImage> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(PersonImage.class), schema);
    assertTrue(et.getEdmItem().hasStream());
  }

  @Test
  void checkHasStreamFalse() throws ODataJPAModelException {
    final IntermediateEntityType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertFalse(et.getEdmItem().hasStream());
  }

  @Test
  void checkHasEtagTrue() throws ODataJPAModelException {
    final IntermediateEntityType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertTrue(et.hasEtag());
  }

  @Test
  void checkHasEtagTrueIfInherited() throws ODataJPAModelException {
    final IntermediateEntityType<Organization> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(Organization.class), schema);
    assertTrue(et.hasEtag());
  }

  @Test
  void checkHasEtagFalse() throws ODataJPAModelException {
    final IntermediateEntityType<AdministrativeDivision> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(AdministrativeDivision.class), schema);
    assertFalse(et.hasEtag());
  }

  @Test
  void checkIgnoreIfAsEntitySet() {
    final IntermediateEntityType<BestOrganization> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BestOrganization.class), schema);
    assertTrue(et.ignore());
  }

  @Test
  void checkEtagValidatorNullWithoutEtag() throws ODataJPAModelException {
    final IntermediateEntityType<AdministrativeDivision> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(AdministrativeDivision.class), schema);
    assertNull(et.getEtagValidator());
  }

  @Test
  void checkEtagValidatorIsStrongForLong() throws ODataJPAModelException {
    final IntermediateEntityType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertEquals(JPAEtagValidator.STRONG, et.getEtagValidator());
  }

  @Test
  void checkEtagValidatorIsWeakForTimestamp() throws ODataJPAModelException {
    final IntermediateEntityType<DeepProtectedExample> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(DeepProtectedExample.class), schema);
    assertEquals(JPAEtagValidator.WEAK, et.getEtagValidator());
  }

  @Test
  void checkEtagValidatorIsInherited() throws ODataJPAModelException {
    final IntermediateEntityType<Person> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(Person.class), schema);
    assertEquals(JPAEtagValidator.STRONG, et.getEtagValidator());
  }

  @Test
  void checkAnnotationSet() throws ODataJPAModelException {
    IntermediateModelElement.setPostProcessor(new PostProcessorSetIgnore());
    final IntermediateEntityType<PersonImage> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(PersonImage.class), schema);
    final List<CsdlAnnotation> act = et.getEdmItem().getAnnotations();
    assertEquals(1, act.size());
    assertEquals("Core.AcceptableMediaTypes", act.get(0).getTerm());
  }

  @Test
  void checkGetPropertyByDBFieldName() throws ODataJPAModelException {
    final IntermediateStructuredType<BusinessPartner> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(BusinessPartner.class), schema);
    assertEquals("Type", et.getPropertyByDBField("\"Type\"").getExternalName());
  }

  @Test
  void checkGetPropertyByDBFieldNameFromSuperType() throws ODataJPAModelException {
    final IntermediateStructuredType<Organization> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(Organization.class), schema);
    assertEquals("Type", et.getPropertyByDBField("\"Type\"").getExternalName());
  }

  @Test
  void checkGetPropertyByDBFieldNameFromEmbedded() throws ODataJPAModelException {
    final IntermediateStructuredType<AdministrativeDivisionDescription> et = new IntermediateEntityType<>(
        new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(AdministrativeDivisionDescription.class), schema);
    assertEquals("CodeID", et.getPropertyByDBField("\"CodeID\"").getExternalName());
  }

  @Test
  void checkAllPathContainsComplexCollection() throws ODataJPAModelException {
    final IntermediateStructuredType<Collection> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(Collection.class), schema);
    final List<JPAPath> act = et.getPathList();

    assertEquals(13, act.size());
    assertNotNull(et.getPath("Complex/Address"));
    assertTrue(et.getPath("Complex/Address").getLeaf().isCollection());
    final IntermediateCollectionProperty<?> actIntermediate = (IntermediateCollectionProperty<?>) et.getPath(
        "Complex/Address").getLeaf();
    assertTrue(actIntermediate.asAssociation().getSourceType() instanceof JPAEntityType);
    assertEquals(2, actIntermediate.asAssociation().getPath().size());

    for (final JPAPath p : act) {
      if (p.getPath().size() > 1
          && p.getPath().get(0).getExternalName().equals("Complex")
          && p.getPath().get(1).getExternalName().equals("Address")) {
        assertTrue(p.getPath().get(1) instanceof IntermediateCollectionProperty);
        final IntermediateCollectionProperty<?> actProperty = (IntermediateCollectionProperty<?>) p.getPath().get(1);
        assertNotNull(actProperty.asAssociation());
        assertEquals(et, actProperty.asAssociation().getSourceType());
        break;
      }
    }
  }

  @Test
  void checkAllPathContainsPrimitiveCollection() throws ODataJPAModelException {
    final IntermediateStructuredType<Collection> et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(
        PUNIT_NAME), getEntityType(Collection.class), schema);
    final List<JPAPath> act = et.getPathList();

    assertEquals(13, act.size());
    assertNotNull(et.getPath("Complex/Comment"));
    assertTrue(et.getPath("Complex/Comment").getLeaf().isCollection());
    final IntermediateCollectionProperty<?> actIntermediate = (IntermediateCollectionProperty<?>) et.getPath(
