package com.agilex.vamf.missionhealth.datalayer;

import java.lang.Integer;
import java.util.*;

import javax.annotation.Resource;

import com.agilex.vamf.missionhealth.domain.*;
import com.agilex.vamf.missionhealth.domain.MissionHealthDetail;
import com.agilex.vamf.missionhealth.domain.MissionHealthProfile;
import com.agilex.vamf.missionhealth.domain.MissionHealthRank;
import com.agilex.vamf.missionhealth.domain.MissionHealthStanding;
import com.agilex.vamf.missionhealth.domain.comparator.MissionHealthDetailStandingComparator;
import com.agilex.vamf.missionhealth.domain.enumeration.StandingType;
import com.agilex.vamf.missionhealth.service.MissionHealthNotificationStatus;
import com.agilex.vamf.missionhealth.service.MissionHealthWeeklyStatus;
import com.agilex.vamf.missionhealth.service.PointsRule;
import com.agilex.vamf.missionhealth.service.ThresholdValues;
import com.agilex.vamf.missionhealth.service.enumeration.NotificationType;
import com.mongodb.DBCursor;
import org.junit.Assert;
import org.junit.Test;

import com.agilex.healthcare.utility.DateHelper;
import com.agilex.vamf.missionhealth.domain.enumeration.MissionStatus;

public class MissionHealthDataLayerImplTest extends SpringBasedIntegrationRepo {

    @Resource
    MissionHealthDataLayerImpl dataLayer;

    @Test
    public void removeMissionHealthDetail(){
    	MissionHealthDetail mhd=dataLayer.getMissionHealthDetail("PATID55");
    	if(mhd!=null){
    	dataLayer.removeMissionHealthDetail(mhd);
    	Assert.assertNull(dataLayer.getMissionHealthDetail("PATID55"));
    	}
    }
    
    @Test
    public void saveMissionHealthDetail() {
    
		MissionHealthDetail missionHealthDetail = new MissionHealthDetail();
		missionHealthDetail.setPatientId("PATID55");
		missionHealthDetail.setMissionHealthProfile(generateMissionHealthProfile());
		missionHealthDetail.setMissions(generateMissions());
		missionHealthDetail.setLoginDate(new Date());
		missionHealthDetail.setPoints(20);
		missionHealthDetail.setAnnualTotalPoints(20);
		missionHealthDetail.setRank(new MissionHealthRank(1));
		missionHealthDetail.setAnnualTotalTroops(2);
		
		MissionHealthDetail savedMissionHealthDetail = dataLayer.saveMissionHealthDetail(missionHealthDetail);
    	Assert.assertNotNull(savedMissionHealthDetail);
    	Assert.assertNotNull(savedMissionHealthDetail.getId());
    }
    
    @Test
    public void getMissionHealthDetails(){
        MissionHealthDetail missionHealthDetail = new MissionHealthDetail();
        missionHealthDetail.setPatientId("PATID55");
        missionHealthDetail.setMissionHealthProfile(generateMissionHealthProfile());
        missionHealthDetail.setMissions(generateMissions());
        missionHealthDetail.setLoginDate(new Date());
        missionHealthDetail.setPoints(20);
        missionHealthDetail.setAnnualTotalPoints(20);
        missionHealthDetail.setRank(new MissionHealthRank(1));
        missionHealthDetail.setAnnualTotalTroops(2);

        MissionHealthDetail savedMissionHealthDetail = dataLayer.saveMissionHealthDetail(missionHealthDetail);

    	missionHealthDetail = dataLayer.getMissionHealthDetail("PATID55");
    	Assert.assertNotNull(missionHealthDetail);
    	Assert.assertNotNull(missionHealthDetail.getId());
    	Assert.assertEquals("PATID55", missionHealthDetail.getPatientId());
    }

