package org.ei.drishti.service.scheduling;
import org.ei.drishti.common.AllConstants;
import org.ei.drishti.common.util.DateUtil;
import org.ei.drishti.service.ActionService;
import org.joda.time.LocalDate;
import org.joda.time.Weeks;
import org.motechproject.model.Time;
import org.motechproject.scheduletracking.api.service.EnrollmentRecord;
import org.motechproject.scheduletracking.api.service.EnrollmentsQuery;
import org.motechproject.scheduletracking.api.service.ScheduleTrackingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import static java.text.MessageFormat.format;
import static java.util.Arrays.asList;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.ei.drishti.common.util.DateUtil.today;
import static org.ei.drishti.common.util.IntegerUtil.tryParse;
import static org.ei.drishti.scheduler.DrishtiScheduleConstants.MotherScheduleConstants.*;
import static org.joda.time.LocalDate.parse;
import static org.joda.time.LocalTime.now;
import static org.motechproject.scheduletracking.api.domain.EnrollmentStatus.ACTIVE;
@Service
public class ANCSchedulesService {
public static final int NUMBER_OF_WEEKS_BEFORE_HB_TEST_2_BECOMES_DUE = 28;
private static Logger logger = LoggerFactory.getLogger(ANCSchedulesService.class.toString());
private final ScheduleTrackingService trackingService;
private static final String[] NON_ANC_SCHEDULES = {SCHEDULE_EDD, SCHEDULE_LAB, SCHEDULE_TT_1, SCHEDULE_IFA_1,
SCHEDULE_HB_TEST_1, SCHEDULE_DELIVERY_PLAN};
private ActionService actionService;
private final ScheduleService scheduleService;
@Autowired
public ANCSchedulesService(ScheduleTrackingService trackingService, ActionService actionService, ScheduleService scheduleService) {
this.trackingService = trackingService;
this.actionService = actionService;
this.scheduleService = scheduleService;
}
public void enrollMother(String caseId, LocalDate referenceDateForSchedule) {
for (String schedule : NON_ANC_SCHEDULES) {
scheduleService.enroll(caseId, schedule, referenceDateForSchedule.toString());
}
enrollIntoCorrectMilestoneOfANCCare(caseId, referenceDateForSchedule);
}
public void ancVisitHasHappened(String entityId, String anmId, int visitNumberToFulfill, String visitDate) {
fastForwardSchedule(entityId, anmId, SCHEDULE_ANC, SCHEDULE_ANC_MILESTONE_PREFIX, visitNumberToFulfill, parse(visitDate));
}
public void deliveryHasBeenPlanned(String entityId, String anmId, String deliveryPlanDate) {
fulfillMilestoneIfPossible(entityId, anmId, SCHEDULE_DELIVERY_PLAN, SCHEDULE_DELIVERY_PLAN, parse(deliveryPlanDate));
}
public void ttVisitHasHappened(String entityId, String anmId, String ttDose, String ttDate) {
if (AllConstants.ANCFormFields.TT_BOOSTER_DOSE_VALUE.equals(ttDose)) {
unEnrollFromSchedule(entityId, anmId, SCHEDULE_TT_1);
} else if (AllConstants.ANCFormFields.TT1_DOSE_VALUE.equals(ttDose)) {
fulfillMilestoneIfPossible(entityId, anmId, SCHEDULE_TT_1, SCHEDULE_TT_1, parse(ttDate));
scheduleService.enroll(entityId, SCHEDULE_TT_2, ttDate);
} else if (AllConstants.ANCFormFields.TT2_DOSE_VALUE.equals(ttDose)) {
fulfillMilestoneIfPossible(entityId, anmId, SCHEDULE_TT_2, SCHEDULE_TT_2, parse(ttDate));
}
}
public void ifaTabletsGiven(String entityId, String anmId, String numberOfIFATabletsGiven, String ifaGivenDate) {
if (tryParse(numberOfIFATabletsGiven, 0) <= 0) {
logger.info("Number of IFA tablets given is zero so not updating schedules for entity: " + entityId);
return;
}
if (fulfillMilestoneIfPossible(entityId, anmId, SCHEDULE_IFA_1, SCHEDULE_IFA_1, parse(ifaGivenDate))) {
logger.info("Enrolling ANC to IFA 2 schedule. Entity id: " + entityId);
scheduleService.enroll(entityId, SCHEDULE_IFA_2, ifaGivenDate);
return;
}
if (fulfillMilestoneIfPossible(entityId, anmId, SCHEDULE_IFA_2, SCHEDULE_IFA_2, parse(ifaGivenDate))) {
logger.info("Enrolling ANC to IFA 3 schedule. Entity id: " + entityId);
scheduleService.enroll(entityId, SCHEDULE_IFA_3, ifaGivenDate);
return;
}
fulfillMilestoneIfPossible(entityId, anmId, SCHEDULE_IFA_3, SCHEDULE_IFA_3, parse(ifaGivenDate));
}
public void hbTestDone(String entityId, String anmId, String date, String anaemicStatus, LocalDate lmp) {
if (fulfillMilestoneIfPossible(entityId, anmId, SCHEDULE_HB_TEST_1, SCHEDULE_HB_TEST_1, parse(date))) {
if (isNotBlank(anaemicStatus)) {
logger.info(format("ANC is anaemic so enrolling her to Hb Followup Test schedule: Entity id:{0}, Anaemic status: {1}", entityId, anaemicStatus));
scheduleService.enroll(entityId, SCHEDULE_HB_FOLLOWUP_TEST, date);
} else {
enrollANCToHbTest2Schedule(entityId, lmp);
}
} else if (fulfillMilestoneIfPossible(entityId, anmId, SCHEDULE_HB_FOLLOWUP_TEST, SCHEDULE_HB_FOLLOWUP_TEST, parse(date))) {
if (parse(date).isAfter(lmp.plusWeeks(NUMBER_OF_WEEKS_BEFORE_HB_TEST_2_BECOMES_DUE))) {
fulfillMilestoneIfPossible(entityId, anmId, SCHEDULE_HB_TEST_2, SCHEDULE_HB_TEST_2, parse(date));
} else {
enrollANCToHbTest2Schedule(entityId, lmp);
}
} else {
fulfillMilestoneIfPossible(entityId, anmId, SCHEDULE_HB_TEST_2, SCHEDULE_HB_TEST_2, parse(date));
}
}
private void enrollANCToHbTest2Schedule(String entityId, LocalDate lmp) {
logger.info(format("Enrolling ANC to Hb Test 2 schedule: {0} Entity id:{1}", SCHEDULE_HB_TEST_2, entityId));
scheduleService.enroll(entityId, SCHEDULE_HB_TEST_2, lmp.toString());
}
public void forceFulfillMilestone(String externalId, String scheduleName) {
if (isNotEnrolled(externalId, scheduleName)) {
logger.info(format("Tried to fulfill a milestone of {0} for entity id: {1}. But {0} not enrolled.", scheduleName, externalId));
return;
}
trackingService.fulfillCurrentMilestone(externalId, scheduleName, today(), new Time(now()));
logger.info(format("Fulfilled milestone of {0} for entity id: {1}", scheduleName, externalId));
}
public void unEnrollFromAllSchedules(String entityId) {
List<EnrollmentRecord> openEnrollments = trackingService.search(new EnrollmentsQuery().havingExternalId(entityId).havingState(ACTIVE));
for (EnrollmentRecord enrollment : openEnrollments) {
logger.info(format("Un-enrolling ANC with Entity id:{0} from schedule: {1} as Delivery happened.", entityId, enrollment.getScheduleName()));
trackingService.unenroll(entityId, asList(enrollment.getScheduleName()));
}
actionService.markAllAlertsAsInactive(entityId);
}
private void unEnrollFromSchedule(String entityId, String anmId, String scheduleName) {
logger.info(format("Un-enrolling ANC with Entity id:{0} from schedule: {1}", entityId, scheduleName));
trackingService.unenroll(entityId, asList(scheduleName));
actionService.markAlertAsInactive(anmId, entityId, scheduleName);
}
private void enrollIntoCorrectMilestoneOfANCCare(String entityId, LocalDate referenceDateForSchedule) {
String milestone;
if (DateUtil.isDateWithinGivenPeriodBeforeToday(referenceDateForSchedule, Weeks.weeks(14).toPeriod().minusDays(1))) {
milestone = SCHEDULE_ANC_1;
} else if (DateUtil.isDateWithinGivenPeriodBeforeToday(referenceDateForSchedule, Weeks.weeks(28).toPeriod().minusDays(1))) {
milestone = SCHEDULE_ANC_2;
} else if (DateUtil.isDateWithinGivenPeriodBeforeToday(referenceDateForSchedule, Weeks.weeks(36).toPeriod().minusDays(1))) {
milestone = SCHEDULE_ANC_3;
} else {
milestone = SCHEDULE_ANC_4;
}
logger.info(format("Enrolling ANC with Entity id:{0} to ANC schedule, milestone: {1}.", entityId, milestone));
scheduleService.enroll(entityId, SCHEDULE_ANC, milestone, referenceDateForSchedule.toString());
}
private void fastForwardSchedule(String entityId, String anmId, String scheduleName, String milestonePrefix, int visitNumberToFulfill, LocalDate visitDate) {
int currentMilestoneNumber = currentMilestoneNumber(entityId, scheduleName, milestonePrefix);
for (int i = currentMilestoneNumber; i <= visitNumberToFulfill; i++) {
fulfillMilestoneIfPossible(entityId, anmId, scheduleName, milestonePrefix + " " + i, visitDate);
}
}
private boolean fulfillMilestoneIfPossible(String entityId, String anmId, String scheduleName, String milestone, LocalDate fulfillmentDate) {
if (isNotEnrolled(entityId, scheduleName)) {
logger.warn(format("Tried to fulfill milestone {0} of {1} for entity id: {2}", milestone, scheduleName, entityId));
return false;
}
logger.warn(format("Fulfilling milestone {0} of {1} for entity id: {2}", milestone, scheduleName, entityId));
trackingService.fulfillCurrentMilestone(entityId, scheduleName, fulfillmentDate, new Time(now()));
actionService.markAlertAsClosed(entityId, anmId, milestone, fulfillmentDate.toString());
return true;
}
private int currentMilestoneNumber(String caseId, String scheduleName, String milestonePrefix) {
if (isNotEnrolled(caseId, scheduleName)) {
return Integer.MAX_VALUE;
}
EnrollmentRecord record = trackingService.getEnrollment(caseId, scheduleName);
return Integer.valueOf(record.getCurrentMilestoneName().replace(milestonePrefix + " ", ""));
}
private boolean isNotEnrolled(String caseId, String scheduleName) {
return trackingService.getEnrollment(caseId, scheduleName) == null;
}
}