// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.tasks.generic;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.tasks.TaskBundle;
import com.intellij.tasks.TaskManager;
import com.intellij.tasks.config.BaseRepositoryEditor;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.TextFieldWithAutoCompletion;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.net.HTTPMethod;
import com.intellij.util.ui.FormBuilder;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;

import static com.intellij.tasks.generic.GenericRepositoryUtil.*;
import static com.intellij.ui.TextFieldWithAutoCompletion.StringsCompletionProvider;

/**
 * @author Evgeny.Zakrevsky
 * @author Mikhail Golubev
 */
public class GenericRepositoryEditor<T extends GenericRepository> extends BaseRepositoryEditor<T> {

  protected EditorTextField myLoginURLText;
  private EditorTextField myTasksListURLText;
  private EditorTextField mySingleTaskURLText;
  protected JBLabel myLoginURLLabel;
  protected ComboBox myLoginMethodTypeComboBox;
  private ComboBox myTasksListMethodTypeComboBox;
  private ComboBox mySingleTaskMethodComboBox;
  private JPanel myPanel;
  private JRadioButton myXmlRadioButton;
  private JRadioButton myTextRadioButton;
  private JButton myTest2Button;
  private JRadioButton myJsonRadioButton;
  private JButton myManageTemplateVariablesButton;
  private JButton myResetToDefaultsButton;
  private JPanel myCardPanel;
  private JBLabel mySingleTaskURLLabel;
  private JBCheckBox myDownloadTasksInSeparateRequests;

  private Map<JTextField, TemplateVariable> myField2Variable;
  private final Map<JRadioButton, ResponseType> myRadio2ResponseType;