    @Test
    public void updateMissionHealthDetail(){
        MissionHealthDetail missionHealthDetail = new MissionHealthDetail();
        missionHealthDetail.setPatientId("PATID55");
        missionHealthDetail.setMissionHealthProfile(generateMissionHealthProfile());
        missionHealthDetail.setMissions(generateMissions());
        missionHealthDetail.setLoginDate(new Date());
        missionHealthDetail.setPoints(20);
        missionHealthDetail.setAnnualTotalPoints(20);
        missionHealthDetail.setRank(new MissionHealthRank(1));
        missionHealthDetail.setAnnualTotalTroops(2);

        MissionHealthDetail savedMissionHealthDetail = dataLayer.saveMissionHealthDetail(missionHealthDetail);

    	missionHealthDetail = dataLayer.getMissionHealthDetail("PATID55");
    	Assert.assertNotNull(missionHealthDetail);
    	
    	MissionHealthProfile missionHealthProfile=missionHealthDetail.getMissionHealthProfile();
    	missionHealthProfile.setReminder("Monthly");
    	
    	missionHealthDetail.setMissionHealthProfile(missionHealthProfile);
    	missionHealthDetail.setPoints(new Integer(400));
    	missionHealthDetail.setAnnualTotalPoints(new Integer(400));
    	
    	MissionHealthDetail updatedMissionHealthDetail=dataLayer.updateMissionHealthDetail(missionHealthDetail);
    	Assert.assertNotNull(updatedMissionHealthDetail.getId());
    	Assert.assertEquals("PATID55",updatedMissionHealthDetail.getPatientId());
    	Assert.assertEquals("Monthly",updatedMissionHealthDetail.getMissionHealthProfile().getReminder());
    	Assert.assertEquals(new Integer(400),updatedMissionHealthDetail.getPoints());
        Assert.assertNotNull(updatedMissionHealthDetail.getStanding());
    }

    @Test
    public void getPointsRuleForLevel(){
        Integer level = 0;
        PointsRule pointsRule = dataLayer.getPointsRuleForLevel(level);

        Assert.assertNotNull(pointsRule);
        Assert.assertNotNull(pointsRule.getMissionThresholds());
        Assert.assertNotNull(pointsRule.getPointThresholds());
        validateThresholdValues(pointsRule.getMissionThresholds());
        validateThresholdValues(pointsRule.getPointThresholds());
    }

    @Test
    public void getPointsRuleMapping(){
        Map<Integer, PointsRule> mapping = dataLayer.getPointsRuleMapping();
        Assert.assertNotNull(mapping);
        for(Map.Entry<Integer, PointsRule> entry : mapping.entrySet()){
            Integer level = entry.getKey();
            PointsRule rule = entry.getValue();
            Assert.assertNotNull(rule);
            Assert.assertEquals(level, rule.getId());
            Assert.assertNotNull(rule.getMissionThresholds());
            Assert.assertNotNull(rule.getPointThresholds());
            validateThresholdValues(rule.getMissionThresholds());
            validateThresholdValues(rule.getPointThresholds());
        }
    }

    private void validateThresholdValues(ThresholdValues thresholdValues){
        Assert.assertNotNull(thresholdValues.getLower());
        Assert.assertNotNull(thresholdValues.getUpper());
        Assert.assertNotNull(thresholdValues.getInterval());
        Assert.assertTrue(thresholdValues.getLower()<=thresholdValues.getUpper());
        Assert.assertTrue(thresholdValues.getLower()%thresholdValues.getInterval()==0);
        Assert.assertTrue(thresholdValues.getUpper()%thresholdValues.getInterval()==0);
    }

    @Test
    public void getMissionHealthRank(){
        String service = "Army";
        Integer level = 0;
        MissionHealthRank missionHealthRank = dataLayer.getMissionHealthRank(service, level);

        Assert.assertNotNull(missionHealthRank);
        Assert.assertTrue(level.equals(missionHealthRank.getLevel()));
        Assert.assertNotNull(missionHealthRank.getAbbr());
        Assert.assertNotNull(missionHealthRank.getTitle());

        Integer invalidLevel = 99;
        missionHealthRank = dataLayer.getMissionHealthRank(service, invalidLevel);
        Assert.assertNotNull(missionHealthRank);
        Assert.assertTrue(invalidLevel.equals(missionHealthRank.getLevel()));
        Assert.assertNull(missionHealthRank.getAbbr());
        Assert.assertNull(missionHealthRank.getTitle());
    }

    @Test
    public void getMissionHealthRankMapping(){
        Map<Integer, Map<String, MissionHealthRank>> mapping = dataLayer.getMissionHealthRankMapping();
        Assert.assertNotNull(mapping);
        for(Map.Entry<Integer, Map<String, MissionHealthRank>> entry : mapping.entrySet()){
            Integer level = entry.getKey();
            Map<String, MissionHealthRank> serviceRankMap = entry.getValue();
            Assert.assertNotNull(serviceRankMap);
            for(Map.Entry<String, MissionHealthRank> entry1 : serviceRankMap.entrySet()){
                String service = entry1.getKey();
                MissionHealthRank rank = entry1.getValue();
                Assert.assertNotNull(rank);
                Assert.assertEquals(level, rank.getLevel());
                Assert.assertEquals(service, rank.getService());
                Assert.assertNotNull(rank.getAbbr());
                Assert.assertNotNull(rank.getTitle());
            }
        }
    }
    
