package gov.cms.grouper.snf.util.reader;

import gov.cms.grouper.snf.SnfTables;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

import gov.cms.grouper.snf.app.Pdpm;
import gov.cms.grouper.snf.lego.SnfUtils;
import gov.cms.grouper.snf.model.Assessment;
import gov.cms.grouper.snf.model.AssessmentType;
import gov.cms.grouper.snf.model.SnfDiagnosisCode;
import gov.cms.grouper.snf.model.SnfError;
import gov.cms.grouper.snf.model.reader.AssessmentItem;
import gov.cms.grouper.snf.model.reader.Rai300;
import gov.cms.grouper.snf.transfer.SnfClaim;

/**
 * Claim Mapper for String/fixed length/UTF record to and from the ISnfClaim.
 */
public class SnfDataMapper {

  /**
   * This wraps the claim in a proxy claim in order to store the original record for use later when
   * the record must be restored.
   */
  public SnfClaim mapMdsToClaim(String utfRecord, AssessmentItem[] items) {
    SnfClaim claim = new SnfClaim();
    claim.setOriginalRecord(utfRecord);
    if (utfRecord.length() < Rai300.FIXED_LENGTH_SIZE) {
      claim.addErrors(SnfError.INVALID_LINE_LENGTH.getReason(utfRecord.length() + ""));
    }

    for (AssessmentItem item : items) {
      final int index = item.getIndex() - 1;
      String itemValue = utfRecord.substring(index, index + item.getLength());

      if (item.getAssessmentType() == AssessmentType.ARD) {
        try {
          claim.setAssessmentReferenceDate(
              LocalDate.parse(itemValue, DateTimeFormatter.BASIC_ISO_DATE));
        } catch (DateTimeParseException e) {
          claim.addErrors(SnfError.INVALID_ASSESSMENT_REFERENCE_DATE.getReason(itemValue));
        }
      } else if (item.getAssessmentType() == AssessmentType.PDX) {
        claim.insertPrimaryDiagnosis(new SnfDiagnosisCode(itemValue, null, null, null));

      } else if (item.getAssessmentType() == AssessmentType.SERVICES) {
        if (!SnfUtils.trimICDCodes(itemValue).isEmpty()) {
          claim.addCode(new SnfDiagnosisCode(itemValue, null, null, null));
        }
      } else if (item.getAssessmentType() == AssessmentType.PPS) {
        int value = SnfUtils.parse(itemValue, Integer.MIN_VALUE);
        if (value == 8) {
          claim.setAiCode(0);
        } else if (value == 1) {
          claim.setAiCode(1);
        } else if (value == 99) {
          claim.setAiCode(null);
        }

      } else if (item.getAssessmentType() == AssessmentType.OBRA) {
        int value = SnfUtils.parse(itemValue, Integer.MIN_VALUE);
        if (Rai300.A0310A.getValidValues().contains(value)) {
          claim.setObra(value);
        } else {
          Pdpm.log.warn("Invalid value for A0310A");
        }
      } else {
        Assessment assessment = new Assessment(item.name(), item.getXmlTag(), itemValue);
        claim.addAssessment(assessment);
      }
    }

    return claim;
  }

  /**
   * This retrieves the original record from the Claim and then puts the Hipps code into is proper
   * place in the record. No other data is altered.
   *
   * @param claim An SnfClaimProxy.
   * @param items array of Assessment items.
   * @return if the claim is a Proxy claim, then it returns the original record with the HIPPS code
   *         placed within the record. Otherwise, it will just return the HIPPS Code (5 character
   *         string).
   */
  public String mapClaimToMds(SnfClaim claim, AssessmentItem[] items) {
    String hippsCode = claim.hasError() ? "       " : claim.getHippsCode();

    StringBuilder record = new StringBuilder(claim.getOriginalRecord());
    int hippsIndex = Rai300.RECALCULATED_Z0100A.getIndex() - 1;
    record.replace(hippsIndex, hippsIndex + hippsCode.length(), hippsCode);

    int versionIndex = Rai300.RECALCULATED_Z0100B.getIndex() - 1;
    // substring to remove the leading V
    String version = claim.getRecalculated_z0100b();
    record.replace(versionIndex, versionIndex + version.length(), version);

    return record.toString();
  }

}
