package com.sasslint.settings;

import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.execution.ExecutionException;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiManager;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.HyperlinkLabel;
import com.intellij.ui.TextFieldWithHistory;
import com.intellij.ui.TextFieldWithHistoryWithBrowseButton;
import com.intellij.util.NotNullProducer;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.SwingHelper;
import com.sasslint.SassLintProjectComponent;
import com.sasslint.cli.SassLintFinder;
import com.sasslint.cli.SassLintRunner;
import com.wix.nodejs.NodeDetectionUtil;
import com.wix.settings.ValidationUtils;
import com.wix.settings.Validator;
import com.wix.ui.PackagesNotificationPanel;
import com.wix.utils.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.util.List;

public class SassLintSettingsPage implements Configurable {
    public static final String FIX_IT = "Fix it";
    public static final String HOW_TO_USE_SASS_LINT = "How to Use Sass Lint";
    public static final String HOW_TO_USE_LINK = "https://github.com/idok/sass-lint-plugin";
    protected final Project project;

    private JCheckBox pluginEnabledCheckbox;
    private JPanel panel;
    private JPanel errorPanel;
    private TextFieldWithHistoryWithBrowseButton sasslintBinField;
    private TextFieldWithHistoryWithBrowseButton nodeInterpreterField;
    private TextFieldWithHistoryWithBrowseButton sassLintConfigFile;
    private JRadioButton searchConfigRadioButton;
    private JRadioButton useProjectConfigRadioButton;
    private HyperlinkLabel usageLink;
    private JLabel sassLintConfigFilePathLabel;
    private JLabel pathToSasslintBinLabel;
    private JLabel nodeInterpreterLabel;
    private JLabel versionLabel;
    private final PackagesNotificationPanel packagesNotificationPanel;

    private final JComponent[] comps = {
        sassLintConfigFile,
        searchConfigRadioButton,
        useProjectConfigRadioButton,
        sasslintBinField,
        nodeInterpreterField,
        sassLintConfigFilePathLabel,
        pathToSasslintBinLabel,
        nodeInterpreterLabel
    };

    public SassLintSettingsPage(@NotNull final Project project) {
        this.project = project;
        configLintBinField();
        configConfigFileField();
        configNodeField();
        useProjectConfigRadioButton.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                sassLintConfigFile.setEnabled(e.getStateChange() == ItemEvent.SELECTED);
//                System.out.println("useProjectConfigRadioButton: " + (e.getStateChange() == ItemEvent.SELECTED ? "checked" : "unchecked"));
            }
        });
        pluginEnabledCheckbox.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                boolean enabled = e.getStateChange() == ItemEvent.SELECTED;
                setEnabledState(enabled);
            }
        });

        this.packagesNotificationPanel = new PackagesNotificationPanel(project);
//        GridConstraints gridConstraints = new GridConstraints(5, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
//                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW,
//                null, new Dimension(250, 150), null);
        errorPanel.add(this.packagesNotificationPanel.getComponent(), BorderLayout.CENTER);

        DocumentAdapter docAdp = new DocumentAdapter() {
            protected void textChanged(DocumentEvent e) {
                updateLaterInEDT();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                super.changedUpdate(e);
//                System.out.println("changedUpdate " + rtBinField.getText());
            }

            @Override
            public void insertUpdate(DocumentEvent e) {
                super.insertUpdate(e);
//                System.out.println("insertUpdate " + rtBinField.getText());
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
//                super.removeUpdate(e);
//                e.getDocument().
//                System.out.println("removeUpdate ");
            }
        };

        sasslintBinField.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp);
        sassLintConfigFile.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp);
        nodeInterpreterField.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp);