    private MissionHealthProfile generateMissionHealthProfile() {
    	MissionHealthProfile missionHealthProfile = new MissionHealthProfile();
		missionHealthProfile.setPatientId("PATID55");
		missionHealthProfile.setName("patient56");
		missionHealthProfile.setDateOfBirth(DateHelper.parseDate("08/08/1984"));
		missionHealthProfile.setGender("M");
		missionHealthProfile.setReminder("Weekly");
		missionHealthProfile.setEmail("patient56@agilexhealth.com");
		missionHealthProfile.setService("Navy");
		
		return missionHealthProfile;

    }
    
    private List<Mission> generateMissions() {
    	List<Mission> missions = new ArrayList<Mission>();
    	Mission mission = new Mission();
    	mission.setHomeService(Service.ARMY.getService());
    	mission.setCompetingService(Service.NAVY.getService());
    	mission.setName("TestMission1");
    	mission.setStartDate(DateHelper.parseDate("10/08/2013"));
    	mission.setStatus(MissionStatus.ACTIVE);
    	mission.setTotalPoints(40);
    	mission.setThresholdPercentage("60%");
    	missions.add(mission);
    	
    	mission = new Mission();
    	mission.setHomeService(Service.NAVY.getService());
    	mission.setCompetingService(Service.ARMY.getService());
    	mission.setName("TestMission2");
    	mission.setStartDate(DateHelper.parseDate("10/08/2013"));
    	mission.setStatus(MissionStatus.ACTIVE);
    	mission.setTotalPoints(60);
    	mission.setThresholdPercentage("50%");
    	missions.add(mission);
    	
    	return missions;

    }

    @Test
    public void getAllMissionHealthDetails(){
        PatientIdentifier patientIdentifier = new PatientIdentifier("EDIPI","P11");
        MissionHealthDetail missionHealthDetail = new MissionHealthDetail();
        missionHealthDetail.setPatientIdentifier(patientIdentifier);

        MissionHealthProfile missionHealthProfile = new MissionHealthProfile();
        missionHealthProfile.setPatientIdentifier(patientIdentifier);
        missionHealthProfile.setName("zztest.p11");
        missionHealthProfile.setDateOfBirth(DateHelper.parseDate("07/02/1980"));
        missionHealthProfile.setGender("MALE");
        missionHealthProfile.setReminder("Daily");
        missionHealthProfile.setEmail("p11@agilexhealth.com");
        missionHealthProfile.setService("Army");

        missionHealthDetail.setMissionHealthProfile(missionHealthProfile);
        missionHealthDetail.setMissions(generateMissions());
        missionHealthDetail.setLoginDate(new Date());
        missionHealthDetail.setPoints(20);
        missionHealthDetail.setAnnualTotalPoints(20);
        missionHealthDetail.setRank(new MissionHealthRank(1));
        missionHealthDetail.setTroops(2);
        missionHealthDetail.setAnnualTotalTroops(2);

        MissionHealthDetail savedMissionHealthDetail = dataLayer.saveMissionHealthDetail(missionHealthDetail);
        Assert.assertNotNull(savedMissionHealthDetail);
        Assert.assertNotNull(savedMissionHealthDetail.getId());

        patientIdentifier = new PatientIdentifier("EDIPI","P12");
        missionHealthDetail = new MissionHealthDetail();
        missionHealthDetail.setPatientIdentifier(patientIdentifier);

        missionHealthProfile = new MissionHealthProfile();
        missionHealthProfile.setPatientIdentifier(patientIdentifier);
        missionHealthProfile.setName("zztest.p12");
        missionHealthProfile.setDateOfBirth(DateHelper.parseDate("07/05/1987"));
        missionHealthProfile.setGender("FEMALE");
        missionHealthProfile.setReminder("Daily");
        missionHealthProfile.setEmail("p12@agilexhealth.com");
        missionHealthProfile.setService("Navy");

        missionHealthDetail.setMissionHealthProfile(missionHealthProfile);
        missionHealthDetail.setMissions(generateMissions());
        missionHealthDetail.setLoginDate(new Date());
        missionHealthDetail.setPoints(200);
        missionHealthDetail.setAnnualTotalPoints(200);
        missionHealthDetail.setRank(new MissionHealthRank(2));
        missionHealthDetail.setTroops(2);
        missionHealthDetail.setAnnualTotalTroops(2);

        savedMissionHealthDetail = dataLayer.saveMissionHealthDetail(missionHealthDetail);
        Assert.assertNotNull(savedMissionHealthDetail);
        Assert.assertNotNull(savedMissionHealthDetail.getId());

        MissionHealthDetails missionHealthDetails = dataLayer.getAllMissionHealthDetails();
        Assert.assertTrue(missionHealthDetails.size()>=2);
        for(MissionHealthDetail detail : missionHealthDetails){
            Assert.assertNotNull(detail.getId());
            if(detail.getPatientId()!=null && (detail.getPatientId().equals("P11")||detail.getPatientId().equals("P12")||detail.getPatientId().equals("PATID55"))){
            dataLayer.removeMissionHealthDetail(detail);
            }
        }
    }

