package org.ei.drishti.service;
import org.ei.drishti.domain.EligibleCouple;
import org.ei.drishti.domain.FPProductInformation;
import org.ei.drishti.domain.register.*;
import org.ei.drishti.form.domain.FormSubmission;
import org.ei.drishti.repository.AllEligibleCouples;
import org.ei.drishti.service.formSubmission.handler.ReportFieldsDefinition;
import org.ei.drishti.service.reporting.ECReportingService;
import org.ei.drishti.service.scheduling.ECSchedulingService;
import org.ei.drishti.util.SafeMap;
import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.ei.drishti.common.AllConstants.CommonFormFields.SUBMISSION_DATE_FIELD_NAME;
import static org.ei.drishti.common.AllConstants.ECCloseFields.IS_EC_CLOSE_CONFIRMED_FIELD_NAME;
import static org.ei.drishti.common.AllConstants.ECRegistrationFields.CASTE;
import static org.ei.drishti.common.AllConstants.ECRegistrationFields.ECONOMIC_STATUS;
import static org.ei.drishti.common.AllConstants.EntityCloseFormFields.CLOSE_REASON_FIELD_NAME;
import static org.ei.drishti.common.AllConstants.EntityCloseFormFields.WRONG_ENTRY_VALUE;
import static org.ei.drishti.common.AllConstants.FamilyPlanningFormFields.*;
import static org.ei.drishti.common.AllConstants.Form.BOOLEAN_TRUE_VALUE;
import static org.ei.drishti.common.AllConstants.ReportDataParameters.QUANTITY;
import static org.ei.drishti.common.AllConstants.ReportDataParameters.SERVICE_PROVIDED_DATE;
@Service
public class ECService {
private static Logger logger = LoggerFactory.getLogger(ActionService.class.toString());
private AllEligibleCouples allEligibleCouples;
private ECReportingService reportingService;
private ECSchedulingService schedulingService;
private ReportFieldsDefinition reportFieldsDefinition;
@Autowired
public ECService(AllEligibleCouples allEligibleCouples, ECSchedulingService schedulingService, ECReportingService reportingService,
ReportFieldsDefinition reportFieldsDefinition) {
this.allEligibleCouples = allEligibleCouples;
this.reportingService = reportingService;
this.schedulingService = schedulingService;
this.reportFieldsDefinition = reportFieldsDefinition;
}
public void registerEligibleCouple(FormSubmission submission) {
EligibleCouple eligibleCouple = allEligibleCouples.findByCaseId(submission.entityId());
String fpMethod = submission.getField(CURRENT_FP_METHOD_FIELD_NAME);
initializeECWithEmptyFPDetails(eligibleCouple);
updateECWithFPDetails(eligibleCouple, submission, fpMethod);
allEligibleCouples.update(eligibleCouple.withANMIdentifier(submission.anmId()));
List<String> reportFields = reportFieldsDefinition.get(submission.formName());
reportingService.registerEC(new SafeMap(submission.getFields(reportFields)));
FPProductInformation fpProductInformation = new FPProductInformation(
submission.entityId(), submission.anmId(),
submission.getField(CURRENT_FP_METHOD_FIELD_NAME),
null, submission.getField(DMPA_INJECTION_DATE_FIELD_NAME),
submission.getField(NUMBER_OF_OCP_STRIPS_SUPPLIED_FIELD_NAME),
submission.getField(OCP_REFILL_DATE_FIELD_NAME),
submission.getField(NUMBER_OF_CONDOMS_SUPPLIED_FIELD_NAME),
submission.getField(SUBMISSION_DATE_FIELD_NAME),
submission.getField(FP_METHOD_CHANGE_DATE_FIELD_NAME), null, null, null);
schedulingService.registerEC(fpProductInformation);
}
private void initializeECWithEmptyFPDetails(EligibleCouple eligibleCouple) {
List<IUDFPDetails> iudFPDetails = new ArrayList<>();
List<CondomFPDetails> condomFPDetails = new ArrayList<>();
List<OCPFPDetails> ocpFPDetails = new ArrayList<>();
List<FemaleSterilizationFPDetails> femaleSterilizationFPDetails = new ArrayList<>();
List<MaleSterilizationFPDetails> maleSterilizationFPDetails = new ArrayList<>();
eligibleCouple
.withCondomFPDetails(condomFPDetails)
.withIUDFPDetails(iudFPDetails)
.withOCPFPDetails(ocpFPDetails)
.withFemaleSterilizationFPDetails(femaleSterilizationFPDetails)
.withMaleSterilizationFPDetails(maleSterilizationFPDetails);
}
private void updateECWithFPDetails(EligibleCouple eligibleCouple, FormSubmission submission,
String fpMethod) {
List<IUDFPDetails> iudFPDetails = eligibleCouple.iudFPDetails();
List<CondomFPDetails> condomFPDetails = eligibleCouple.condomFPDetails();
List<OCPFPDetails> ocpFPDetails = eligibleCouple.ocpFPDetails();
List<FemaleSterilizationFPDetails> femaleSterilizationFPDetails = eligibleCouple.femaleSterilizationFPDetails();
List<MaleSterilizationFPDetails> maleSterilizationFPDetails = eligibleCouple.maleSterilizationFPDetails();
String fpAcceptanceDate = submission.getField(FP_METHOD_CHANGE_DATE_FIELD_NAME);
if (IUD_FP_METHOD_VALUE.equalsIgnoreCase(fpMethod)) {
iudFPDetails.add(new IUDFPDetails(fpAcceptanceDate, submission.getField(IUD_PLACE), submission.getField(LMP_DATE), submission.getField(UPT_RESULT)));
}
if (CONDOM_FP_METHOD_VALUE.equalsIgnoreCase(fpMethod)) {
condomFPDetails.add(getCondomFPDetails(submission, fpAcceptanceDate));
}
if (OCP_FP_METHOD_VALUE.equalsIgnoreCase(fpMethod)) {
ocpFPDetails.add(getOCPFPDetails(submission, fpAcceptanceDate));
}
if (FEMALE_STERILIZATION_FP_METHOD_VALUE.equalsIgnoreCase(fpMethod)) {
femaleSterilizationFPDetails.add(
new FemaleSterilizationFPDetails(
submission.getField(FEMALE_STERILIZATION_TYPE),
fpAcceptanceDate,
null)
);
}
if (MALE_STERILIZATION_FP_METHOD_VALUE.equalsIgnoreCase(fpMethod)) {
maleSterilizationFPDetails.add(
new MaleSterilizationFPDetails(
submission.getField(MALE_STERILIZATION_TYPE),
fpAcceptanceDate,
null)
);
}
eligibleCouple
.withIUDFPDetails(iudFPDetails)
.withCondomFPDetails(condomFPDetails)
.withOCPFPDetails(ocpFPDetails)
.withFemaleSterilizationFPDetails(femaleSterilizationFPDetails)
.withMaleSterilizationFPDetails(maleSterilizationFPDetails);
}
private OCPFPDetails getOCPFPDetails(FormSubmission submission, String fpAcceptanceDate) {
Map<String, String> refill = new HashMap<>();
refill.put(SERVICE_PROVIDED_DATE, fpAcceptanceDate);
refill.put(QUANTITY, submission.getField(NUMBER_OF_OCP_STRIPS_SUPPLIED_FIELD_NAME));
List<Map<String, String>> refills = new ArrayList<>();
refills.add(refill);
return new OCPFPDetails(fpAcceptanceDate, refills, submission.getField(LMP_DATE), submission.getField(UPT_RESULT));
}
private CondomFPDetails getCondomFPDetails(FormSubmission submission, String fpAcceptanceDate) {
Map<String, String> refill = new HashMap<>();
refill.put(SERVICE_PROVIDED_DATE, fpAcceptanceDate);
refill.put(QUANTITY, submission.getField(NUMBER_OF_CONDOMS_SUPPLIED_FIELD_NAME));
List<Map<String, String>> refills = new ArrayList<>();
refills.add(refill);
return new CondomFPDetails(fpAcceptanceDate, refills);
}
public void reportFPComplications(FormSubmission submission) {
EligibleCouple couple = allEligibleCouples.findByCaseId(submission.entityId());
if (couple == null) {
logger.warn("Tried to report FP Complications of a non-existing EC, with submission: " + submission);
return;
}
FPProductInformation fpProductInformation = new FPProductInformation(
submission.entityId(), submission.anmId(),
null, null, null, null, null, null, submission.getField(SUBMISSION_DATE_FIELD_NAME), null,
submission.getField(COMPLICATION_DATE_FIELD_NAME), submission.getField(NEEDS_FOLLOWUP_FIELD_NAME), submission.getField(NEEDS_REFERRAL_FOLLOWUP_FIELD_NAME));
schedulingService.reportFPComplications(fpProductInformation);
}
public void reportFPChange(FormSubmission submission) {
EligibleCouple couple = allEligibleCouples.findByCaseId(submission.entityId());
if (couple == null) {
logger.warn("Tried to report FP Change of a non-existing EC, with submission: " + submission);
return;
}
String newFPMethod = submission.getField(NEW_FP_METHOD_FIELD_NAME);
couple.details().put(CURRENT_FP_METHOD_FIELD_NAME, newFPMethod);
updateECWithFPDetails(couple, submission, newFPMethod);
allEligibleCouples.update(couple);
List<String> reportFields = reportFieldsDefinition.get(submission.formName());
Map<String, String> reportFieldsMap = submission.getFields(reportFields);
reportFieldsMap.put(CASTE, couple.details().get(CASTE));
reportFieldsMap.put(ECONOMIC_STATUS, couple.details().get(ECONOMIC_STATUS));
reportingService.fpChange(new SafeMap(reportFieldsMap));
String fpMethodChangeDate = submission.getField(FP_METHOD_CHANGE_DATE_FIELD_NAME);
if (isBlank(fpMethodChangeDate)) {
fpMethodChangeDate = submission.getField(SUBMISSION_DATE_FIELD_NAME);
}
FPProductInformation fpProductInformation = new FPProductInformation(
submission.entityId(), submission.anmId(),
newFPMethod,
submission.getField(PREVIOUS_FP_METHOD_FIELD_NAME), null,
submission.getField(NUMBER_OF_OCP_STRIPS_SUPPLIED_FIELD_NAME),
null,
submission.getField(NUMBER_OF_CONDOMS_SUPPLIED_FIELD_NAME),
submission.getField(SUBMISSION_DATE_FIELD_NAME),
fpMethodChangeDate,
null, null, null);
schedulingService.fpChange(fpProductInformation);
}
public void renewFPProduct(FormSubmission submission) {
EligibleCouple couple = allEligibleCouples.findByCaseId(submission.entityId());
if (couple == null) {
logger.warn("Tried to report FP Renew of a non-existing EC, with submission: " + submission);
return;
}
allEligibleCouples.update(updateECWithRefills(submission, couple));
FPProductInformation fpProductInformation = new FPProductInformation(
submission.entityId(), submission.anmId(),
submission.getField(CURRENT_FP_METHOD_FIELD_NAME),
null, submission.getField(DMPA_INJECTION_DATE_FIELD_NAME),
submission.getField(NUMBER_OF_OCP_STRIPS_SUPPLIED_FIELD_NAME),
submission.getField(OCP_REFILL_DATE_FIELD_NAME),
submission.getField(NUMBER_OF_CONDOMS_SUPPLIED_FIELD_NAME),
submission.getField(SUBMISSION_DATE_FIELD_NAME), null, null, null, null);
schedulingService.renewFPProduct(fpProductInformation);
}
private EligibleCouple updateECWithRefills(FormSubmission submission, EligibleCouple couple) {
String fpMethod = submission.getField(CURRENT_FP_METHOD_FIELD_NAME);
String date = submission.getField(FP_RENEW_METHOD_VISIT_DATE);
if (OCP_FP_METHOD_VALUE.equalsIgnoreCase(fpMethod)) {
return updateOCPDetailsWithRefills(couple, submission.getField(NUMBER_OF_OCP_STRIPS_SUPPLIED_FIELD_NAME), date);
}
if (CONDOM_FP_METHOD_VALUE.equalsIgnoreCase(fpMethod)) {
return updateCondomDetailsWithRefills(couple, submission.getField(NUMBER_OF_CONDOMS_SUPPLIED_FIELD_NAME), date);
}
return null;
}
private EligibleCouple updateOCPDetailsWithRefills(EligibleCouple couple, String quantity, String date) {
List<OCPFPDetails> ocpFPDetails = couple.ocpFPDetails();
sortOCPFPDetails(ocpFPDetails);
OCPFPDetails requiredOCPFPDetail = getRequiredOCPFPDetails(ocpFPDetails, date);
int indexOfFPDetailToBeUpdated = ocpFPDetails.indexOf(requiredOCPFPDetail);
Map<String, String> refill = new HashMap<>();
refill.put(SERVICE_PROVIDED_DATE, date);
refill.put(QUANTITY, quantity);
requiredOCPFPDetail.refills().add(refill);
couple.ocpFPDetails().set(indexOfFPDetailToBeUpdated, requiredOCPFPDetail);
return couple;
}
private EligibleCouple updateCondomDetailsWithRefills(EligibleCouple couple, String quantity, String date) {
List<CondomFPDetails> condomFPDetails = couple.condomFPDetails();
sortCondomFPDetails(condomFPDetails);
CondomFPDetails requiredCondomFPDetail = getRequiredCondomFPDetails(date, condomFPDetails);
int indexOfFPDetailToBeUpdated = condomFPDetails.indexOf(requiredCondomFPDetail);
Map<String, String> refill = new HashMap<>();
refill.put(SERVICE_PROVIDED_DATE, date);
refill.put(QUANTITY, quantity);
requiredCondomFPDetail.refills().add(refill);
couple.condomFPDetails().set(indexOfFPDetailToBeUpdated, requiredCondomFPDetail);
return couple;
}
public void followupOnFPMethod(FormSubmission submission) {
EligibleCouple couple = allEligibleCouples.findByCaseId(submission.entityId());
if (couple == null) {
logger.warn("Tried to report FP follow up of a non-existing EC, with submission: " + submission);
return;
}
couple = updateECWithFPFollowUp(couple, submission, submission.getField(FP_FOLLOWUP_DATE_FIELD_NAME));
allEligibleCouples.update(couple);
FPProductInformation fpProductInformation = new FPProductInformation(
submission.entityId(), submission.anmId(),
submission.getField(CURRENT_FP_METHOD_FIELD_NAME),
null, null, null, null, null,
submission.getField(SUBMISSION_DATE_FIELD_NAME),
null, submission.getField(FP_FOLLOWUP_DATE_FIELD_NAME), submission.getField(NEEDS_FOLLOWUP_FIELD_NAME), submission.getField(NEEDS_REFERRAL_FOLLOWUP_FIELD_NAME));
schedulingService.fpFollowup(fpProductInformation);
}
private EligibleCouple updateECWithFPFollowUp(EligibleCouple couple, FormSubmission submission, String date) {
String fpMethod = submission.getField(CURRENT_FP_METHOD_FIELD_NAME);
if (FEMALE_STERILIZATION_FP_METHOD_VALUE.equalsIgnoreCase(fpMethod)) {
return updateFemaleSterilizationDetailsWithFollowUpDate(date, couple);
}
if (MALE_STERILIZATION_FP_METHOD_VALUE.equalsIgnoreCase(fpMethod)) {
return updateMaleSterilizationDetailsWithFollowUpDate(date, couple);
}
return couple;
}
private EligibleCouple updateMaleSterilizationDetailsWithFollowUpDate(String followUpDate, EligibleCouple couple) {
List<MaleSterilizationFPDetails> maleSterilizationFPDetails = couple.maleSterilizationFPDetails();
sortMaleSterilizationDetails(maleSterilizationFPDetails);
MaleSterilizationFPDetails requiredMaleSterilizationFPDetail = getRequiredMaleSterilizationFPDetails(followUpDate, maleSterilizationFPDetails);
int indexOfFPDetailToBeUpdated = maleSterilizationFPDetails.indexOf(requiredMaleSterilizationFPDetail);
requiredMaleSterilizationFPDetail.followupVisitDates().add(followUpDate);
couple.maleSterilizationFPDetails().set(indexOfFPDetailToBeUpdated, requiredMaleSterilizationFPDetail);
return couple;
}
private EligibleCouple updateFemaleSterilizationDetailsWithFollowUpDate(String followUpDate, EligibleCouple couple) {
List<FemaleSterilizationFPDetails> femaleSterilizationFPDetails = couple.femaleSterilizationFPDetails();
sortFemaleSterilizationDetails(femaleSterilizationFPDetails);
FemaleSterilizationFPDetails requiredFemaleSterilizationFPDetail = getRequiredFemaleSterilizationFPDetails(followUpDate, femaleSterilizationFPDetails);
int indexOfFPDetailToBeUpdated = femaleSterilizationFPDetails.indexOf(requiredFemaleSterilizationFPDetail);
requiredFemaleSterilizationFPDetail.followupVisitDates().add(followUpDate);
couple.femaleSterilizationFPDetails().set(indexOfFPDetailToBeUpdated, requiredFemaleSterilizationFPDetail);
return couple;
}
private void sortOCPFPDetails(List<OCPFPDetails> ocpFPDetails) {
Collections.sort(ocpFPDetails, new Comparator<OCPFPDetails>() {
@Override
public int compare(OCPFPDetails detail, OCPFPDetails anotherDetail) {
return LocalDate.parse(detail.fpAcceptanceDate()).compareTo(LocalDate.parse(anotherDetail.fpAcceptanceDate()));
}
});
}
private void sortCondomFPDetails(List<CondomFPDetails> condomFPDetails) {
Collections.sort(condomFPDetails, new Comparator<CondomFPDetails>() {
@Override
public int compare(CondomFPDetails detail, CondomFPDetails anotherDetail) {
return LocalDate.parse(detail.fpAcceptanceDate()).compareTo(LocalDate.parse(anotherDetail.fpAcceptanceDate()));
}
});
}
private void sortFemaleSterilizationDetails(List<FemaleSterilizationFPDetails> femaleSterilizationDetails) {
Collections.sort(femaleSterilizationDetails, new Comparator<FemaleSterilizationFPDetails>() {
@Override
public int compare(FemaleSterilizationFPDetails detail, FemaleSterilizationFPDetails anotherDetail) {
return LocalDate.parse(detail.sterilizationDate()).compareTo(LocalDate.parse(anotherDetail.sterilizationDate()));
}
});
}
private void sortMaleSterilizationDetails(List<MaleSterilizationFPDetails> maleSterilizationDetails) {
Collections.sort(maleSterilizationDetails, new Comparator<MaleSterilizationFPDetails>() {
@Override
public int compare(MaleSterilizationFPDetails detail, MaleSterilizationFPDetails anotherDetail) {
return LocalDate.parse(detail.sterilizationDate()).compareTo(LocalDate.parse(anotherDetail.sterilizationDate()));
}
});
}
private OCPFPDetails getRequiredOCPFPDetails(List<OCPFPDetails> ocpFPDetails, String refillDate) {
List<OCPFPDetails> requiredOCPFPDetails = new ArrayList<>();
for (OCPFPDetails ocpFPDetail : ocpFPDetails) {
LocalDate date = LocalDate.parse(ocpFPDetail.fpAcceptanceDate());
if (!LocalDate.parse(refillDate).isBefore(date)) {
requiredOCPFPDetails.add(ocpFPDetail);
}
}
return requiredOCPFPDetails.get(requiredOCPFPDetails.size() - 1);
}
private CondomFPDetails getRequiredCondomFPDetails(String refillDate, List<CondomFPDetails> condomFPDetails) {
List<CondomFPDetails> requiredCondomFPDetails = new ArrayList<>();
for (CondomFPDetails condomFPDetail : condomFPDetails) {
LocalDate date = LocalDate.parse(condomFPDetail.fpAcceptanceDate());
if (!LocalDate.parse(refillDate).isBefore(date)) {
requiredCondomFPDetails.add(condomFPDetail);
}
}
return requiredCondomFPDetails.get(requiredCondomFPDetails.size() - 1);
}
private FemaleSterilizationFPDetails getRequiredFemaleSterilizationFPDetails(String followUpDate, List<FemaleSterilizationFPDetails> femaleSterilizationFPDetails) {
List<FemaleSterilizationFPDetails> requiredFemaleSterilizationFPDetails = new ArrayList<>();
for (FemaleSterilizationFPDetails femaleSterilizationFPDetail : femaleSterilizationFPDetails) {
LocalDate date = LocalDate.parse(femaleSterilizationFPDetail.sterilizationDate());
if (!LocalDate.parse(followUpDate).isBefore(date)) {
requiredFemaleSterilizationFPDetails.add(femaleSterilizationFPDetail);
}
}
return requiredFemaleSterilizationFPDetails.get(requiredFemaleSterilizationFPDetails.size() - 1);
}
private MaleSterilizationFPDetails getRequiredMaleSterilizationFPDetails(String followUpDate, List<MaleSterilizationFPDetails> sterilizationFPDetails) {
List<MaleSterilizationFPDetails> requiredMaleSterilizationFPDetails = new ArrayList<>();
for (MaleSterilizationFPDetails maleSterilizationFPDetail : sterilizationFPDetails) {
LocalDate date = LocalDate.parse(maleSterilizationFPDetail.sterilizationDate());
if (!LocalDate.parse(followUpDate).isBefore(date)) {
requiredMaleSterilizationFPDetails.add(maleSterilizationFPDetail);
}
}
return requiredMaleSterilizationFPDetails.get(requiredMaleSterilizationFPDetails.size() - 1);
}
public void handleReferralFollowup(FormSubmission submission) {
EligibleCouple couple = allEligibleCouples.findByCaseId(submission.entityId());
if (couple == null) {
logger.warn("Tried to report FP Referral follow-up of a non-existing EC, with submission: " + submission);
return;
}
couple = updateECWithFPFollowUp(couple, submission, submission.getField(REFERRAL_FOLLOW_UP_DATE_FIELD_NAME));
allEligibleCouples.update(couple);
FPProductInformation fpProductInformation = new FPProductInformation(
submission.entityId(), submission.anmId(),
null, null, null, null, null, null, submission.getField(SUBMISSION_DATE_FIELD_NAME), null,
submission.getField(REFERRAL_FOLLOW_UP_DATE_FIELD_NAME), submission.getField(NEEDS_FOLLOWUP_FIELD_NAME), submission.getField(NEEDS_REFERRAL_FOLLOWUP_FIELD_NAME));
schedulingService.reportReferralFollowup(fpProductInformation);
}
public void closeEligibleCouple(FormSubmission submission) {
if (!BOOLEAN_TRUE_VALUE.equalsIgnoreCase(submission.getField(IS_EC_CLOSE_CONFIRMED_FIELD_NAME))) {
logger.warn("ANM has not confirmed the close so not closing EC! Form Submission: " + submission);
return;
}
if (!allEligibleCouples.exists(submission.entityId())) {
logger.warn("Cannot close EC as it does not exist! Form Submission: " + submission);
return;
}
if (WRONG_ENTRY_VALUE.equalsIgnoreCase(submission.getField(CLOSE_REASON_FIELD_NAME))) {
logger.info("Deleting Reports for EC with Entity Id: " + submission.entityId());
reportingService.deleteReportsForEC(submission.entityId());
}
logger.info("Closing EC : " + submission);
allEligibleCouples.close(submission.entityId());
//#TODO: actionService.markAllAlertsAsInactive(submission.entityId());
}
}