# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright 2016 Continuum Analytics, Inc.
#
# May be copied and distributed freely only as part of an Anaconda or
# Miniconda installation.
# -----------------------------------------------------------------------------
"""Environment creation, import, deletion dialogs."""

# yapf: disable

# Standard library imports
import os
import sys

# Third party imports
from qtpy.compat import getopenfilename
from qtpy.QtCore import QRegExp
from qtpy.QtGui import QRegExpValidator
from qtpy.QtWidgets import (QGridLayout, QHBoxLayout, QLabel, QLineEdit,
                            QVBoxLayout)
import yaml

# Local imports
from anaconda_navigator.widgets import (ButtonDanger, ButtonNormal,
                                        ButtonPrimary, CheckBoxBase,
                                        ComboBoxBase, SpacerHorizontal,
                                        SpacerVertical)
from anaconda_navigator.widgets.dialogs import DialogBase

# yapf: enable

RE_ENV_NAME = '^[A-Za-z][A-Za-z0-9_-]{0,100}$'


def get_regex_validator():
    """Helper that creates a regex validator."""
    regex = QRegExp(RE_ENV_NAME)
    return QRegExpValidator(regex)


class ImportDialog(DialogBase):
    """Import environment from environment specification dialog."""

    CONDA_ENV_FILES = 'Conda environment files (*.yaml *.yml)'
    CONDA_SPEC_FILES = 'Conda explicit specification files (*.txt)'
    PIP_REQUIREMENT_FILES = 'Pip requirement files (*.txt)'

    def __init__(self, parent=None, environments=None):
        """Import environment from environment specification dialog."""
        super(ImportDialog, self).__init__(parent=parent)

        self.environments = environments if environments else []
        self.selected_file_filter = None
        self.path = None

        # Widgets
        self.label_name = QLabel("Environment name")
        self.label_path = QLabel("Specification File")
        self.text_name = QLineEdit()
        self.text_path = QLineEdit()
        self.button_path = ButtonNormal("")
        self.button_cancel = ButtonNormal('Cancel')
        self.button_ok = ButtonPrimary('Import')

        # Widgets setup
        self.button_path.setObjectName('import')
        self.button_ok.setDefault(True)
        self.text_path.setPlaceholderText("File to import from")
        self.text_name.setPlaceholderText("New environment name")
        self.setMinimumWidth(380)
        self.setWindowTitle("Import new environment")
        self.text_name.setValidator(get_regex_validator())

        # Layouts
        layout_infile = QHBoxLayout()
        layout_infile.addWidget(self.text_path)
        layout_infile.addWidget(SpacerHorizontal())
        layout_infile.addWidget(self.button_path)

        layout_grid = QGridLayout()
        layout_grid.addWidget(self.label_name, 0, 0)
        layout_grid.addWidget(SpacerHorizontal(), 0, 1)
        layout_grid.addWidget(self.text_name, 0, 2)
        layout_grid.addWidget(SpacerVertical(), 1, 0)
        layout_grid.addWidget(self.label_path, 2, 0)
        layout_grid.addWidget(SpacerHorizontal(), 2, 1)
        layout_grid.addLayout(layout_infile, 2, 2)

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_cancel)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_ok)

        layout = QVBoxLayout()
        layout.addLayout(layout_grid)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)

        self.setLayout(layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.button_path.clicked.connect(self.choose)
        self.text_path.textChanged.connect(self.refresh)
        self.text_name.textChanged.connect(self.refresh)

        # Setup
        self.refresh()

    def choose(self):
        """Display file dialog to select environment specification."""
        path, selected_filter = getopenfilename(
            caption="Import Environment",
            basedir=os.path.expanduser('~'),
            parent=None,
            filters="{0};;{1};;{2}".format(
                self.CONDA_ENV_FILES, self.CONDA_SPEC_FILES,
                self.PIP_REQUIREMENT_FILES
            )
        )

        if path:
            name = self.text_name.text()
            self.selected_file_filter = selected_filter
            self.text_path.setText(path)
            self.refresh(path)

            # Try to get the name key of the file if an environment.yaml file
            if selected_filter == self.CONDA_ENV_FILES:
                try:
                    with open(path, 'r') as f:
                        raw = f.read()
                    data = yaml.load(raw)
                    name = data.get('name', name)
                except Exception:
                    pass
            self.text_name.setText(name)

    def refresh(self, text=''):
        """Update the status of buttons based on combobox selection."""
        text = self.text_name.text()
        self.path = self.text_path.text()

        if (text and self.path and os.path.exists(self.path) and
                text not in self.environments):
            self.button_ok.setDisabled(False)
            self.button_ok.setDefault(True)
        else:
            self.button_ok.setDisabled(True)
            self.button_cancel.setDefault(True)


class CreateDialog(DialogBase):
    """Create new environment dialog."""

    def __init__(self, parent=None, path=None, environments=None):
        """Create new environment dialog."""
        super(CreateDialog, self).__init__(parent=parent)

        self.environments = environments

        # Widgets
        self.label_name = QLabel("Environment name")
        self.check_python = CheckBoxBase("Python")
        self.check_r = CheckBoxBase('R')
        self.label_version = QLabel("Python version")
        self.text_name = QLineEdit()
        self.combo_version = ComboBoxBase()
        self.button_ok = ButtonPrimary('Create')
        self.button_cancel = ButtonNormal('Cancel')

        # Widgets setup
        self.check_python.setChecked(True)
        self.check_r.setChecked(False)
        self.text_name.setPlaceholderText("New environment name")
        self.setMinimumWidth(380)
        self.setWindowTitle("Create new environment")
        self.text_name.setValidator(get_regex_validator())

        # Supported set of python versions
        versions = ['2.7', '3.5', '3.6']
        now = "{}.{}".format(sys.version_info.major, sys.version_info.minor)
        if now not in versions:
            # Guard against non-standard version, or the coming 3.6
            versions.append(now)
            versions.sort()
        versions = list(reversed(versions))
        self.combo_version.addItems(versions)
        self.combo_version.setCurrentIndex(versions.index(now))

        # Layouts
        check = QHBoxLayout()
        check.addWidget(self.check_python)
        check.addWidget(self.check_r)
        check.addStretch()
        grid = QGridLayout()
        grid.addWidget(self.label_name, 0, 0)
        grid.addWidget(SpacerHorizontal(), 0, 1)
        grid.addWidget(self.text_name, 0, 2)
        grid.addWidget(SpacerVertical(), 1, 0)
        grid.addLayout(check, 2, 2)
        grid.addWidget(SpacerVertical(), 3, 0)
        grid.addWidget(self.label_version, 4, 0)
        grid.addWidget(SpacerHorizontal(), 4, 1)
        grid.addWidget(self.combo_version, 4, 2)
        grid.addWidget(SpacerVertical(), 5, 0)

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_cancel)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_ok)

        main_layout = QVBoxLayout()
        main_layout.addLayout(grid)
        main_layout.addWidget(SpacerVertical())
        main_layout.addWidget(SpacerVertical())
        main_layout.addLayout(layout_buttons)

        self.setLayout(main_layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.text_name.textChanged.connect(self.refresh)
        self.check_python.stateChanged.connect(self.refresh)
        self.check_r.stateChanged.connect(self.refresh)

        # Setup
        self.refresh()

    def refresh(self, text=''):
        """Update status of buttons based on combobox selection."""
        text = self.text_name.text().strip()
        if self.environments:
            check = self.check_r.checkState() or self.check_python.checkState()
            if check and is_valid_env_name(text, self.environments):
                self.button_ok.setDisabled(False)
            else:
                self.button_ok.setDisabled(True)
        self.combo_version.setEnabled(self.check_python.checkState())


class CloneDialog(DialogBase):
    """Clone environment dialog."""

    def __init__(self, parent=None, environments=None):
        """Clone environment dialog."""
        super(CloneDialog, self).__init__(parent=parent)
        self.environments = environments

        # Widgets
        self.label_name = QLabel("Environment name")
        self.text_name = QLineEdit()
        self.button_ok = ButtonPrimary('Clone')
        self.button_cancel = ButtonNormal('Cancel')

        # Widget setup
        self.setMinimumWidth(380)
        self.setWindowTitle("Clone environment")
        self.text_name.setPlaceholderText("New environment name")
        self.text_name.setValidator(get_regex_validator())

        # Layouts
        layout_text = QHBoxLayout()
        layout_text.addWidget(self.label_name)
        layout_text.addWidget(SpacerHorizontal())
        layout_text.addWidget(self.text_name)

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_cancel)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_ok)

        layout = QVBoxLayout()
        layout.addLayout(layout_text)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)

        self.setLayout(layout)

        # Signals
        self.text_name.textChanged.connect(self.refresh)
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)

    def refresh(self, text=''):
        """Update status of buttons based on combobox selection."""
        text = self.text_name.text().strip()
        if self.environments:
            self.button_ok.setDisabled(
                not is_valid_env_name(text, self.environments)
            )