    //@Test
    public void saveAndFetchMissionHealthWeeklyStatus() {
        MissionHealthWeeklyStatus status = new MissionHealthWeeklyStatus();
        dataLayer.saveMissionHealthWeeklyStatus(status);

        status = dataLayer.fetchMostRecentMissionHealthWeeklyStatus();
        Assert.assertNotNull(status);
        Assert.assertFalse(status.isComplete());
        Assert.assertNotNull(status.getId());
    }

    @Test
    public void getTotalMissionsAcrossAllUsers() {
        Integer totalMissions = dataLayer.getTotalMissionsAcrossAllUsers();
        Assert.assertNotNull(totalMissions);
    }

    @Test
    public void missionHealthStandingTest(){
        String[] services = {"Army","Marines"};
        Integer[] levels = {0,5,10,15};
        for(String service: services){
            for(Integer level:levels){
                standingsTest(service, level);
            }
        }
    }

    @Test
    public void weeklyStandingTest(){
        MissionHealthDetail mhd = getRandomMissionHealthDetail("WST_patientId", "wst"+5+"Army", null, 5, "Army");
        mhd = dataLayer.saveMissionHealthDetail(mhd);

        dataLayer.clearWeeklyStanding();
        MissionHealthTranslator translator = new MissionHealthTranslator();
        DBCursor weeklyJobCursor = dataLayer.getWeeklyJobCursor();
        while(weeklyJobCursor.hasNext()){
            MissionHealthDetail detail = translator.translateToMissionHealthDetail(weeklyJobCursor.next());
            if(null!=detail.getMissionHealthProfile() && detail.hasRecentMissions())
                dataLayer.updateWeeklyStanding(detail);
        }

        validateWeeklyStandings(dataLayer.getMissionHealthStandings(mhd, StandingType.ALL));
        validateWeeklyStandings(dataLayer.getMissionHealthStandings(mhd, StandingType.SERVICE));
        validateWeeklyStandings(dataLayer.getMissionHealthStandings(mhd, StandingType.LEVEL));
    }


