package elasta.orm.entity.impl;

import com.google.common.collect.ImmutableList;
import elasta.orm.entity.DependencyInfo;
import elasta.orm.entity.DependencyTpl;
import elasta.orm.entity.EntitiesPreprocessor;
import elasta.orm.entity.TableDependency;
import elasta.orm.entity.core.ColumnType;
import elasta.orm.entity.core.Entity;
import elasta.orm.entity.core.ForeignColumnMapping;
import elasta.orm.entity.core.columnmapping.DbColumnMapping;
import elasta.orm.entity.core.columnmapping.IndirectDbColumnMapping;
import elasta.orm.entity.core.columnmapping.RelationMapping;
import elasta.orm.entity.core.columnmapping.impl.IndirectDbColumnMappingImpl;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by sohan on 3/17/2017.
 */
final public class EntitiesPreprocessorImpl implements EntitiesPreprocessor {
    @Override
    public List<Entity> process(Params params) {
        return new InternalEntityPreprocessorImpl(params).process();
    }

    final private class InternalEntityPreprocessorImpl {
        final Map<String, Entity> entityMap = new HashMap<>();
        private final Collection<Entity> entities;
        private final Map<String, Entity> entityNameToEntityMap;
        private final Map<String, TableDependency> tableToTableDependencyMap;

        public InternalEntityPreprocessorImpl(Params params) {
            entities = params.getEntities();
            entityNameToEntityMap = params.getEntityNameToEntityMap();
            tableToTableDependencyMap = params.getTableToTableDependencyMap();
        }

        public List<Entity> process() {

            entities.forEach(this::processEntity);

            return ImmutableList.copyOf(entityMap.values());
        }

        private Entity processEntity(Entity entity) {
            Arrays.stream(entity.getDbMapping().getDbColumnMappings())
                .map(dbColumnMapping -> {

                    return processColumnMapping(entity, dbColumnMapping);

                })
                .collect(Collectors.toList());
            return entity;
        }

        private DbColumnMapping processColumnMapping(Entity entity, DbColumnMapping dbColumnMapping) {
            switch (dbColumnMapping.getColumnType()) {
                case INDIRECT:
                    IndirectDbColumnMapping mapping = (IndirectDbColumnMapping) dbColumnMapping;
                    DependencyTpl dependencyTpl = checkCommonRelationalValidity(entity.getDbMapping().getTable(), mapping);

                    return dependencyTpl.getFieldToDependencyInfoMap().values().stream()
                        .filter(dependencyInfo -> {
                            if (dependencyInfo.getDbColumnMapping().getColumnType() == ColumnType.INDIRECT) {

                                IndirectDbColumnMapping depMapping = (IndirectDbColumnMapping) dependencyInfo.getDbColumnMapping();

                                if (mapping.getRelationTable().equals(depMapping.getRelationTable())) {
                                    return true;
                                }
                            }
                            return false;
                        })
                        .findAny()
                        .map(
                            dependencyInfo -> setValuesIfNecessary(
                                (IndirectDbColumnMapping) dbColumnMapping,
                                (IndirectDbColumnMapping) dependencyInfo.getDbColumnMapping()
                            ))
                        .orElse(dbColumnMapping);
            }
            return dbColumnMapping;
        }

        private DbColumnMapping setValuesIfNecessary(IndirectDbColumnMapping mapping, IndirectDbColumnMapping mappingOther) {
            if (mapping.getSrcForeignColumnMappingList().size() > 0 && mapping.getDstForeignColumnMappingList().size() > 0) {
                return mapping;
            }

            return new IndirectDbColumnMappingImpl(
                mapping.getReferencingTable(),
                mapping.getReferencingEntity(),
                mapping.getRelationTable(),
                mapping.getSrcForeignColumnMappingList().size() > 0 ? mapping.getSrcForeignColumnMappingList() : mappingOther.getDstForeignColumnMappingList(),
                mapping.getDstForeignColumnMappingList().size() > 0 ? mapping.getDstForeignColumnMappingList() : mappingOther.getSrcForeignColumnMappingList(),
                mapping.getField()
            );
        }

        private DependencyTpl checkCommonRelationalValidity(String table, RelationMapping mapping) {

            TableDependency tableDependency = tableToTableDependencyMap.get(table);

            Map<String, DependencyTpl> tableToDependencyInfoMap = tableDependency.getTableToDependencyInfoMap();

            return tableToDependencyInfoMap.get(mapping.getReferencingTable());
        }
    }
}

@@ -286,16 +286,57 @@
 mnType;%0A
+import elasta.orm.entity.core.DbMapping;%0A
 import e
@@ -1818,16 +1818,65 @@
         
+%0A            List%3CDbColumnMapping%3E mappingList = 
 Arrays.s
@@ -1969,38 +1969,8 @@
 g -%3E
- %7B%0A%0A                    return
  pro
@@ -2015,28 +2015,8 @@
 ing)
-;%0A%0A                %7D
 )%0A  
@@ -2056,24 +2056,25 @@
 .toList());%0A
+%0A
             
@@ -2080,22 +2080,397 @@
  return 
-entity
+new Entity(%0A                entity.getName(),%0A                entity.getPrimaryKey(),%0A                entity.getFields(),%0A                new DbMapping(%0A                    entity.getDbMapping().getTable(),%0A                    entity.getDbMapping().getPrimaryColumn(),%0A                    mappingList.toArray(new DbColumnMapping%5BmappingList.size()%5D)%0A                )%0A            )
 ;%0A      

fix returning same entity in EntitiesPreprocessorImpl