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

import gov.cms.grouper.snf.SnfContext;
import gov.cms.grouper.snf.model.SnfProcessException;
import gov.cms.grouper.snf.r2.logic.nursing.BscpLogic;
import gov.cms.grouper.snf.r2.logic.nursing.ReducedPhysicalFunctionLogic;
import gov.cms.grouper.snf.r2.logic.nursing.SpecialCare;
import gov.cms.grouper.snf.model.enums.NursingCmg;
import gov.cms.grouper.snf.model.enums.Rai300;
import gov.cms.grouper.snf.util.ClaimInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <a href="doc-files/mds-3.0-rai-manual-v1.17.1_october_2019.pdf#page=690" class="req">PDPM
 * Payment
 * Component: Nursing</a>
 */
public class NursingLogic extends SnfDataVersionImpl<NursingCmg> {
  private static final Logger log = LoggerFactory.getLogger(NursingLogic.class);

  public static final List<Rai300> EXTENSIVE_SERVICES =
      Arrays.asList(Rai300.O0100E2, Rai300.O0100F2, Rai300.O0100M2);

  private final ClaimInfo claim;
  private final SpecialCare specialCare;
  private final ReducedPhysicalFunctionLogic physical;
  private final BscpLogic bscp;

  public NursingLogic(ClaimInfo claim, SpecialCare specialCare,
      ReducedPhysicalFunctionLogic physical, BscpLogic bscp) {
    super(claim.getDataVersion());
    this.claim = claim;
    this.specialCare = specialCare;
    this.physical = physical;
    this.bscp = bscp;
  }


  protected List<Rai300> getCodeServices() {
    List<Rai300> services = EXTENSIVE_SERVICES.stream().filter((item) -> {
      boolean result = claim.isCheckedAndNotNull(item);
      return result;
    }).collect(Collectors.toList());

    return services;
  }

  /**
   * Evaluate the resident's
   * <a href="doc-files/mds-3.0-rai-manual-v1.17.1_october_2019.pdf#page=692" class="req">Extensive
   * Services</a> related to tracheostomy care, ventilator or respirator, in isolation or
   * quarantine.
   *
   * @return the applicable NursingCmg, or null if not applicable
   */
  protected NursingCmg evaluateExtensiveServices() {

    List<Rai300> codedServices = this.getCodeServices();
    int nursingFunctionScore = claim.getFunctionScore();

    NursingCmg result = null;
    if (codedServices != null && !codedServices.isEmpty()) {
      if (nursingFunctionScore >= 15) {
        result = this.specialCare.evaluateClinicallyComplex();

      } else if (codedServices.contains(Rai300.O0100E2) && codedServices.contains(Rai300.O0100F2)) {
        result = NursingCmg.ES3;
      } else if (codedServices.contains(Rai300.O0100E2) || codedServices.contains(Rai300.O0100F2)) {
        result = NursingCmg.ES2;
      } else if (codedServices.contains(Rai300.O0100M2) && !codedServices.contains(Rai300.O0100E2)
          && !codedServices.contains(Rai300.O0100F2)) {
        result = NursingCmg.ES1;
      } else {
        result = null;
      }
    }

    return SnfContext.trace(result);
  }

  @Override
  public NursingCmg exec() {

    NursingCmg cmg = this.evaluateExtensiveServices();
    if (cmg == null) {
      List<SnfDataVersionImpl<NursingCmg>> components =
          Arrays.asList(this.specialCare, this.bscp, this.physical);

      for (SnfDataVersionImpl<NursingCmg> com : components) {
        cmg = com.exec();
        if (cmg != null) {
          break;
        }
      }
    }

    if (cmg == null) {  // if cmg is still null, throw an exception
      throw new SnfProcessException("Unable to determine CMG for Nursing Logic");
    }

    return SnfContext.trace(cmg);
  }

  public ClaimInfo getClaim() {
    return claim;
  }

  public SpecialCare getSpecialCare() {
    return specialCare;
  }

  public ReducedPhysicalFunctionLogic getPhysical() {
    return physical;
  }

  public BscpLogic getBscp() {
    return bscp;
  }

}