    private void standingsTest(String service, Integer level){
        String newPatId = "standingTest"+service+level+"New"; // only created if none exist
        String topPatId = "standingTest"+service+level+"Top"; // always created
        String middlePatId = "standingTest"+service+level+"Middle"; // always created
        String bottomPatId = "standingTest"+service+level+"Bottom"; // only created if there are no existing patients with 0 points

        String[] ids = {newPatId, topPatId, middlePatId, bottomPatId};
        /* delete any of these records if they exist */
        for(String id : ids){
            MissionHealthDetail rm = dataLayer.getMissionHealthDetail(id);
            if(null!=rm)
                dataLayer.removeMissionHealthDetail(rm);
        }

        /* pull entire mhd collection, sort */
        List<MissionHealthDetail> allDetails = dataLayer.getAllMissionHealthDetails();
        int beginningSize = allDetails.size();
        Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
        MissionHealthDetail top;
        MissionHealthDetail bottom;

        /* if no records exist, validate case when zero records exist */
        if(allDetails.size()==0){
            MissionHealthDetail mhd = getRandomMissionHealthDetail(newPatId, "new"+level+service, null, level, service);
            dataLayer.saveMissionHealthDetail(mhd);

            /* pull entire mhd collection, sort */
            allDetails = dataLayer.getAllMissionHealthDetails();
            Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
            top = allDetails.get(0);
            bottom = allDetails.get(allDetails.size()-1);

            validateStandings(allDetails);
            Assert.assertTrue("There is only one details record", allDetails.size()==1);
            Assert.assertEquals("The top record's standing = 1", new Integer(1), top.getStanding());
            Assert.assertEquals("The bottom record's standing = 1", new Integer(1), bottom.getStanding());
        }else{
            top = allDetails.get(0);
            bottom = allDetails.get(allDetails.size()-1);
        }

        /* insert new mhd with standing at top */
        MissionHealthDetail mhd = getRandomMissionHealthDetail(topPatId, "top"+level+service, top.getPoints()+30, level, service);
        mhd = dataLayer.saveMissionHealthDetail(mhd);
        allDetails = dataLayer.getAllMissionHealthDetails();
        Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
        top = allDetails.get(0);
        bottom = allDetails.get(allDetails.size()-1);

        Assert.assertEquals("The top record's standing = 1", new Integer(1), top.getStanding());
        Assert.assertEquals("The record we just inserted is the top record", top.getPatientId(), mhd.getPatientId());
        Assert.assertEquals("The top record's points = "+mhd.getPoints(), mhd.getPoints(), top.getPoints());

        validateStandings(allDetails);

        /* insert new mhd with standing in the middle */
        mhd = dataLayer.saveMissionHealthDetail(getRandomMissionHealthDetail(middlePatId, "mid"+level+service, top.getPoints()-10, level, service));
        allDetails = dataLayer.getAllMissionHealthDetails();
        Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
        top = allDetails.get(0);
        bottom = allDetails.get(allDetails.size()-1);

        Assert.assertEquals("The top record's standing = 1", new Integer(1), top.getStanding());
        validateStandings(allDetails);

        /* insert new mhd with standing at the bottom (if we are able to insert something below the existing set) */
        if(bottom.getPoints()>1){
            mhd = dataLayer.saveMissionHealthDetail(getRandomMissionHealthDetail(bottomPatId, "btm"+level+service, bottom.getPoints()-1, level, service));

            allDetails = dataLayer.getAllMissionHealthDetails();
            Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
            top = allDetails.get(0);
            bottom = allDetails.get(allDetails.size()-1);

            Assert.assertEquals("The top record's standing = 1", new Integer(1), top.getStanding());
            Assert.assertEquals("The record we just inserted is the bottom record", bottom.getPatientId(), bottom.getPatientId());
            Assert.assertEquals("The bottom record's points = "+mhd.getPoints(), mhd.getPoints(), bottom.getPoints());
            validateStandings(allDetails);

        }

        /* update top to middle */
        top.setPoints(top.getPoints() - 20);
        mhd = dataLayer.updateMissionHealthDetail(top);
        allDetails = dataLayer.getAllMissionHealthDetails();
        Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
        top = allDetails.get(0); // new top can be anything here
        bottom = allDetails.get(allDetails.size()-1);

        Assert.assertEquals("The top record's standing = 1", new Integer(1), top.getStanding());
        Assert.assertTrue("The record we just updated is not the top record", !top.getPatientId().equals(mhd.getPatientId()));
        validateStandings(allDetails);

        /* update middle to top */
        mhd = dataLayer.getMissionHealthDetail(topPatId);
        mhd.setPoints(top.getPoints()+10);
        dataLayer.updateMissionHealthDetail(mhd);
        allDetails = dataLayer.getAllMissionHealthDetails();
        Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
        top = allDetails.get(0);
        bottom = allDetails.get(allDetails.size()-1);

        Assert.assertEquals("The top record's standing = 1", new Integer(1), top.getStanding());
        Assert.assertEquals("The record we just inserted is the top record", top.getPatientId(), mhd.getPatientId());
        Assert.assertEquals("The top record's points = " + mhd.getPoints(), mhd.getPoints(), top.getPoints());
        validateStandings(allDetails);

        /* update top to bottom */
        top.setPoints(bottom.getPoints() - 1);
        mhd = dataLayer.updateMissionHealthDetail(top);
        allDetails = dataLayer.getAllMissionHealthDetails();
        Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
        top = allDetails.get(0);
        bottom = allDetails.get(allDetails.size()-1);
        Assert.assertEquals("The top record's standing = 1", new Integer(1), top.getStanding());
        Assert.assertTrue("The record we just updated is not the top record", !top.getPatientId().equals(mhd.getPatientId()));
        Assert.assertTrue("The record we just updated is the bottom record", bottom.getPatientId().equals(mhd.getPatientId()));
        validateStandings(allDetails);

        /* update bottom to top */
        mhd = dataLayer.getMissionHealthDetail(topPatId);
        mhd.setPoints(top.getPoints() + 10);
        dataLayer.updateMissionHealthDetail(mhd);
        allDetails = dataLayer.getAllMissionHealthDetails();
        Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
        top = allDetails.get(0);

        Assert.assertEquals("The top record's standing = 1", new Integer(1), top.getStanding());
        Assert.assertEquals("The record we just inserted is the top record", top.getPatientId(), mhd.getPatientId());
        Assert.assertEquals("The top record's points = " + mhd.getPoints(), mhd.getPoints(), top.getPoints());
        validateStandings(allDetails);

        /* update middle to middle (diff standing) */
        mhd = dataLayer.getMissionHealthDetail(middlePatId);
        Integer middlePoints = mhd.getPoints();
        Integer middleStanding = mhd.getStanding();
        MissionHealthDetail mhd1 = findByStanding(middleStanding+1, allDetails);
        Assert.assertNotNull(mhd1);
        mhd.setPoints(mhd1.getPoints() - 1);
        mhd = dataLayer.updateMissionHealthDetail(mhd);
        allDetails = dataLayer.getAllMissionHealthDetails();
        Collections.sort(allDetails, new MissionHealthDetailStandingComparator());

        Assert.assertTrue("Middle record's standing is now lower", mhd.getStanding() > middleStanding);
        validateStandings(allDetails);

        mhd = dataLayer.getMissionHealthDetail(middlePatId);
        mhd.setPoints(middlePoints);
        mhd = dataLayer.updateMissionHealthDetail(mhd);

        Assert.assertTrue("Middle record's standing back to original", mhd.getStanding().equals(middleStanding));
        validateStandings(allDetails);

        /* update middle to bottom */
        mhd = dataLayer.getMissionHealthDetail(middlePatId);
        mhd.setPoints(bottom.getPoints() - 1);
        mhd = dataLayer.updateMissionHealthDetail(mhd);
        allDetails = dataLayer.getAllMissionHealthDetails();
        Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
        top = allDetails.get(0);
        bottom = allDetails.get(allDetails.size()-1);

        Assert.assertTrue("The record we just updated is the bottom record", bottom.getPatientId().equals(mhd.getPatientId()));
        validateStandings(allDetails);

        /* update bottom to middle */
        bottom.setPoints(middlePoints);
        mhd = dataLayer.updateMissionHealthDetail(bottom);
        allDetails = dataLayer.getAllMissionHealthDetails();
        Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
        top = allDetails.get(0);
        bottom = allDetails.get(allDetails.size()-1);

        Assert.assertTrue("The record we just updated is not the bottom record", !bottom.getPatientId().equals(mhd.getPatientId()));
        validateStandings(allDetails);

        /* delete mhd with standing at the top */
        int prevSize = allDetails.size();
        dataLayer.removeMissionHealthDetail(top);
        allDetails = dataLayer.getAllMissionHealthDetails();
        Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
        top = allDetails.get(0);

        Assert.assertEquals("Exactly one record was removed", allDetails.size(), prevSize-1);
        Assert.assertEquals("The top record's standing = 1", new Integer(1), top.getStanding());
        Assert.assertTrue("Middle is the new top", top.getPatientId().equals(middlePatId));
        mhd = dataLayer.getMissionHealthDetail(topPatId);
        Assert.assertNull("The correct record was removed", mhd);
        validateStandings(allDetails);

        /* delete mhd with standing in the middle */
        prevSize = allDetails.size();
        mhd = dataLayer.getMissionHealthDetail(middlePatId);
        dataLayer.removeMissionHealthDetail(mhd);
        allDetails = dataLayer.getAllMissionHealthDetails();
        Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
        top = allDetails.get(0);

        Assert.assertEquals("Exactly one record was removed", allDetails.size(), prevSize - 1);
        Assert.assertEquals("The top record's standing = 1", new Integer(1), top.getStanding());
        mhd = dataLayer.getMissionHealthDetail(middlePatId);
        Assert.assertNull("The correct record was removed", mhd);
        validateStandings(allDetails);

        /* delete mhd with standing at the bottom */
        prevSize = allDetails.size();
        mhd = dataLayer.getMissionHealthDetail(bottomPatId);
        dataLayer.removeMissionHealthDetail(mhd);
        allDetails = dataLayer.getAllMissionHealthDetails();
        Collections.sort(allDetails, new MissionHealthDetailStandingComparator());
        top = allDetails.get(0);

        Assert.assertEquals("Exactly one record was removed", allDetails.size(), prevSize - 1);
        Assert.assertEquals("The top record's standing = 1", new Integer(1), top.getStanding());
        mhd = dataLayer.getMissionHealthDetail(bottomPatId);
        Assert.assertNull("The correct record was removed", mhd);
        validateStandings(allDetails);

        mhd = dataLayer.getMissionHealthDetail(newPatId);
        if(null!=mhd){
            dataLayer.removeMissionHealthDetail(mhd);
            allDetails = dataLayer.getAllMissionHealthDetails();
        }

        Assert.assertEquals("All records from this test were deleted", beginningSize, allDetails.size());
    }