//        customRulesPathField.getDocument().addDocumentListener(docAdp);
    }

    private File getProjectPath() {
        return project.getBasePath() == null ? null : new File(project.getBasePath());
    }

    private void updateLaterInEDT() {
        UIUtil.invokeLaterIfNeeded(new Runnable() {
            public void run() {
                SassLintSettingsPage.this.update();
            }
        });
    }

    private void update() {
        ApplicationManager.getApplication().assertIsDispatchThread();
        validate();
    }

    private void setEnabledState(boolean enabled) {
//        sassLintConfigFile.setEnabled(enabled);
////        customRulesPathField.setEnabled(enabled);
//        searchConfigRadioButton.setEnabled(enabled);
//        useProjectConfigRadioButton.setEnabled(enabled);
//        sasslintBinField.setEnabled(enabled);
//        nodeInterpreterField.setEnabled(enabled);
//        sassLintConfigFilePathLabel.setEnabled(enabled);
////        rulesDirectoryLabel.setEnabled(enabled);
//        pathToSasslintBinLabel.setEnabled(enabled);
//        nodeInterpreterLabel.setEnabled(enabled);

//        private JComponent[] comps = {sasslintBinField};
        for (JComponent c : comps) {
            c.setEnabled(enabled);
        }
    }

    private void validateField(Validator validator, TextFieldWithHistoryWithBrowseButton field, boolean allowEmpty, String message) {
        if (!ValidationUtils.validatePath(project, field.getChildComponent().getText(), allowEmpty)) {
            validator.add(field.getChildComponent().getTextEditor(), message, FIX_IT);
        }
    }

    private void validate() {
        if (!pluginEnabledCheckbox.isSelected()) {
            return;
        }
        Validator validator = new Validator();
        validateField(validator, sasslintBinField, false, "Path to sass-lint is invalid {{LINK}}");
        validateField(validator, sassLintConfigFile, true, "Path to config file is invalid {{LINK}}"); //Please correct path to
        validateField(validator, nodeInterpreterField, false, "Path to node interpreter is invalid {{LINK}}");
        if (!validator.hasErrors()) {
            getVersion();
        }
        packagesNotificationPanel.processErrors(validator);
    }

    private SassLintRunner.SassLintSettings settings;

    private void getVersion() {
        if (settings != null &&
                areEqual(nodeInterpreterField, settings.node) &&
                areEqual(sasslintBinField, settings.executablePath) &&
                settings.cwd.equals(project.getBasePath())
                ) {
            return;
        }
        settings = new SassLintRunner.SassLintSettings();
        settings.node = nodeInterpreterField.getChildComponent().getText();
        settings.executablePath = sasslintBinField.getChildComponent().getText();
        settings.cwd = project.getBasePath();
        try {
            String version = SassLintRunner.runVersion(settings);
            versionLabel.setText(version.trim());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static TextFieldWithHistory configWithDefaults(TextFieldWithHistoryWithBrowseButton field) {
        TextFieldWithHistory textFieldWithHistory = field.getChildComponent();
        textFieldWithHistory.setHistorySize(-1);
        textFieldWithHistory.setMinimumAndPreferredWidth(0);
        return textFieldWithHistory;
    }

    private void configLintBinField() {
        configWithDefaults(sasslintBinField);
        SwingHelper.addHistoryOnExpansion(sasslintBinField.getChildComponent(), new NotNullProducer<List<String>>() {
            @NotNull
            public List<String> produce() {
                List<File> newFiles = SassLintFinder.searchForSassLintExe(getProjectPath());
                return FileUtils.toAbsolutePath(newFiles);
            }
        });
        SwingHelper.installFileCompletionAndBrowseDialog(project, sasslintBinField, "Select Sass Lint Exe", FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor());
    }

    private void configConfigFileField() {
        TextFieldWithHistory textFieldWithHistory = configWithDefaults(sassLintConfigFile);
        SwingHelper.addHistoryOnExpansion(textFieldWithHistory, new NotNullProducer<List<String>>() {
            @NotNull
            public List<String> produce() {
                return SassLintFinder.searchForConfigFiles(getProjectPath());
            }
        });
        SwingHelper.installFileCompletionAndBrowseDialog(project, sassLintConfigFile, "Select Sass Lint Config", FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor());
    }

    private void configNodeField() {
        TextFieldWithHistory textFieldWithHistory = configWithDefaults(nodeInterpreterField);
        SwingHelper.addHistoryOnExpansion(textFieldWithHistory, new NotNullProducer<List<String>>() {
            @NotNull
            public List<String> produce() {
                List<File> newFiles = NodeDetectionUtil.listAllPossibleNodeInterpreters();
                return FileUtils.toAbsolutePath(newFiles);
            }
        });
        SwingHelper.installFileCompletionAndBrowseDialog(project, nodeInterpreterField, "Select Node Interpreter", FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor());
    }

    @Nls
    @Override
    public String getDisplayName() {
        return "Sass Lint";
    }

    @Nullable
    @Override
    public String getHelpTopic() {
        return null;
    }

    @Nullable
    @Override
    public JComponent createComponent() {
        loadSettings();
        return panel;
    }

    private static boolean areEqual(TextFieldWithHistoryWithBrowseButton field, String value) {
        return field.getChildComponent().getText().equals(value);
    }

    @Override
    public boolean isModified() {
        Settings s = getSettings();
        return pluginEnabledCheckbox.isSelected() != s.pluginEnabled ||
                !areEqual(sasslintBinField, s.lintExecutable) ||
                !areEqual(nodeInterpreterField, s.nodeInterpreter) ||
//                !customRulesPathField.getText().equals(s.rulesPath) ||
                !getConfigFile().equals(s.configFile);
    }

    private String getConfigFile() {
        return useProjectConfigRadioButton.isSelected() ? sassLintConfigFile.getChildComponent().getText() : "";
    }

    @Override
    public void apply() throws ConfigurationException {
        saveSettings();
        PsiManager.getInstance(project).dropResolveCaches();
    }

    private void saveSettings() {
        Settings settings = getSettings();
        settings.pluginEnabled = pluginEnabledCheckbox.isSelected();
        settings.lintExecutable = sasslintBinField.getChildComponent().getText();
        settings.nodeInterpreter = nodeInterpreterField.getChildComponent().getText();
        settings.configFile = getConfigFile();
        if (!project.isDefault()) {
            project.getComponent(SassLintProjectComponent.class).validateSettings();
            DaemonCodeAnalyzer.getInstance(project).restart();
        }
    }

    private void loadSettings() {
        Settings settings = getSettings();
        pluginEnabledCheckbox.setSelected(settings.pluginEnabled);
        sasslintBinField.getChildComponent().setText(settings.lintExecutable);
        sassLintConfigFile.getChildComponent().setText(settings.configFile);
        nodeInterpreterField.getChildComponent().setText(settings.nodeInterpreter);
        useProjectConfigRadioButton.setSelected(StringUtils.isNotEmpty(settings.configFile));
        searchConfigRadioButton.setSelected(StringUtils.isEmpty(settings.configFile));
        sassLintConfigFile.setEnabled(useProjectConfigRadioButton.isSelected());
        setEnabledState(settings.pluginEnabled);
    }

    @Override
    public void reset() {
        loadSettings();
    }

    @Override
    public void disposeUIResources() {
    }

    protected Settings getSettings() {
        return Settings.getInstance(project);
    }

    private void createUIComponents() {
        // TODO: place custom component creation code here
        usageLink = SwingHelper.createWebHyperlink(HOW_TO_USE_SASS_LINT, HOW_TO_USE_LINK);
    }
}