  public GenericRepositoryEditor(final Project project,
                                 final T repository,
                                 final Consumer<? super T> changeListener) {
    super(project, repository, changeListener);

    myTest2Button.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        afterTestConnection(TaskManager.getManager(project).testConnection(repository));
      }
    });

    myLoginAnonymouslyJBCheckBox.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent e) {
        loginUrlEnablingChanged();
      }
    });
    myUseHttpAuthenticationCheckBox.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent e) {
        loginUrlEnablingChanged();
      }
    });

    ActionListener radioButtonListener = new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent e) {
        singleTaskUrlEnablingChanged();
        doApply();
        selectCardByResponseType();
      }
    };
    myXmlRadioButton.addActionListener(radioButtonListener);
    myTextRadioButton.addActionListener(radioButtonListener);
    myJsonRadioButton.addActionListener(radioButtonListener);

    myLoginMethodTypeComboBox.setSelectedItem(myRepository.getLoginMethodType().toString()); //NON-NLS
    myTasksListMethodTypeComboBox.setSelectedItem(myRepository.getTasksListMethodType().toString()); //NON-NLS
    mySingleTaskMethodComboBox.setSelectedItem(myRepository.getSingleTaskMethodType().toString()); //NON-NLS

    // set default listener updating model fields
    installListener(myLoginMethodTypeComboBox);
    installListener(myTasksListMethodTypeComboBox);
    installListener(mySingleTaskMethodComboBox);
    installListener(myLoginURLText);
    installListener(myTasksListURLText);
    installListener(mySingleTaskURLText);
    installListener(myDownloadTasksInSeparateRequests);
    myTabbedPane.addTab(TaskBundle.message("server.configuration"), myPanel);

    // Put appropriate configuration components on the card panel
    ResponseHandler xmlHandler = myRepository.getResponseHandler(ResponseType.XML);
    ResponseHandler jsonHandler = myRepository.getResponseHandler(ResponseType.JSON);
    ResponseHandler textHandler = myRepository.getResponseHandler(ResponseType.TEXT);
    // Select appropriate card pane
    myCardPanel.add(xmlHandler.getConfigurationComponent(myProject), ResponseType.XML.getMimeType());
    myCardPanel.add(jsonHandler.getConfigurationComponent(myProject), ResponseType.JSON.getMimeType());
    myCardPanel.add(textHandler.getConfigurationComponent(myProject), ResponseType.TEXT.getMimeType());

    myRadio2ResponseType = new IdentityHashMap<>();
    myRadio2ResponseType.put(myJsonRadioButton, ResponseType.JSON);
    myRadio2ResponseType.put(myXmlRadioButton, ResponseType.XML);
    myRadio2ResponseType.put(myTextRadioButton, ResponseType.TEXT);

    myManageTemplateVariablesButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent e) {
        final ManageTemplateVariablesDialog dialog = new ManageTemplateVariablesDialog(myManageTemplateVariablesButton);
        dialog.setTemplateVariables(myRepository.getAllTemplateVariables());
        if (dialog.showAndGet()) {
          myRepository.setTemplateVariables(ContainerUtil.filter(dialog.getTemplateVariables(), variable -> !variable.isReadOnly()));
          myCustomPanel.removeAll();
          myCustomPanel.add(createCustomPanel());
          //myCustomPanel.repaint();
          myTabbedPane.getComponentAt(0).repaint();

          //myLoginURLText = createEditorFieldWithPlaceholderCompletion(myRepository.getLoginUrl());
          List<String> placeholders = createPlaceholdersList(myRepository);
          ((TextFieldWithAutoCompletion)myLoginURLText).setVariants(placeholders);
          ((TextFieldWithAutoCompletion)myTasksListURLText).setVariants(concat(placeholders, "{max}", "{since}"));
          ((TextFieldWithAutoCompletion)mySingleTaskURLText).setVariants(concat(placeholders, "{id}"));
          myPanel.repaint();
        }
      }
    });

    myResetToDefaultsButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent e) {
        myRepository.resetToDefaults();
        // TODO: look closely
        reset(myRepository.clone());
      }
    });

    selectRadioButtonByResponseType();
    selectCardByResponseType();
    loginUrlEnablingChanged();
    singleTaskUrlEnablingChanged();
    myDownloadTasksInSeparateRequests.setSelected(myRepository.getDownloadTasksInSeparateRequests());
  }

  private void singleTaskUrlEnablingChanged() {
    boolean enabled = !myTextRadioButton.isSelected();
    // single task URL doesn't make sense when legacy regex handler is used
    mySingleTaskURLText.setEnabled(enabled);
    mySingleTaskMethodComboBox.setEnabled(enabled);
    mySingleTaskURLLabel.setEnabled(enabled);
  }

  protected void loginUrlEnablingChanged() {
    boolean enabled = !myLoginAnonymouslyJBCheckBox.isSelected() && !myUseHttpAuthenticationCheckBox.isSelected();
    myLoginURLLabel.setEnabled(enabled);
    myLoginURLText.setEnabled(enabled);
    myLoginMethodTypeComboBox.setEnabled(enabled);
  }

  @Override
  protected @Nullable JComponent createCustomPanel() {
    myField2Variable = new IdentityHashMap<>();
    FormBuilder builder = FormBuilder.createFormBuilder();
    for (final TemplateVariable variable : myRepository.getTemplateVariables()) {
      if (variable.isShownOnFirstTab()) {
        JTextField field = variable.isHidden() ? new JPasswordField(variable.getValue()) : new JTextField(variable.getValue());
        myField2Variable.put(field, variable);
        installListener(field);
        JBLabel label = new JBLabel(prettifyVariableName(variable.getName()) + ":", SwingConstants.RIGHT);
        label.setAnchor(getAnchor());
        builder.addLabeledComponent(label, field);
      }
    }
    return builder.getPanel();
  }

  protected void reset(final GenericRepository clone) {
    myLoginURLText.setText(clone.getLoginUrl());
    myTasksListURLText.setText(clone.getTasksListUrl());
    mySingleTaskURLText.setText(clone.getSingleTaskUrl());
    //myTaskPatternText.setText(clone.getTaskPattern());
    myLoginMethodTypeComboBox.setSelectedItem(clone.getLoginMethodType());
    myTasksListMethodTypeComboBox.setSelectedItem(clone.getTasksListMethodType());
    mySingleTaskMethodComboBox.setSelectedItem(clone.getSingleTaskMethodType());
    selectRadioButtonByResponseType();
    selectCardByResponseType();
    loginUrlEnablingChanged();
    myDownloadTasksInSeparateRequests.setSelected(myRepository.getDownloadTasksInSeparateRequests());
  }

  private void selectRadioButtonByResponseType() {
    for (Map.Entry<JRadioButton, ResponseType> entry : myRadio2ResponseType.entrySet()) {
      if (entry.getValue() == myRepository.getResponseType()) {
        entry.getKey().setSelected(true);
      }
    }
  }

  private void selectCardByResponseType() {
    CardLayout cardLayout = (CardLayout) myCardPanel.getLayout();
    cardLayout.show(myCardPanel, myRepository.getResponseType().getMimeType());
  }

  @Override
  public void apply() {
    myRepository.setLoginUrl(myLoginURLText.getText());
    myRepository.setTasksListUrl(myTasksListURLText.getText());
    myRepository.setSingleTaskUrl(mySingleTaskURLText.getText());

    myRepository.setLoginMethodType(HTTPMethod.valueOf((String)myLoginMethodTypeComboBox.getSelectedItem()));
    myRepository.setTasksListMethodType(HTTPMethod.valueOf((String)myTasksListMethodTypeComboBox.getSelectedItem()));
    myRepository.setSingleTaskMethodType(HTTPMethod.valueOf((String)mySingleTaskMethodComboBox.getSelectedItem()));

    myRepository.setDownloadTasksInSeparateRequests(myDownloadTasksInSeparateRequests.isSelected());
   for (Map.Entry<JTextField, TemplateVariable> entry : myField2Variable.entrySet()) {
      TemplateVariable variable = entry.getValue();
      JTextField field = entry.getKey();
      variable.setValue(field.getText());
    }
    for (Map.Entry<JRadioButton, ResponseType> entry : myRadio2ResponseType.entrySet()) {
      if (entry.getKey().isSelected()) {
        myRepository.setResponseType(entry.getValue());
      }
    }
    super.apply();
  }

  private void createUIComponents() {
    List<String> placeholders = createPlaceholdersList(myRepository);
    myLoginURLText = createTextFieldWithCompletion(myRepository.getLoginUrl(), placeholders);
    myTasksListURLText = createTextFieldWithCompletion(myRepository.getTasksListUrl(), concat(placeholders, "{max}", "{since}"));
    mySingleTaskURLText = createTextFieldWithCompletion(myRepository.getSingleTaskUrl(), concat(placeholders, "{id}"));
  }

  private TextFieldWithAutoCompletion<String> createTextFieldWithCompletion(String text, final List<String> variants) {
    final StringsCompletionProvider provider = new StringsCompletionProvider(variants, null) {
      @Override
      public @Nullable String getPrefix(@NotNull String text, int offset) {
        final int i = text.lastIndexOf('{', offset - 1);
        if (i < 0) {
          return "";
        }
        return text.substring(i, offset);
      }
    };
    return new TextFieldWithAutoCompletion<>(myProject, provider, true, text);
  }

  @Override
  public void setAnchor(@Nullable JComponent anchor) {
    super.setAnchor(anchor);
    List<JBLabel> labels = UIUtil.findComponentsOfType(myCustomPanel, JBLabel.class);
    for (JBLabel label : labels) {
      label.setAnchor(anchor);
    }
  }
}
