package gov.cms.grouper.snf.process;

import gov.cms.grouper.snf.SnfContext;
import gov.cms.grouper.snf.model.Assessment;
import gov.cms.grouper.snf.model.enums.AssessmentType;
import gov.cms.grouper.snf.model.enums.Rai300;
import gov.cms.grouper.snf.transfer.ISnfClaim;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import gov.cms.grouper.snf.transfer.SnfClaim;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SnfValidations {

  private static final Logger log = LoggerFactory.getLogger(SnfValidations.class);
  private static final Pattern pattern =
      Pattern.compile("([A-Za-z][0-9][0-9A-Za-z]\\.[0-9A-Za-z^]{4})|(\\^)");
  private final ISnfClaim snfClaim;

  public SnfValidations(ISnfClaim snfClaim) {
    this.snfClaim = snfClaim;
  }

  public static void validateInputs(ISnfClaim snfClaim) {
    if (!validateDiagnosisPattern(snfClaim.getPrimaryDiagnosis().getValue())) {
      SnfContext.trace("Invalid principal diagnosis code pattern");

    } else if (!validateAssessments(snfClaim.getAssessmentMap())) {
      SnfContext.trace("Invalid assessment value");
    }

  }

  /**
   * Since some of the valid value is range based some are not, we use special logic: treating the
   * first two values as its range and the following are also valid. So if a value is between it's
   * first two values or matches any of the following values, it's valid.
   */
  protected static boolean validateAssessments(final Map<String, Assessment> assessmentMap) {
    boolean isAllValueValid = true;
    List<Assessment> validAssessments = assessmentMap.values().stream().filter((assessment) -> {
      boolean result = false;

      Optional<Rai300> maybeRai300 = Rai300.of(assessment.getItem());
      if (maybeRai300.isPresent()) {
        AssessmentType assessmentType = maybeRai300.get().getAssessmentType();
        // Filter down the actual assessment values not being these other types.
        if (!AssessmentType.getNonAssessmentSpecificTypes().contains(assessmentType)
              && assessment.getValueInt() != Assessment.NULL_VALUE) {
          result = true;
        }
      } else {
        log.warn("Invalid assessment item '{}' provided", assessment.getItem());
      }

      return result;
    }).collect(Collectors.toList());

    for (Assessment assessment : validAssessments) {
      // Rai300.valueOf can be used here as the valid assessments are already filtered above.
      List<Integer> validValues = Rai300.valueOf(assessment.getItem()).getValidValues();

      if ((assessment.getValueInt() <= validValues.get(0)
          || assessment.getValueInt() >= validValues.get(1))
          && !validValues.contains(assessment.getValueInt())) {
        log.warn("Item " + assessment.getItem() + " has invalid value of: " + assessment.getValueInt());
        isAllValueValid = false;
      }
    }

    return isAllValueValid;
  }

  protected static boolean validateDiagnosisPattern(String code) {
    final char caret = '^';
    // Check pattern with regex; condition a - e
    boolean isMatch = SnfValidations.pattern.matcher(code).matches();

    // Check ^, condition f; not sure how to check this with regex otherwise
    if (isMatch) {
      int caretIndex = code.indexOf(caret);
      if (caretIndex != -1) {
        for (int i = caretIndex; i < code.length(); i++) {
          if (code.charAt(i) != caret) {
            return false;
          }
        }
      }
    }

    return isMatch;
  }
}
