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

import com.airlock.iam.plugin.configeditor.ConfigEditorApplication;
import com.airlock.iam.plugin.configeditor.session.ConfigEditorSessionAccessor;
import com.airlock.iam.plugin.configeditor.util.GuiResources;
import com.airlock.iam.plugin.framework.misc.config.ConfigHistoryItem;
import com.vaadin.server.ThemeResource;
import com.vaadin.server.Sizeable.Unit;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Image;
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.Date;
import java.util.Optional;
import java.util.function.BooleanSupplier;

public final class ApplyConfigDialog extends Window {
   private static final int DIALOG_WIDTH = 450;
   private final ConfigEditorApplication app = (ConfigEditorApplication)UI.getCurrent();
   private final ConfigMetaDataPanel configCommentPanel;
   private final StatusPanel validationPanel;
   private final StatusPanel applyStatusPanel;
   private final Button retryButton;
   private final BooleanSupplier shouldLogConfigurationChanges;
   private boolean configurationStored;

   @SuppressFBWarnings(value = "MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR", justification = "See ALIAM-15315")
   public ApplyConfigDialog(BooleanSupplier shouldLogConfigurationChanges) {
      this.setWidth(450.0F, Unit.PIXELS);
      this.setCaption("Activate Configuration");
      this.setModal(true);
      this.setResizable(false);
      this.shouldLogConfigurationChanges = shouldLogConfigurationChanges;
      VerticalLayout mainLayout = new VerticalLayout();
      this.validationPanel = this.createValidationPanel();
      this.validationPanel.setId("validationPanel");
      mainLayout.addComponent(this.validationPanel);
      this.configCommentPanel = new ConfigMetaDataPanel(false, ConfigEditorSessionAccessor.getEditorUserName(), new Date());
      this.configCommentPanel.setConfigComment(Optional.of("Activated configuration."));
      mainLayout.addComponent(this.configCommentPanel);
      this.applyStatusPanel = new StatusPanel("Deployment Status");
      this.applyStatusPanel.setId("statusPanel");
      mainLayout.addComponent(this.applyStatusPanel);
      HorizontalLayout buttonLayout = new HorizontalLayout();
      buttonLayout.setSpacing(false);
      buttonLayout.setWidth("100%");
      this.retryButton = new Button("Retry");
      this.retryButton.addClickListener(event -> {
         this.showApplyButton();
         this.retryButton.setVisible(false);
      });
      this.retryButton.setVisible(false);
      this.retryButton.setId("retryButton");
      buttonLayout.addComponent(this.retryButton);
      buttonLayout.setComponentAlignment(this.retryButton, Alignment.MIDDLE_RIGHT);
      mainLayout.addComponent(buttonLayout);
      this.setContent(mainLayout);
      this.showApplyButton();
      this.configCommentPanel.focusOnCommentField();
   }

   private StatusPanel createValidationPanel() {
      StatusPanel panel = new StatusPanel("Validation");
      panel.showProgressIndicator("Validating Configuration...");
      return panel;
   }

   public void updateValidationPanel() {
      if (this.app.isValidConfiguration()) {
         this.validationPanel.showMessage(icon(GuiResources.ICON_VALIDATION_OK), "The configuration is valid" + this.forEnvironment() + ".");
      } else {
         this.validationPanel
            .showMessage(
               icon(GuiResources.ICON_VALIDATION_ERROR),
               "<b>WARNING:</b> This configuration contains validation errors" + this.forEnvironment() + "! A deployment might lead to unexpected behavior."
            );
      }
   }

   private String forEnvironment() {
      return this.app.getInstanceEnvironment().isCommonEnvironment() ? "" : " for environment <b>" + this.app.getInstanceEnvironment() + "</b>";
   }

   private void applyConfiguration() {
      this.applyInstanceConfig();
   }

   private void applyInstanceConfig() {
      this.applyStatusPanel.showProgressIndicator("Preparing...");
      this.configCommentPanel.setConfigCommentFieldReadOnly(true);
      ApplyConfigThread applyThread = new ApplyConfigThread(
         this.app,
         new ApplyConfigDialog.ApplyConfigListener(),
         this.getConfigMetadata(),
         ConfigEditorSessionAccessor.getEditorUserName(),
         this.shouldLogConfigChanges()
      );
      this.app.setPollInterval(1000);
      applyThread.start();
   }

   private boolean shouldLogConfigChanges() {
      return this.shouldLogConfigurationChanges.getAsBoolean();
   }

   private static Image icon(ThemeResource iconResource) {
      Image icon = new Image(null, iconResource);
      icon.setWidth(24.0F, Unit.PIXELS);
      icon.setHeight(24.0F, Unit.PIXELS);
      return icon;
   }

   private void showApplyButton() {
      this.configCommentPanel.setConfigCommentFieldReadOnly(false);
      this.applyStatusPanel.displayBigButton("Go Live!", GuiResources.ICON_APPLY, event -> this.applyConfiguration());
   }

   public ConfigHistoryItem getConfigMetadata() {
      return this.configCommentPanel.getHistoryItem();
   }

   public void close() {
      this.app.setPollInterval(-1);
      super.close();
      if (this.configurationStored) {
         this.app.refresh();
      }
   }

   private class ApplyConfigListener implements ApplyConfigThreadListener {
      @Override
      public void beforeApplyConfig() {
         ApplyConfigDialog.this.app.access(ApplyConfigDialog.this.app::pauseWatchingConfigFile);
      }

      @Override
      public void configurationStored() {
         ApplyConfigDialog.this.configurationStored = true;
      }

      @Override
      public void updateStatusMessage(String message) {
         ApplyConfigDialog.this.app.access(() -> ApplyConfigDialog.this.applyStatusPanel.updateStatusMessage(message));
      }

      @Override
      public void displaySuccess(String message) {
         ApplyConfigDialog.this.app.access(() -> {
            ApplyConfigDialog.this.setClosable(true);
            ApplyConfigDialog.this.applyStatusPanel.showMessage(ApplyConfigDialog.icon(GuiResources.ICON_VALIDATION_OK), message);
         });
      }

      @Override
      public void displayError(String message) {
         ApplyConfigDialog.this.app.access(() -> {
            ApplyConfigDialog.this.setClosable(true);
            ApplyConfigDialog.this.applyStatusPanel.showMessage(ApplyConfigDialog.icon(GuiResources.ICON_VALIDATION_ERROR), message);
            ApplyConfigDialog.this.retryButton.setVisible(true);
         });
      }

      @Override
      public void afterApplyConfig() {
         ApplyConfigDialog.this.app.access(() -> {
            ApplyConfigDialog.this.app.resumeWatchingConfigFile();
            ApplyConfigDialog.this.app.setPollInterval(-1);
         });
      }
   }
}