    /**
     * Validates a list of mission {@link MissionHealthDetail} records sorted by standing
     *  to make sure that the standings have been correctly assigned.
     *
     * @param detailsList a list of {@link MissionHealthDetail} records
     * @return
     */
    private Integer validateStandings(List<MissionHealthDetail> detailsList){
        /* make sure that they are sorted */
        Collections.sort(detailsList, new MissionHealthDetailStandingComparator());
        Integer detailsCount = Integer.valueOf(detailsList.size());
        Integer points = null;
        Integer standing = null;
        for(MissionHealthDetail mhd : detailsList){
            Assert.assertNotNull(mhd.getStanding());
            Assert.assertNotNull(mhd.getPoints());
            Assert.assertNotNull(mhd.getTotalUsers());
            Assert.assertEquals(detailsCount, mhd.getTotalUsers());
            Assert.assertTrue(mhd.getStanding() <= mhd.getTotalUsers());
            if(null != points && null != standing){
                Assert.assertTrue(mhd.getStanding()>=standing);
                if(mhd.getStanding().equals(standing))
                    Assert.assertTrue(mhd.getPoints().equals(points));
                else
                    Assert.assertTrue(mhd.getPoints()<points);
            }
            points = mhd.getPoints();
            standing = mhd.getStanding();
        }
        return detailsCount;
    }

