package org.slc.sli.api.security.context.resolver;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;

import org.slc.sli.api.config.AssociationDefinition;
import org.slc.sli.api.config.EntityDefinitionStore;
import org.slc.sli.api.config.EntityNames;
import org.slc.sli.api.config.ResourceNames;
import org.slc.sli.api.security.context.AssociativeContextHelper;
import org.slc.sli.domain.Entity;
import org.slc.sli.domain.EntityQuery;
import org.slc.sli.domain.EntityRepository;

/**
 * Resolves which teachers a given teacher is allowed to see
 * 
 * @author dkornishev
 * 
 */
@Component
public class TeacherSessionResolver implements EntityContextResolver {
    
    @Autowired
    private AssociativeContextHelper helper;
    
    @Autowired
    private EntityRepository repository;
    @Autowired
    private EntityDefinitionStore definitionStore;
    
    @Override
    public boolean canResolve(String fromEntityType, String toEntityType) {
        return EntityNames.TEACHER.equals(fromEntityType) && EntityNames.SESSION.equals(toEntityType);
    }
    
    @Override
    public List<String> findAccessible(Entity principal) {
        List<String> ids = new ArrayList<String>(Arrays.asList(principal.getEntityId()));

        AssociationDefinition teacherSectionDef = (AssociationDefinition) definitionStore
                .lookupByResourceName(ResourceNames.TEACHER_SECTION_ASSOCIATIONS);
        AssociationDefinition sectionStudentDef = (AssociationDefinition) definitionStore
                .lookupByResourceName(ResourceNames.STUDENT_SECTION_ASSOCIATIONS);

        ids = findIdsFromAssociation(ids, EntityNames.TEACHER, EntityNames.SECTION, teacherSectionDef);
        ids = findIdsFromAssociation(ids, EntityNames.SECTION, EntityNames.STUDENT, sectionStudentDef);
        ids = findIdsFromAssociation(ids, EntityNames.STUDENT, EntityNames.SECTION, sectionStudentDef);
        
        StringBuilder query = new StringBuilder();
        String separator = "";
        
        for (String s : ids) {
            query.append(separator);
            separator = ",";            
            query.append(s);
        }
        
        EntityQuery.EntityQueryBuilder queryBuilder = new EntityQuery.EntityQueryBuilder();
        queryBuilder.addField("_id", query.toString());
        
        Iterable<Entity> entities = repository.findAll(EntityNames.SECTION, queryBuilder.build());

        List<String> sessionIds = new ArrayList<String>();
        
        for (Entity e : entities) {
            sessionIds.add((String) e.getBody().get("sessionId"));
        }

        return sessionIds;
    }
    
    private List<String> findIdsFromAssociation(List<String> ids, String sourceType, String targetType, AssociationDefinition definition) {
        List<String> associationIds = new ArrayList<String>();
        
        List<String> keys = getAssocKeys(sourceType, definition);
        String sourceKey = keys.get(0);
        String targetKey = keys.get(1);
        
        Iterable<Entity> entities = this.repository.findByQuery(definition.getStoredCollectionName(), new Query(Criteria.where("body." + sourceKey).in(ids)), 0, 9999);
        
        for (Entity e : entities) {
            associationIds.add((String) e.getBody().get(targetKey));
        }
        
        return associationIds;
    }
    
    private List<String> getAssocKeys(String entityType, AssociationDefinition ad) {
        
        if (ad.getSourceEntity().getType().equals(entityType)) {
            return Arrays.asList(ad.getSourceKey(), ad.getTargetKey());
        } else if (ad.getTargetEntity().getType().equals(entityType)) {
            return Arrays.asList(ad.getTargetKey(), ad.getSourceKey());
        } else {
            throw new IllegalArgumentException("Entity is not a member of association " + entityType + " " + ad.getType());
        }
    }
}

@@ -216,255 +216,30 @@
 ork.
