package gov.cms.grouper.snf.r2.logic.nursing;

import gov.cms.grouper.snf.SnfContext;
import gov.cms.grouper.snf.r2.logic.SnfDataVersionImpl;
import gov.cms.grouper.snf.model.Assessment;
import gov.cms.grouper.snf.model.enums.NursingCmg;
import gov.cms.grouper.snf.model.enums.Rai300;
import gov.cms.grouper.snf.util.ClaimInfo;
import gov.cms.grouper.snf.util.Lazy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;

/**
 * <a href="doc-files/mds-3.0-rai-manual-v1.17.1_october_2019.pdf#page=701" class=
 * "req">CATEGORY:BEHAVIORAL SYMPTOMS AND COGNITIVE PERFORMANCE</a>
 */
public class BscpLogic extends SnfDataVersionImpl<NursingCmg> {

  public static final int FUNCTION_SCORE_LIMIT = 11;
  public static final int NURSING_COUNT_NUM_DAYS_IN_LAST_7_DAYS = 6;


  private final ClaimInfo claim;
  private final Supplier<ReducedPhysicalFunctionLogic> physicalSupplier;
  private final Integer c0500;
  private final Integer c0100;
  private final Boolean isBehavioralSymptomsPresent;
  private final Supplier<NursingCmg> physicalCmg;

  public BscpLogic(ClaimInfo claim, Supplier<ReducedPhysicalFunctionLogic> physicalSupplier) {
    super(claim.getDataVersion());
    this.claim = claim;
    this.physicalSupplier = physicalSupplier;
    c0500 = claim.getAssessmentValue(Rai300.C0500);
    c0100 = claim.getAssessmentValue(Rai300.C0100);
    isBehavioralSymptomsPresent = getBehavioralSymptomsPresent();
    physicalCmg = new Lazy<>(() -> physicalSupplier.get().exec());
  }

  public List<Assessment> getBehavioralSymptomsAssessmentList(ClaimInfo claim) {
    List<Assessment> result = new ArrayList<>();
    for (Rai300 rai : RaiSets.BEHAVIORAL_SYMPTOMS.getSet()) {
      Assessment ast = claim.getAssessment(rai);
      if (ast != null) {
        result.add(ast);
      }
    }
    return Collections.unmodifiableList(result);
  }

  public boolean isClassifiedBehavioralSymptomsCognitivePerformance() {
    Supplier<Boolean> comatoseSupplier = new Lazy<>(
        () -> claim.isComaAndNoActivities());
    boolean result = ClaimInfo.isClassifiedBehavioralSymptomsCognitivePerformance(
        () -> claim.getAssessmentValue(Rai300.B0700),
        () -> claim.getAssessmentValue(Rai300.C0700),
        () -> claim.getAssessmentValue(Rai300.C1000), comatoseSupplier);

    return result;
  }

  public Boolean getBehavioralSymptomsPresent() {
      boolean e0100AChecked = claim.isCheckedAndNotNull(Rai300.E0100A);
      boolean e0100BChecked = claim.isCheckedAndNotNull(Rai300.E0100B);
      List<Assessment> behavioralSymptomsAssessmentList =
          getBehavioralSymptomsAssessmentList(claim);
      boolean hasSymptoms = hasSymptoms(behavioralSymptomsAssessmentList);
      return isBehavioralSymptomsPresent(e0100AChecked, e0100BChecked, hasSymptoms);
  }

  public boolean isBehavioralSymptomsPresent(boolean e0100AChecked, boolean e0100BChecked,
      boolean hasSymptoms) {

    return hasSymptoms || e0100BChecked || e0100AChecked;
  }

  public boolean hasSymptoms(List<Assessment> behavioralSymptomsAssessmentList) {
    boolean hasSymptoms = false;
    for (Assessment ast : behavioralSymptomsAssessmentList) {
      // since we already did a null check when loading in the list, no need for 2nd one
      hasSymptoms = ast.getValueInt() >= 2;
      // ClaimInfo.isTrue(ast, (item) -> item.getValueInt() >= 2);
      if (hasSymptoms) {
        break;
      }
    }
    return hasSymptoms;
  }

