package uk.ac.ox.zoo.seeg.abraid.mp.dataacquisition.model;

import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils;
import org.junit.Before;
import org.junit.Test;
import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.DiseaseGroup;
import uk.ac.ox.zoo.seeg.abraid.mp.common.service.core.DiseaseService;
import uk.ac.ox.zoo.seeg.abraid.mp.common.service.workflow.ModelRunWorkflowService;

import static org.mockito.Mockito.*;

/**
 * Tests the ModelRunManager.
 * Copyright (c) 2014 University of Oxford
 */
public class ModelRunManagerTest {
    private DiseaseService diseaseService;
    private ModelRunManager modelRunManager;
    private ModelRunWorkflowService modelRunWorkflowService;
    private DiseaseGroup diseaseGroup;

    private static final int DISEASE_GROUP_ID = 87;

    @Before
    public void setFixedTime() {
        diseaseService = mock(DiseaseService.class);
        ModelRunGatekeeper modelRunGatekeeper = new ModelRunGatekeeper(diseaseService);
        modelRunWorkflowService = mock(ModelRunWorkflowService.class);
        modelRunManager = new ModelRunManager(modelRunGatekeeper, modelRunWorkflowService);

        // The default disease group for dengue has automatic model runs set to true
        diseaseGroup = new DiseaseGroup(DISEASE_GROUP_ID);
        diseaseGroup.setAutomaticModelRuns(true);

        DateTimeUtils.setCurrentMillisFixed(1400148490000L);
    }

    @Test
    public void modelPrepShouldNotRunWhenAutomaticModelRunsNotEnabled() throws Exception {
        diseaseGroup.setAutomaticModelRuns(false);
        expectModelPrepNotToRun(null, true);
    }

    @Test
    public void modelPrepShouldRunWhenLastModelRunPrepDateIsNullAndNewOccurrencesIsOverThreshold() throws Exception {
        expectModelPrepToRun(null, true);
    }

    @Test
    public void modelPrepShouldRunWhenLastModelRunPrepDateIsNullAndNewOccurrencesIsUnderThreshold() throws Exception {
        expectModelPrepToRun(null, false);
    }

    @Test
    public void modelPrepShouldRunWhenAWeekHasElapsedAndNewOccurrencesIsOverThreshold() throws Exception {
        expectModelPrepToRun(true, true);
    }

    @Test
    public void modelPrepShouldRunWhenAWeekHasElapsedAndNewOccurrencesIsUnderThreshold() throws Exception {
        expectModelPrepToRun(true, false);
    }

    @Test
    public void modelPrepShouldRunWhenAWeekHasNotElapsedAndNewOccurrencesIsOverThreshold() throws Exception {
        expectModelPrepToRun(false, true);
    }

    @Test
    public void modelPrepShouldNotRunWhenAWeekHasNotPassedAndNewOccurrencesIsUnderThreshold() throws Exception {
        expectModelPrepNotToRun(false, false);
    }

    @Test
    public void modelPrepShouldNotRunWhenAWeekHasNotPassedAndThresholdIsNull() throws Exception {
        expectModelPrepNotToRun(false, null);
    }

    @Test
    public void modelPrepShouldNotRunWhenAWeekHasPassedAndThresholdIsNull() throws Exception {
        expectModelPrepNotToRun(true, null);
    }

    @Test
    public void modelPrepShouldNotRunWhenLastModelRunPrepDateIsNullAndThresholdIsNull() throws Exception {
        expectModelPrepNotToRun(null, null);
    }

    private void expectModelPrepToRun(Boolean weekHasElapsed, Boolean newOccurrenceCountOverThreshold) {
        // Arrange and Act
        DiseaseGroup diseaseGroup = mockGetDiseaseGroupById();
        arrangeAndAct(diseaseGroup, weekHasElapsed, newOccurrenceCountOverThreshold);

        // Assert
        verify(modelRunWorkflowService, times(1)).prepareForAndRequestModelRun(eq(DISEASE_GROUP_ID));
    }