-data.mongodb.core.query.Criteria;%0Aimport org.springframework.data.mongodb.core.query.Query;%0Aimport org.springframework.stereotype.Component;%0A%0Aimport org.slc.sli.api.config.AssociationDefinition;%0Aimport org.slc.sli.api.config.EntityDefinitionStore;
+stereotype.Component;%0A
 %0Aimp
@@ -818,78 +818,8 @@
 ry;%0A
-    @Autowired%0A    private EntityDefinitionStore definitionStore;%0A    
 %0A   
@@ -1123,200 +1123,72 @@
 s = 
-new ArrayList%3CString%3E(Arrays.asList(principal.getEntityId()));%0A%0A        AssociationDefinition teacherSectionDef = (AssociationDefinition) definitionStore%0A                .lookupByResourceName(
+helper.findAccessible(principal, Arrays.asList(%0A                
 Reso
@@ -1225,18 +1225,17 @@
 CIATIONS
-);
+,
 %0A       
@@ -1239,128 +1239,16 @@
     
-AssociationDefinition sectionStudentDef = (AssociationDefinition) definitionStore%0A                .lookupByResourceName(
+        
 Reso
@@ -1285,19 +1285,17 @@
 CIATIONS
-);%0A
+,
 %0A       
@@ -1299,320 +1299,62 @@
     
-ids = findIdsFromAssociation(ids, EntityNames.TEACHER, EntityNames.SECTION, teacherSectionDef);%0A        ids = findIdsFromAssociation(ids, EntityNames.SECTION, EntityNames.STUDENT, sectionStudentDef);%0A        ids = findIdsFromAssociation(ids, EntityNames.STUDENT, EntityNames.SECTION, sectionStudentDef);%0A        
+        ResourceNames.STUDENT_SECTION_ASSOCIATIONS));%0A
 %0A   
@@ -1432,24 +1432,16 @@
 r = %22%22;%0A
-        
 %0A       
@@ -1529,28 +1529,16 @@
 r = %22,%22;
-            
 %0A       
@@ -1565,32 +1565,24 @@
 ;%0A        %7D%0A
-        
 %0A        Ent
@@ -1718,24 +1718,16 @@
 ing());%0A
-        
 %0A       
@@ -1874,32 +1874,24 @@
 %3CString%3E();%0A
-        
 %0A        for
@@ -2035,1248 +2035,6 @@
    %0A
-    private List%3CString%3E findIdsFromAssociation(List%3CString%3E ids, String sourceType, String targetType, AssociationDefinition definition) %7B%0A        List%3CString%3E associationIds = new ArrayList%3CString%3E();%0A        %0A        List%3CString%3E keys = getAssocKeys(sourceType, definition);%0A        String sourceKey = keys.get(0);%0A        String targetKey = keys.get(1);%0A        %0A        Iterable%3CEntity%3E entities = this.repository.findByQuery(definition.getStoredCollectionName(), new Query(Criteria.where(%22body.%22 + sourceKey).in(ids)), 0, 9999);%0A        %0A        for (Entity e : entities) %7B%0A            associationIds.add((String) e.getBody().get(targetKey));%0A        %7D%0A        %0A        return associationIds;%0A    %7D%0A    %0A    private List%3CString%3E getAssocKeys(String entityType, AssociationDefinition ad) %7B%0A        %0A        if (ad.getSourceEntity().getType().equals(entityType)) %7B%0A            return Arrays.asList(ad.getSourceKey(), ad.getTargetKey());%0A        %7D else if (ad.getTargetEntity().getType().equals(entityType)) %7B%0A            return Arrays.asList(ad.getTargetKey(), ad.getSourceKey());%0A        %7D else %7B%0A            throw new IllegalArgumentException(%22Entity is not a member of association %22 + entityType + %22 %22 + ad.getType());%0A        %7D%0A    %7D%0A
 %7D%0A

Refactor TeacherSessionResolver to use helper methods in AssociativeContextResolver.