class RemoveDialog(DialogBase):
    """Remove existing environment dialog."""

    def __init__(self, parent=None, environment=None):
        """Remove existing environment dialog."""
        super(RemoveDialog, self).__init__(parent=parent)

        # Widgets
        self.button_cancel = ButtonNormal('Cancel')
        self.button_remove = ButtonDanger('Remove')
        self.label_environment = QLabel(
            'Do you want to remove environment '
            '<b>"{0}"</b>'.format(environment)
        )

        # Setup
        self.setWindowTitle('Remove environment')
        self.setMinimumWidth(380)

        # Layouts
        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_cancel)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_remove)

        layout = QVBoxLayout()
        layout.addWidget(self.label_environment)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)
        self.setLayout(layout)

        # Signals
        self.button_remove.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)


class CreateConflictDialog(DialogBase):
    """Create new environment dialog if navigator conflicts with deps."""

    def __init__(
        self, parent=None, environments=None, package=None, extra_message=''
    ):
        """Create new environment dialog if navigator conflicts with deps."""
        super(CreateConflictDialog, self).__init__(parent=parent)

        self.environments = environments
        package_name = package.split('=')[0]

        base_message = (
            '<b>{0}</b> cannot be installed on this '
            'environment.'
        ).format(package_name)

        base_message = extra_message or base_message
        # Widgets
        self.label_info = QLabel(
            base_message + '<br><br>'
            'Do you want to install the package in an existing '
            'environment or <br>create a new environment?'
            ''.format(package_name)
        )
        self.label_name = QLabel("Environment name")
        # self.text_name = QLineEdit()
        self.combo_version = ComboBoxBase()
        self.button_ok = ButtonPrimary('Create')
        self.button_cancel = ButtonNormal('Cancel')

        # Widgets setup
        self.combo_version.addItems(environments)
        # self.combo_version.setCurrentIndex(versions.index(now))
        self.combo_version.setEditable(True)
        self.combo_version.setCompleter(None)
        self.combo_version.setValidator(get_regex_validator())

        # self.text_name.setPlaceholderText("New environment name")
        self.setMinimumWidth(340)
        self.setWindowTitle(
            "Create new environment for '{}'".format(package_name)
        )
        if package_name.lower() not in [e.lower() for e in environments]:
            self.combo_version.setEditText(package_name)

        # Layouts
        vlayout = QVBoxLayout()
        vlayout.addWidget(self.label_info)
        vlayout.addWidget(SpacerVertical())
        vlayout.addWidget(SpacerVertical())

        hlayout = QHBoxLayout()
        hlayout.addWidget(self.label_name)
        hlayout.addWidget(SpacerHorizontal())
        hlayout.addWidget(self.combo_version)
        vlayout.addLayout(hlayout)

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_cancel)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_ok)

        main_layout = QVBoxLayout()
        main_layout.addLayout(vlayout)
        main_layout.addWidget(SpacerVertical())
        main_layout.addWidget(SpacerVertical())
        main_layout.addLayout(layout_buttons)

        self.setLayout(main_layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)

    @property
    def environment(self):
        return self.combo_version.currentText()


def is_valid_env_name(env, envs):
    """
    Check that an environment has a valid name.

    Windows is not case sensitive.
    """
    if os.name == 'nt':
        env = env.lower()
        envs = [e.lower() for e in envs]

    return env and env not in envs


# --- Local testing
# -----------------------------------------------------------------------------
def test():  # pragma: no cover
    """Run local tests."""
    from anaconda_navigator.utils.qthelpers import qapplication

    app = qapplication()
    widget_create = CreateDialog(parent=None, environments=['root', 'boom'])
    widget_create.show()

    widget_clone = CloneDialog(parent=None)
    widget_clone.show()

    widget_import = ImportDialog(parent=None, environments=['root', 'boom'])
    widget_import.show()

    widget_remove = RemoveDialog(parent=None, environment='Test')
    widget_remove.show()

    widget_create_conflict = CreateConflictDialog(
        parent=None,
        environments=['Test'],
        package='orange3',
    )
    widget_create_conflict.show()

    app.exec_()


if __name__ == '__main__':  # pragma: no cover
    test()