  /**
   * Determine whether the resident presents with one of the following behavioral symptoms.
   * <a href="doc-files/mds-3.0-rai-manual-v1.17.1_october_2019.pdf#page=702" class="req">Step4</a>
   */
  protected NursingCmg step4(boolean isBehavioralSymptomsPresent) {
    NursingCmg result;

    if (isBehavioralSymptomsPresent) {
      result = step6();
    } else {
      result = physicalSupplier.get().exec();
    }

    return SnfContext.trace(result);
  }

  /**
   * Determine Restorative Nursing Count: Count the number of the following services provided for 15
   * or more minutes a day for 6 or more of the last 7 days
   * <a href="doc-files/mds-3.0-rai-manual-v1.17.1_october_2019.pdf#page=703" class=
   * "req">Step5</a>
   */
  public int step5() {
    int restorativeNursingCount = 0;
    final int h0200C = claim.getAssessmentValue(Rai300.H0200C);
    final int h0500 = claim.getAssessmentValue(Rai300.H0500);
    final int o0500A = claim.getAssessmentValue(Rai300.O0500A);
    final int o0500B = claim.getAssessmentValue(Rai300.O0500B);
    final int o0500D = claim.getAssessmentValue(Rai300.O0500D);
    final int o0500F = claim.getAssessmentValue(Rai300.O0500F);

    if (h0200C == Assessment.CHECKED || h0500 == Assessment.CHECKED) {
      restorativeNursingCount += 1;
    }
    if (o0500A >= NURSING_COUNT_NUM_DAYS_IN_LAST_7_DAYS
        || o0500B >= NURSING_COUNT_NUM_DAYS_IN_LAST_7_DAYS) {
      restorativeNursingCount += 1;
    }
    if (o0500D >= NURSING_COUNT_NUM_DAYS_IN_LAST_7_DAYS
        || o0500F >= NURSING_COUNT_NUM_DAYS_IN_LAST_7_DAYS) {
      restorativeNursingCount += 1;
    }

    for (Rai300 service : RaiSets.BSCP_SERVICES.getSet()) {
      if (claim.getAssessmentValue(service) >= NURSING_COUNT_NUM_DAYS_IN_LAST_7_DAYS) {
        restorativeNursingCount += 1;
      }
    }

    return SnfContext.trace(restorativeNursingCount);
  }

  /**
   * Select the final PDPM Classification by using the total PDPM Nursing Function Score and the
   * Restorative Nursing Count
   * <a href="doc-files/mds-3.0-rai-manual-v1.17.1_october_2019.pdf#page=703" class="req">Step6</a>
   */
  protected NursingCmg step6() {
    int nursingFunctionScore = claim.getFunctionScore();

    NursingCmg result = null;
    if (FUNCTION_SCORE_LIMIT <= nursingFunctionScore && nursingFunctionScore <= 16) {
      int step5RestorativeNursingCount = step5();
      if (step5RestorativeNursingCount >= 2) {
        result = NursingCmg.BAB2;
      } else {
        result = NursingCmg.BAB1;
      }
    }

    return SnfContext.trace(result);
  }

  public NursingCmg eval(boolean isClassifiedBehavioralSymptomsCognitivePerformance,
      Integer c0500, Integer c0100,
      Supplier<NursingCmg> physicalSupplier) {
    NursingCmg result = null;
    // step #1
    int nursingFunctionScore = claim.getFunctionScore();
    if (nursingFunctionScore < FUNCTION_SCORE_LIMIT) {
      result = physicalSupplier.get();
    } else if (c0100 != 0 && c0500 <= 9
        && c0100 != Assessment.NULL_VALUE && c0500 != Assessment.NULL_VALUE) {
      // step #2
      // step #5/6
      result = step6();
    } else if (c0100 != 0 && c0500 > 9 && c0500 != 99) {
      // step #4
      result = step4(isBehavioralSymptomsPresent);
    } else if (isClassifiedBehavioralSymptomsCognitivePerformance) {
      // step #5/6
      result = step6();
    } else {
      // step #4
      result = step4(isBehavioralSymptomsPresent);
    }

    return SnfContext.trace(result);

  }

  @Override
  public NursingCmg exec() {

    boolean isClassifiedBehavioralSymptomsCognitivePerformance =
        isClassifiedBehavioralSymptomsCognitivePerformance();

    NursingCmg result = eval(isClassifiedBehavioralSymptomsCognitivePerformance, c0500,
        c0100, physicalCmg);

    return SnfContext.trace(result);
  }

}