    private void expectModelPrepNotToRun(Boolean weekHasElapsed, Boolean newOccurrenceCountOverThreshold) {
        // Arrange and Act
        DiseaseGroup diseaseGroup = mockGetDiseaseGroupById();
        arrangeAndAct(diseaseGroup, weekHasElapsed, newOccurrenceCountOverThreshold);

        // Assert
        verify(modelRunWorkflowService, never()).prepareForAndRequestModelRun(eq(DISEASE_GROUP_ID));
    }

    private void arrangeAndAct(DiseaseGroup diseaseGroup, Boolean weekHasElapsed,
                                   Boolean newOccurrenceCountOverThreshold) {
        // Arrange
        int newOccurrencesCount = 100;
        setLastModelRunPrepDate(diseaseGroup, weekHasElapsed);
        mockGetNewOccurrencesCountByDiseaseGroup(newOccurrencesCount);
        setModelRunMinNewOccurrences(diseaseGroup, newOccurrencesCount, newOccurrenceCountOverThreshold);

        // Act
        modelRunManager.prepareForAndRequestModelRun(diseaseGroup.getId());
    }

    private void setLastModelRunPrepDate(DiseaseGroup diseaseGroup, Boolean weekHasElapsed) {
        DateTime lastModelRunPrepDate = null;
        if (weekHasElapsed != null) {
            int days = weekHasElapsed ? 7 : 1;
            lastModelRunPrepDate = DateTime.now().minusDays(days);
        }
        diseaseGroup.setLastModelRunPrepDate(lastModelRunPrepDate);
    }

    private void setModelRunMinNewOccurrences(DiseaseGroup diseaseGroup, int newOccurrencesCount,
                                              Boolean newOccurrenceCountOverThreshold) {
        Integer minNewOccurrences = null;
        if (newOccurrenceCountOverThreshold != null) {
            int thresholdAdjustment = newOccurrenceCountOverThreshold ? -1 : +1;
            minNewOccurrences = newOccurrencesCount + thresholdAdjustment;
        }
        diseaseGroup.setModelRunMinNewOccurrences(minNewOccurrences);
    }

    private DiseaseGroup mockGetDiseaseGroupById() {
        when(diseaseService.getDiseaseGroupById(DISEASE_GROUP_ID)).thenReturn(diseaseGroup);
        return diseaseGroup;
    }

    private void mockGetNewOccurrencesCountByDiseaseGroup(long newOccurrencesCount) {
        when(diseaseService.getNewOccurrencesCountByDiseaseGroup(DISEASE_GROUP_ID)).thenReturn(newOccurrencesCount);
    }
}

@@ -3267,36 +3267,8 @@
     
- DiseaseGroup diseaseGroup =
  moc
@@ -3309,38 +3309,24 @@
 rangeAndAct(
-diseaseGroup, 
 weekHasElaps
@@ -3637,36 +3637,8 @@
     
- DiseaseGroup diseaseGroup =
  moc
@@ -3683,30 +3683,16 @@
 eAndAct(
-diseaseGroup, 
 weekHasE
@@ -3895,93 +3895,31 @@
 Act(
-DiseaseGroup diseaseGroup, Boolean weekHasElapsed,%0A                                  
+Boolean weekHasElapsed,
  Boo
@@ -4048,30 +4048,16 @@
 repDate(
-diseaseGroup, 
 weekHasE
@@ -4173,30 +4173,16 @@
 rrences(
-diseaseGroup, 
 newOccur
@@ -4372,35 +4372,8 @@
 ate(
-DiseaseGroup diseaseGroup, 
 Bool
@@ -4727,105 +4727,32 @@
 ces(
-DiseaseGroup diseaseGroup, int newOccurrencesCount,%0A                                             
+int newOccurrencesCount,
  Boo
@@ -5143,28 +5143,20 @@
 private 
-DiseaseGroup
+void
  mockGet
@@ -5273,37 +5273,8 @@
 p);%0A
-        return diseaseGroup;%0A
     

Fix checkstyle violations