    private void validateWeeklyStandings(MissionHealthStandings standings){
        for(MissionHealthStanding standing: standings){
            Integer points = null;
            Integer allStanding = null;
            Map<String, Integer> serviceStandings = new HashMap<String,Integer>();
            Map<Integer, Integer> levelStandings = new HashMap<Integer,Integer>();
            for(MissionHealthStanding mhs : standings){
                Assert.assertNotNull(mhs.getService());
                Assert.assertNotNull(mhs.getTroops());
                Assert.assertNotNull(mhs.getPoints());
                Assert.assertNotNull(mhs.getType());
                Assert.assertNotNull(mhs.getStanding());
                if(mhs.getStanding()>0){
                    if(mhs.getType().equals(StandingType.SERVICE)){
                        if(serviceStandings.containsKey(mhs.getService()))
                            Assert.assertTrue(mhs.getStanding()>=serviceStandings.get(mhs.getService()));
                        serviceStandings.put(mhs.getService(), mhs.getStanding());
                    }else if(mhs.getType().equals(StandingType.LEVEL)){
                        if(serviceStandings.containsKey(mhs.getLevel()))
                            Assert.assertTrue(mhs.getStanding()>=levelStandings.get(mhs.getLevel()));
                        levelStandings.put(mhs.getLevel(), mhs.getStanding());
                    }else{
                        if(null!=allStanding)
                            Assert.assertTrue(mhs.getStanding()>=allStanding);
                        allStanding=mhs.getStanding();
                    }
                }
            }

        }
    }

    private MissionHealthDetail getRandomMissionHealthDetail(String id, String name, Integer points, Integer level, String service){
        MissionHealthDetail missionHealthDetail = new MissionHealthDetail();
        String[] services = {"Air Force","Army","Navy","Marines","Coast Guard"};
        String svc = (null==service ? services[new Random().nextInt(4)] : service);
        Integer lvl =  (null==level ? new Random().nextInt(15) : level);
        Integer pts = (null==points ? (new Random().nextInt(100)+1)*10 : points);

        missionHealthDetail.setPatientId(id.toString());

        MissionHealthProfile profile = new MissionHealthProfile();
        profile.setName(name);
        profile.setService(svc);
        missionHealthDetail.setMissionHealthProfile(profile);

        MissionHealthRank rank = new MissionHealthRank(lvl);
        missionHealthDetail.setRank(rank);
        missionHealthDetail.setPoints(pts);
        missionHealthDetail.setAnnualTotalPoints(pts);

        if(new Random().nextBoolean()) {
            missionHealthDetail.setLastMissionCreateDate(new Date());
            boolean hrm = missionHealthDetail.hasRecentMissions();
        }

        return missionHealthDetail;
    }

