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

import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.plugin.api.config.ConfigurationEnvironment;
import com.airlock.iam.plugin.api.config.ConfigurationVariable;
import com.airlock.iam.plugin.api.property.PropertyConfiguration;
import com.airlock.iam.plugin.api.util.PropertyDisplayNameReader;
import com.airlock.iam.plugin.configeditor.ConfigEditorApplication;
import com.airlock.iam.plugin.framework.application.configuration.MutablePluginConfiguration;
import com.vaadin.event.selection.SelectionEvent;
import com.vaadin.server.Sizeable.Unit;
import com.vaadin.shared.data.sort.SortDirection;
import com.vaadin.ui.Component;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Grid.Column;
import com.vaadin.ui.Grid.SelectionMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
import java.util.stream.Stream;

final class ConfigurationVariablesPanel implements PanelContainer {
   private final Supplier<Set<MutablePluginConfiguration>> configsProvider;
   private final BooleanSupplier environmentsEnabled;
   private final Panel panel = new Panel();
   private final List<ConfigurationVariablesPanel.PropertyInfo> data = new ArrayList<>();
   private final Grid<ConfigurationVariablesPanel.PropertyInfo> grid = new Grid();
   private final Label noVariablesMessage = new Label("There are no variables defined in the current configuration.");

   public ConfigurationVariablesPanel(Supplier<Set<MutablePluginConfiguration>> configsProvider, BooleanSupplier environmentsEnabled) {
      this.configsProvider = configsProvider;
      this.environmentsEnabled = environmentsEnabled;
      this.configureGrid();
      this.panel.addStyleName("minimal");
      this.panel.setContent(this.buildLayout());
      this.panel.setId("variablesPanel");
      this.panel.setSizeFull();
   }

   private VerticalLayout buildLayout() {
      VerticalLayout vl = new VerticalLayout();
      vl.addComponent(this.grid);
      this.noVariablesMessage.setWidth(100.0F, Unit.PERCENTAGE);
      vl.addComponent(this.noVariablesMessage);
      vl.setSizeFull();
      return vl;
   }

   private void configureGrid() {
      this.grid.setSelectionMode(SelectionMode.SINGLE);
      this.grid.setSizeFull();
      this.grid.setColumnReorderingAllowed(true);
      this.grid.setItems(this.data);
      this.setupGridColumns();
      this.grid.addSelectionListener(ConfigurationVariablesPanel::selectionChange);
      this.grid.setId("variablesTable");
      this.grid.setVisible(false);
      this.grid.setStyleGenerator(item -> "with-hand");
   }

   private void setupGridColumns() {
      this.grid.removeAllColumns();
      Column<ConfigurationVariablesPanel.PropertyInfo, String> variableColumn = this.grid
         .addColumn(p -> ((ConfigurationVariable)p.property().getVariable().orElseThrow()).name())
         .setCaption("Variable");
      this.grid.addColumn(p -> p.plugin().getClassNameAndDisplayName()).setCaption("Plugin");
      this.grid.addColumn(p -> PropertyDisplayNameReader.propertyDisplayName(p.plugin(), p.property().getName())).setCaption("Property");
      if (this.environmentsEnabled.getAsBoolean()) {
         this.grid.addColumn(p -> p.env().toString()).setCaption("Environment");
      }

      this.grid.addColumn(p -> p.context().toString()).setCaption("Context");
      this.grid.sort(variableColumn, SortDirection.ASCENDING);
   }

   @Override
   public void update() {
      this.data.clear();
      this.configsProvider.get().stream().flatMap(ConfigurationVariablesPanel::collectPropertiesWithVariable).forEach(this.data::add);
      if (this.data.isEmpty()) {
         this.noVariablesMessage.setVisible(true);
         this.grid.setVisible(false);
      } else {
         this.noVariablesMessage.setVisible(false);
         this.grid.setVisible(true);
         this.setupGridColumns();
         this.grid.getDataProvider().refreshAll();
      }
   }

   private static Stream<ConfigurationVariablesPanel.PropertyInfo> collectPropertiesWithVariable(MutablePluginConfiguration plugin) {
      List<ConfigurationVariablesPanel.PropertyInfo> properties = new ArrayList<>();

      for (ConfigurationEnvironment env : plugin.getLocallyUsedConfigurationEnvironments()) {
         for (ConfigurationContext context : plugin.getUsedConfigurationContexts(env)) {
            plugin.getDirectlyConfiguredProperties(env, context)
               .stream()
               .filter(PropertyConfiguration::hasVariable)
               .map(property -> new ConfigurationVariablesPanel.PropertyInfo(property, context, env, plugin))
               .forEach(properties::add);
         }
      }

      return properties.stream();
   }

   private static void selectionChange(SelectionEvent<ConfigurationVariablesPanel.PropertyInfo> event) {
      event.getFirstSelectedItem().ifPresent(ConfigurationVariablesPanel::navigateToSelectedProperty);
   }

   private static void navigateToSelectedProperty(ConfigurationVariablesPanel.PropertyInfo property) {
      ConfigEditorApplication application = (ConfigEditorApplication)UI.getCurrent();
      application.getEnvironmentSelection().setSelectedEnvironment(property.env());
      application.setSelectedContext(property.context());
      application.selectConfigurationField(property.plugin(), property.property());
   }

   @Override
   public Component getContent() {
      return this.panel;
   }

   private record PropertyInfo(PropertyConfiguration property, ConfigurationContext context, ConfigurationEnvironment env, MutablePluginConfiguration plugin) {
   }
}
