package com.airlock.iam.plugin.configeditor.ui.dialog;

import com.airlock.iam.plugin.api.config.ConfigurationVariable;
import com.vaadin.data.HasValue.ValueChangeEvent;
import com.vaadin.data.HasValue.ValueChangeListener;
import com.vaadin.server.UserError;
import com.vaadin.server.Sizeable.Unit;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;

public final class VariableDialog extends Window {
   private static final String STORE_BUTTON_CAPTION = "Save";
   private static final String CANCEL_BUTTON_CAPTION = "Cancel";
   private static final String VARIABLE_INPUT_FIELD_LABEL = "Variable name";
   private static final String DIALOG_TITLE = "Configuration variable";
   private static final String FIELD_DESCRIPTION = "The name of the environment variable for the property.";
   private final TextField inputField;
   private final CheckBox sensitiveCheckbox;
   private boolean hasChanged;
   private Optional<ConfigurationVariable> variable = Optional.empty();

   public VariableDialog(Optional<ConfigurationVariable> variable, boolean sensitiveProperty) {
      this.setModal(true);
      this.setResizable(false);
      this.setCaption("Configuration variable");
      VerticalLayout layout = new VerticalLayout();
      layout.setWidth(300.0F, Unit.PIXELS);
      this.inputField = new TextField("Variable name");
      this.inputField.setDescription("The name of the environment variable for the property.");
      this.inputField.setWidthFull();
      this.inputField.setMaxLength(200);
      this.inputField.setId("variableName");
      variable.ifPresent(v -> this.inputField.setValue(v.name()));
      layout.addComponent(this.inputField);
      this.sensitiveCheckbox = new CheckBox("Sensitive value (masked in logs)");
      this.sensitiveCheckbox.setId("variableSensitive");
      if (variable.isPresent()) {
         this.sensitiveCheckbox.setValue(variable.get().sensitive());
      } else {
         this.sensitiveCheckbox.setValue(sensitiveProperty);
      }

      layout.addComponent(this.sensitiveCheckbox);
      HorizontalLayout footer = new HorizontalLayout();
      footer.setWidth(100.0F, Unit.PERCENTAGE);
      Button cancelButton = new Button("Cancel", event -> UI.getCurrent().removeWindow(this));
      cancelButton.setId("cancelButton");
      footer.addComponent(cancelButton);
      Button storeVariableButton = new Button("Save", event -> this.handleSaveVariable());
      storeVariableButton.setId("primaryButton");
      storeVariableButton.setClickShortcut(13, new int[0]);
      storeVariableButton.addStyleName("primary");
      footer.addComponent(storeVariableButton);
      footer.setComponentAlignment(storeVariableButton, Alignment.MIDDLE_RIGHT);
      layout.addComponent(footer);
      this.inputField.addValueChangeListener(new VariableDialog.VariableNameValidator(this.inputField, storeVariableButton));
      this.setContent(layout);
   }

   private void handleSaveVariable() {
      this.variable = Optional.ofNullable(this.inputField.getValue())
         .<String>map(StringUtils::trimToNull)
         .map(v -> new ConfigurationVariable(v, this.sensitiveCheckbox.getValue()));
      this.hasChanged = true;
      UI.getCurrent().removeWindow(this);
   }

   public Optional<ConfigurationVariable> getVariable() {
      return this.variable;
   }

   public boolean hasChanged() {
      return this.hasChanged;
   }

   private static final class VariableNameValidator implements ValueChangeListener<String> {
      private static final Predicate<String> VARIABLE_REGEX = Pattern.compile("[A-Za-z0-9_]*").asMatchPredicate();
      private final TextField field;
      private final Button saveButton;

      public void valueChange(ValueChangeEvent<String> event) {
         if (!VARIABLE_REGEX.test((String)event.getValue())) {
            this.field.setDescription(null);
            this.field.setComponentError(new UserError("Environment variables must consist of letters, digits, and underscores (\"_\")."));
            this.saveButton.setEnabled(false);
         } else {
            this.field.setDescription("The name of the environment variable for the property.");
            this.field.setComponentError(null);
            this.saveButton.setEnabled(true);
         }
      }

      @SuppressFBWarnings(justification = "generated code")
      public VariableNameValidator(TextField field, Button saveButton) {
         this.field = field;
         this.saveButton = saveButton;
      }
   }
}