    private MissionHealthDetail findByStanding(Integer standing, List<MissionHealthDetail> detailsList){
        for(MissionHealthDetail missionHealthDetail : detailsList){
            if(missionHealthDetail.getStanding().equals(standing))
                return missionHealthDetail;
        }
        return null;
    }

    @Test
    public void startWeeklyTasks(){
        dataLayer.triggerWeeklyTasks();
        MissionHealthWeeklyStatus missionHealthWeeklyStatus = dataLayer.startWeeklyTasks();
        Assert.assertNotNull(missionHealthWeeklyStatus);
        Assert.assertEquals(MissionHealthWeeklyStatus.PROCESSING, missionHealthWeeklyStatus.getStatus());

        MissionHealthWeeklyStatus missionHealthWeeklyStatus1 = dataLayer.startWeeklyTasks();
        Assert.assertNull(missionHealthWeeklyStatus1);

        missionHealthWeeklyStatus.setStatus(MissionHealthWeeklyStatus.SUCCESS);
        missionHealthWeeklyStatus.setComplete(true);
        dataLayer.saveMissionHealthWeeklyStatus(missionHealthWeeklyStatus);

        missionHealthWeeklyStatus1 = dataLayer.startWeeklyTasks();
        Assert.assertNull(missionHealthWeeklyStatus1);

        dataLayer.triggerWeeklyTasks();
        dataLayer.triggerWeeklyTasks();
        dataLayer.triggerWeeklyTasks();
        missionHealthWeeklyStatus = dataLayer.startWeeklyTasks();
        Assert.assertNotNull(missionHealthWeeklyStatus);
        Assert.assertEquals(MissionHealthWeeklyStatus.PROCESSING, missionHealthWeeklyStatus.getStatus());
        missionHealthWeeklyStatus.setStatus(MissionHealthWeeklyStatus.SUCCESS);
        missionHealthWeeklyStatus.setCompleteDatetime(new Date());
        missionHealthWeeklyStatus.setComplete(true);
        dataLayer.saveMissionHealthWeeklyStatus(missionHealthWeeklyStatus);
    }

    @Test
    public void startNotificationTasks(){
        startNotificationTasks(NotificationType.EMAIL_REMINDER_TYPE_DAILY.toString());
        startNotificationTasks(NotificationType.EMAIL_REMINDER_TYPE_MID_WEEK.toString());
        startNotificationTasks(NotificationType.EMAIL_REMINDER_TYPE_END_WEEK.toString());
    }

    private void startNotificationTasks(String notificationType){
        dataLayer.triggerNotificationTask(notificationType);
        MissionHealthNotificationStatus missionHealthNotificationStatus = dataLayer.startNotificationTask(notificationType);
        Assert.assertNotNull(missionHealthNotificationStatus);
        Assert.assertEquals(MissionHealthNotificationStatus.PROCESSING, missionHealthNotificationStatus.getStatus());

        MissionHealthNotificationStatus missionHealthNotificationStatus1 = dataLayer.startNotificationTask(notificationType);
        Assert.assertNull(missionHealthNotificationStatus1);

        missionHealthNotificationStatus.setStatus(MissionHealthNotificationStatus.SUCCESS);
        missionHealthNotificationStatus.setComplete(true);
        dataLayer.saveNotificationStatus(missionHealthNotificationStatus);

        missionHealthNotificationStatus1 = dataLayer.startNotificationTask(notificationType);
        Assert.assertNull(missionHealthNotificationStatus1);

        dataLayer.triggerNotificationTask(notificationType);
        dataLayer.triggerNotificationTask(notificationType);
        dataLayer.triggerNotificationTask(notificationType);
        missionHealthNotificationStatus = dataLayer.startNotificationTask(notificationType);
        Assert.assertNotNull(missionHealthNotificationStatus);
        Assert.assertEquals(MissionHealthNotificationStatus.PROCESSING, missionHealthNotificationStatus.getStatus());
        missionHealthNotificationStatus.setStatus(MissionHealthNotificationStatus.SUCCESS);
        missionHealthNotificationStatus.setCompleteDatetime(new Date());
        missionHealthNotificationStatus.setComplete(true);
        dataLayer.saveNotificationStatus(missionHealthNotificationStatus);
    }
}
