// 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.redmine;

import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.tasks.TaskBundle;
import com.intellij.tasks.config.BaseRepositoryEditor;
import com.intellij.tasks.impl.TaskUiUtil;
import com.intellij.tasks.redmine.model.RedmineProject;
import com.intellij.ui.SimpleListCellRenderer;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.Consumer;
import com.intellij.util.containers.Stack;
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.util.ArrayList;
import java.util.List;

/**
 * @author Mikhail Golubev
 * @author Dennis.Ushakov
 */
public class RedmineRepositoryEditor extends BaseRepositoryEditor<RedmineRepository> {
  private ComboBox<RedmineProjectItem> myProjectCombo;
  private JTextField myAPIKey;
  private JCheckBox myAllAssigneesCheckBox;
  private JBLabel myProjectLabel;
  private JBLabel myAPIKeyLabel;

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

    myTestButton.setEnabled(myRepository.isConfigured());
    myAPIKey.setText(repository.getAPIKey());
    myAllAssigneesCheckBox.setSelected(!repository.isAssignedToMe());

    installListener(myProjectCombo);
    installListener(myAPIKey);
    installListener(myAllAssigneesCheckBox);

    toggleCredentialsVisibility();

    UIUtil.invokeLaterIfNeeded(() -> initialize());
  }

  @Override
  protected void afterTestConnection(boolean connectionSuccessful) {
    if (connectionSuccessful) {
      new FetchProjectsTask().queue();
    }
    else {
      myProjectCombo.removeAllItems();
    }
  }

  private void initialize() {
    final RedmineProject currentProject = myRepository.getCurrentProject();
    if (currentProject != null && myRepository.isConfigured()) {
      new FetchProjectsTask().queue();
    }
    else {
      myProjectCombo.removeAllItems();
    }
  }

  @Override
  public void apply() {
    super.apply();
    RedmineProjectItem selected = (RedmineProjectItem)myProjectCombo.getSelectedItem();
    myRepository.setCurrentProject(selected != null ? selected.myProject : null);
    myRepository.setAPIKey(myAPIKey.getText().trim());
    myRepository.setAssignedToMe(!myAllAssigneesCheckBox.isSelected());
    myTestButton.setEnabled(myRepository.isConfigured());
    toggleCredentialsVisibility();
  }

  private void toggleCredentialsVisibility() {
    myPasswordLabel.setVisible(myRepository.isUseHttpAuthentication());
    myPasswordText.setVisible(myRepository.isUseHttpAuthentication());

    myUsernameLabel.setVisible(myRepository.isUseHttpAuthentication());
    myUserNameText.setVisible(myRepository.isUseHttpAuthentication());

    myAPIKeyLabel.setVisible(!myRepository.isUseHttpAuthentication());
    myAPIKey.setVisible(!myRepository.isUseHttpAuthentication());
  }

  @Override
  protected @Nullable JComponent createCustomPanel() {
    myProjectLabel = new JBLabel(TaskBundle.message("label.project"), SwingConstants.RIGHT);
    myProjectCombo = new ComboBox<>(300);
    //myProjectCombo.setRenderer(new TaskUiUtil.SimpleComboBoxRenderer("Set URL and password/token first"));
    myProjectCombo.setRenderer(SimpleListCellRenderer.create(TaskBundle.message("label.set.url.password.token.first"), value -> {
      if (myProjectCombo.isPopupVisible()) {
        //if (value.myLevel == 0 && value.myProject != RedmineRepository.UNSPECIFIED_PROJECT) {
        //setFont(UIUtil.getListFont().deriveFont(Font.BOLD));
        //}
        return StringUtil.repeat("   ", value.myLevel) + value.myProject.getName();
      }
      else {
        // Do not indent selected project
        return value.myProject.getName();
      }
    }));

    myAPIKeyLabel = new JBLabel(TaskBundle.message("label.api.token"), SwingConstants.RIGHT);
    myAPIKey = new JPasswordField();

    myAllAssigneesCheckBox = new JBCheckBox(TaskBundle.message("checkbox.include.issues.not.assigned.to.me"));
    return FormBuilder.createFormBuilder()
      .addLabeledComponent(myAPIKeyLabel, myAPIKey)
      .addLabeledComponent(myProjectLabel, myProjectCombo)
      .addComponentToRightColumn(myAllAssigneesCheckBox)
      .getPanel();
  }

  @Override
  public void setAnchor(final @Nullable JComponent anchor) {
    super.setAnchor(anchor);
    myProjectLabel.setAnchor(anchor);
    myAPIKeyLabel.setAnchor(anchor);
  }

  private static class RedmineProjectItem {
    public final RedmineProject myProject;
    public final int myLevel;

    RedmineProjectItem(@NotNull RedmineProject project, int level) {
      myProject = project;
      myLevel = level;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null) return false;

      if (o instanceof RedmineProject) {
        return myProject.equals(o);
      }
      else if (o instanceof RedmineProjectItem) {
        return myProject.equals(((RedmineProjectItem)o).myProject);
      }
      return false;
    }

    @Override
    public int hashCode() {
      return myProject.hashCode();
    }
  }

  private final class FetchProjectsTask extends TaskUiUtil.ComboBoxUpdater<RedmineProjectItem> {
    private FetchProjectsTask() {
      super(RedmineRepositoryEditor.this.myProject, TaskBundle.message("progress.title.downloading.redmine.projects"), myProjectCombo);
    }

    @Override
    public RedmineProjectItem getExtraItem() {
      return new RedmineProjectItem(RedmineRepository.UNSPECIFIED_PROJECT, 0);
    }

    @Override
    public @Nullable RedmineProjectItem getSelectedItem() {
      RedmineProject currentProject = myRepository.getCurrentProject();
      return currentProject != null ? new RedmineProjectItem(currentProject, -1) : null;
    }

    @Override
    protected @NotNull List<RedmineProjectItem> fetch(@NotNull ProgressIndicator indicator) throws Exception {
      // Seems that Redmine always return its project hierarchy in DFS order.
      // So it's easy to find level of each project using stack of parents.
      Stack<RedmineProject> parents = new Stack<>();
      List<RedmineProjectItem> items = new ArrayList<>();
      for (RedmineProject project : myRepository.fetchProjects()) {
        RedmineProject parentProject = project.getParent();
        if (parentProject == null) {
          items.add(new RedmineProjectItem(project, 0));
          parents.clear();
        }
        else {
          while (!parents.isEmpty() && !parents.peek().equals(parentProject)) {
            parents.pop();
          }
          items.add(new RedmineProjectItem(project, parents.size()));
        }
        parents.push(project);
      }
      return items;
    }
  }
}
