package org.sagebionetworks.bridge.services.backfill;

import java.util.List;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.sagebionetworks.bridge.dao.HealthCodeDao;
import org.sagebionetworks.bridge.json.BridgeObjectMapper;
import org.sagebionetworks.bridge.models.BackfillRecord;
import org.sagebionetworks.bridge.models.BackfillTask;
import org.sagebionetworks.bridge.models.HealthId;
import org.sagebionetworks.bridge.models.studies.Study;
import org.sagebionetworks.bridge.services.AccountEncryptionService;
import org.sagebionetworks.bridge.services.StudyService;
import org.sagebionetworks.bridge.stormpath.StormpathFactory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.stormpath.sdk.account.Account;
import com.stormpath.sdk.application.Application;
import com.stormpath.sdk.client.Client;

/**
 * Backfills study IDs to the health code table.
 */
public class StudyIdBackfill extends AsyncBackfillTemplate  {

    private static final ObjectMapper MAPPER = BridgeObjectMapper.get();

    private StudyService studyService;
    public void setStudyService(StudyService studyService) {
        this.studyService = studyService;
    }

    private Client stormpathClient;
    public void setStormpathClient(Client client) {
        this.stormpathClient = client;
    }

    private AccountEncryptionService accountEncryptionService;
    public void setAccountEncryptionService(AccountEncryptionService accountEncryptionService) {
        this.accountEncryptionService = accountEncryptionService;
    }

    private HealthCodeDao healthCodeDao;
    public void setHealthCodeDao(HealthCodeDao healthCodeDao) {
        this.healthCodeDao = healthCodeDao;
    }

    @Override
    int getLockExpireInSeconds() {
        return 30 * 60;
    }

    @Override
    void doBackfill(final BackfillTask task, final BackfillCallback callback) {
        List<Study> studies = studyService.getStudies();
        for (final Study study : studies) {
            Application application = StormpathFactory.getStormpathApplication(stormpathClient);
            StormpathAccountIterator iterator = new StormpathAccountIterator(application);
            while (iterator.hasNext()) {
                List<Account> accountList = iterator.next();
                for (final Account account : accountList) {
                    HealthId healthId = accountEncryptionService.getHealthCode(study, account);
                    try {
                        healthCodeDao.setStudyId(healthId.getCode(), study.getIdentifier());
                        callback.newRecords(new BackfillRecord() {
                            @Override
                            public String getTaskId() {
                                return task.getId();
                            }
                            @Override
                            public long getTimestamp() {
                                return DateTime.now(DateTimeZone.UTC).getMillis();
                            }
                            @Override
                            public String getRecord() {
                                ObjectNode node = MAPPER.createObjectNode();
                                node.put("studyIdentifier", study.getIdentifier());
                                node.put("account", account.getEmail());
                                node.put("operation", "Study ID backfilled");
                                try {
                                    return MAPPER.writeValueAsString(node);
                                } catch (JsonProcessingException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                        });
                    } catch (final RuntimeException e) {
                        callback.newRecords(new BackfillRecord() {
                            @Override
                            public String getTaskId() {
                                return task.getId();
                            }
                            @Override
                            public long getTimestamp() {
                                return DateTime.now(DateTimeZone.UTC).getMillis();
                            }
                            @Override
                            public String getRecord() {
                                ObjectNode node = MAPPER.createObjectNode();
                                node.put("studyIdentifier", study.getIdentifier());
                                node.put("account", account.getEmail());
                                node.put("operation", e.getMessage());
                                try {
                                    return MAPPER.writeValueAsString(node);
                                } catch (JsonProcessingException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                        });
                    }
                }
            }
        }
    }
}

@@ -2001,24 +2001,30 @@
 ) %7B%0A        
+final 
 List%3CStudy%3E 
@@ -2064,56 +2064,8 @@
 ();%0A
-        for (final Study study : studies) %7B%0A    
     
@@ -2161,20 +2161,16 @@
         
-    
 Stormpat
@@ -2248,20 +2248,16 @@
         
-   
- 
 while (i
@@ -2273,28 +2273,24 @@
 asNext()) %7B%0A
-    
             
@@ -2346,20 +2346,16 @@
         
-   
- 
 for (fin
@@ -2386,24 +2386,76 @@
 ountList) %7B%0A
+                for (final Study study : studies) %7B%0A
             

Move the stormpath account iterator to be the outer loop