/**
   * Processes requests to purge a concept reference term
   *
   * @param request the {@link WebRequest} object
   * @param conceptReferenceTermModel
   * @return the url to forward to
   */
  @RequestMapping(method = RequestMethod.POST, value = "/admin/concepts/purgeConceptReferenceTerm")
  public String purgeTerm(
      WebRequest request,
      @ModelAttribute(value = "conceptReferenceTermModel")
          ConceptReferenceTermModel conceptReferenceTermModel) {
    Integer id = conceptReferenceTermModel.getConceptReferenceTerm().getId();
    try {
      Context.getConceptService()
          .purgeConceptReferenceTerm(conceptReferenceTermModel.getConceptReferenceTerm());
      if (log.isDebugEnabled()) {
        log.debug("Purged concept reference term with id: " + id);
      }
      request.setAttribute(
          WebConstants.OPENMRS_MSG_ATTR,
          Context.getMessageSourceService().getMessage("ConceptReferenceTerm.purged"),
          WebRequest.SCOPE_SESSION);
      return "redirect:" + FIND_CONCEPT_REFERENCE_TERM_URL;
    } catch (APIException e) {
      log.warn("Error occurred while attempting to purge concept reference term", e);
      request.setAttribute(
          WebConstants.OPENMRS_ERROR_ATTR,
          Context.getMessageSourceService().getMessage("ConceptReferenceTerm.purge.error"),
          WebRequest.SCOPE_SESSION);
    }

    // send the user back to form
    return "redirect:" + CONCEPT_REFERENCE_TERM_FORM_URL + ".form?conceptReferenceTermId=" + id;
  }
  /**
   * Processes requests to unretire concept reference terms
   *
   * @param request the {@link WebRequest} object
   * @param conceptReferenceTermModel the concept reference term model object for the term to
   *     unretire
   * @param retireReason the reason why the concept reference term is being unretired
   * @return the url to redirect to
   */
  @RequestMapping(
      method = RequestMethod.POST,
      value = "/admin/concepts/unretireConceptReferenceTerm")
  public String unretireConceptReferenceTerm(
      WebRequest request,
      @ModelAttribute(value = "conceptReferenceTermModel")
          ConceptReferenceTermModel conceptReferenceTermModel) {

    try {
      ConceptReferenceTerm conceptReferenceTerm =
          conceptReferenceTermModel.getConceptReferenceTerm();
      Context.getConceptService().unretireConceptReferenceTerm(conceptReferenceTerm);
      if (log.isDebugEnabled()) {
        log.debug("Unretired concept reference term with id: " + conceptReferenceTerm.getId());
      }
      request.setAttribute(
          WebConstants.OPENMRS_MSG_ATTR,
          Context.getMessageSourceService().getMessage("ConceptReferenceTerm.unretired"),
          WebRequest.SCOPE_SESSION);

      return "redirect:"
          + CONCEPT_REFERENCE_TERM_FORM_URL
          + ".form?conceptReferenceTermId="
          + conceptReferenceTerm.getConceptReferenceTermId();
    } catch (APIException e) {
      log.error("Error occurred while unretiring concept reference term", e);
      request.setAttribute(
          WebConstants.OPENMRS_ERROR_ATTR,
          Context.getMessageSourceService().getMessage("ConceptReferenceTerm.unretire.error"),
          WebRequest.SCOPE_SESSION);
    }

    // an error occurred, show the form
    return CONCEPT_REFERENCE_TERM_FORM;
  }
  /**
   * Processes requests to retire concept reference terms
   *
   * @param request the {@link WebRequest} object
   * @param conceptReferenceTermModel the concept reference term model for the term to retire
   * @param retireReason the reason why the concept reference term is being retired
   * @return the url to redirect to
   */
  @RequestMapping(method = RequestMethod.POST, value = "/admin/concepts/retireConceptReferenceTerm")
  public String retireConceptReferenceTerm(
      WebRequest request,
      @ModelAttribute(value = "conceptReferenceTermModel")
          ConceptReferenceTermModel conceptReferenceTermModel,
      @RequestParam(required = false, value = "retireReason") String retireReason) {

    if (!StringUtils.hasText(retireReason)) {
      retireReason = Context.getMessageSourceService().getMessage("general.default.retireReason");
    }

    try {
      ConceptReferenceTerm conceptReferenceTerm =
          conceptReferenceTermModel.getConceptReferenceTerm();
      Context.getConceptService().retireConceptReferenceTerm(conceptReferenceTerm, retireReason);
      if (log.isDebugEnabled()) {
        log.debug("Retired concept reference term with id: " + conceptReferenceTerm.getId());
      }
      request.setAttribute(
          WebConstants.OPENMRS_MSG_ATTR,
          Context.getMessageSourceService().getMessage("ConceptReferenceTerm.retired"),
          WebRequest.SCOPE_SESSION);

      return "redirect:" + FIND_CONCEPT_REFERENCE_TERM_URL;
    } catch (APIException e) {
      log.error("Error occurred while retiring concept reference term", e);
      request.setAttribute(
          WebConstants.OPENMRS_ERROR_ATTR,
          Context.getMessageSourceService().getMessage("ConceptReferenceTerm.retire.error"),
          WebRequest.SCOPE_SESSION);
    }

    // an error occurred
    return CONCEPT_REFERENCE_TERM_FORM;
  }
  /**
   * Processes requests to save/update a concept reference term
   *
   * @param request the {@link WebRequest} object
   * @param conceptReferenceTermModel the concept reference term object to save/update
   * @param result the {@link BindingResult} object
   * @return the url to redirect to
   */
  @RequestMapping(method = RequestMethod.POST, value = CONCEPT_REFERENCE_TERM_FORM_URL)
  public String saveConceptReferenceTerm(
      WebRequest request,
      @ModelAttribute(value = "conceptReferenceTermModel")
          ConceptReferenceTermModel conceptReferenceTermModel,
      BindingResult result) {

    ConceptReferenceTerm conceptReferenceTerm = conceptReferenceTermModel.getConceptReferenceTerm();
    // add all the term maps
    // store ids of already mapped terms so that we don't map a term multiple times
    Set<Integer> mappedTermIds = null;
    for (int x = 0; x < conceptReferenceTermModel.getTermMaps().size(); x++) {
      if (mappedTermIds == null) {
        mappedTermIds = new HashSet<Integer>();
      }
      ConceptReferenceTermMap map = conceptReferenceTermModel.getTermMaps().get(x);

      if (map != null && map.getTermB() != null) {
        // skip past this mapping because its term is already in use by another mapping for this
        // term
        if (!mappedTermIds.add(map.getTermB().getConceptReferenceTermId())) {
          continue;
        }

        if (request.getParameter("_termMaps[" + x + "].exists") == null) {
          // because of the _termMap[x].exists input name in the jsp, the value will be null for
          // deleted maps, remove the map.
          conceptReferenceTerm.removeConceptReferenceTermMap(map);
        } else {
          if (map.getConceptMapType() == null) {
            result.rejectValue(
                "termMaps[" + x + "]",
                "ConceptReferenceTerm.error.mapTypeRequired",
                "Concept Map Type is required");
            log.warn("Concept Map Type is required");
            break;
          } else if (map.getTermB().equals(conceptReferenceTerm)) {
            result.rejectValue(
                "termMaps[" + x + "]",
                "ConceptReferenceTerm.map.sameTerm",
                "Cannot map a concept reference term to itself");
            log.warn("Cannot map a concept reference term to itself");
            break;
          } else if (!conceptReferenceTerm.getConceptReferenceTermMaps().contains(map)) {
            conceptReferenceTerm.addConceptReferenceTermMap(map);
          }
        }
      }
    }

    // if there are errors  with the term
    if (!result.hasErrors()) {
      try {
        result.pushNestedPath("conceptReferenceTerm");
        ValidationUtils.invokeValidator(
            new ConceptReferenceTermValidator(), conceptReferenceTerm, result);
        ValidationUtils.invokeValidator(
            new ConceptReferenceTermWebValidator(), conceptReferenceTerm, result);
      } finally {
        result.popNestedPath();
      }
    }

    if (!result.hasErrors()) {
      try {
        conceptReferenceTerm =
            Context.getConceptService().saveConceptReferenceTerm(conceptReferenceTerm);
        if (log.isDebugEnabled()) {
          log.debug("Saved concept reference term: " + conceptReferenceTerm.toString());
        }
        request.setAttribute(
            WebConstants.OPENMRS_MSG_ATTR, "ConceptReferenceTerm.saved", WebRequest.SCOPE_SESSION);
        return "redirect:"
            + CONCEPT_REFERENCE_TERM_FORM_URL
            + ".form?conceptReferenceTermId="
            + conceptReferenceTerm.getConceptReferenceTermId();
      } catch (APIException e) {
        log.error("Error while saving concept reference term", e);
        request.setAttribute(
            WebConstants.OPENMRS_ERROR_ATTR,
            "ConceptReferenceTerm.save.error",
            WebRequest.SCOPE_SESSION);
      }
    }

    return CONCEPT_REFERENCE_TERM_FORM;
  }