# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright 2016 Continuum Analytics, Inc.
#
# May be copied and distributed freely only as part of an Anaconda or
# Miniconda installation.
# -----------------------------------------------------------------------------
"""Tests for environment-related dialogs."""

# yapf: disable

# Standard library imports
import os

# Third party imports
from qtpy.QtCore import Qt  # analysis:ignore
import pytest
import pytestqt.qtbot as qtbot  # analysis:ignore
import yaml

# Local imports
from anaconda_navigator.utils.fixtures import tmpfolder
from anaconda_navigator.widgets.dialogs import environment
from anaconda_navigator.widgets.dialogs.environment import (CloneDialog,
                                                            CreateDialog,
                                                            ImportDialog,
                                                            RemoveDialog)

# yapf: enable

# Contants
BASE_ENVIRONMENTS = ['test1', 'test2']


# --- Helpers
# -----------------------------------------------------------------------------
class MockGetOpenFilename:
    """Mock of the QtPy getopenfilename compatibility function."""

    def __init__(self, path, selected_filer):
        self.selected_filter = selected_filer
        self.path = path

    def __call__(self, *args, **kwargs):
        return self.path, self.selected_filter


class MockVersionInfo:
    """Mock of the sys.version_infomodule to get fake higher python version."""

    def __init__(self):
        self.major = '3'
        self.minor = '10'


class MockSysModule:
    """Mock of the sys module to get a potential higher python version."""

    def __init__(self):
        self.version_info = MockVersionInfo()


# --- Fixtures
# -----------------------------------------------------------------------------
@pytest.fixture
def tmpyamlfile(tmpfolder):
    """Return a path to a yaml file with dependencies."""
    path = os.path.join(tmpfolder, 'environment.yaml')
    data = {'dependencies': ['python']}
    with open(path, 'w') as f:
        yaml.dump(data, f, default_flow_style=False)
    return path


@pytest.fixture
def tmpyamlnamefile(tmpfolder):
    """Return a path to a yaml file with dependencies and environment name."""
    path = os.path.join(tmpfolder, 'environment.yml')
    data = {'name': 'yamltest', 'dependencies': ['python']}
    with open(path, 'w') as f:
        yaml.dump(data, f, default_flow_style=False)
    return path


@pytest.fixture
def botremove(qtbot):
    """Return bot and widget."""
    widget = RemoveDialog()
    widget.show()
    qtbot.addWidget(widget)
    return qtbot, widget


@pytest.fixture
def botcreate(qtbot):
    """Return bot and widget."""
    environment.sys = MockSysModule()
    widget = CreateDialog(environments=BASE_ENVIRONMENTS)
    widget.show()
    qtbot.addWidget(widget)
    return qtbot, widget


@pytest.fixture
def botimport(qtbot):
    """Return bot and widget."""
    widget = ImportDialog(environments=BASE_ENVIRONMENTS)
    widget.show()
    qtbot.addWidget(widget)
    return qtbot, widget


@pytest.fixture
def botclone(qtbot):
    """Return bot and widget."""
    widget = CloneDialog(environments=BASE_ENVIRONMENTS)
    widget.show()
    qtbot.addWidget(widget)
    return qtbot, widget


# --- Tests
# -----------------------------------------------------------------------------
class TestImportEnvironmentDialog:
    """Test the import environment dialog."""

    def test_conda_env_file(self, botimport, tmpyamlfile):
        bot, widget = botimport
        environment.getopenfilename = MockGetOpenFilename(
            path=tmpyamlfile, selected_filer=ImportDialog.CONDA_ENV_FILES
        )

        with bot.waitSignal(widget.text_path.textChanged, 1000, raising=True):
            bot.mouseClick(widget.button_path, Qt.LeftButton)

        # Check path was loaded on text box
        assert widget.text_path.text() == tmpyamlfile
        assert not widget.button_ok.isEnabled()

        # Check repeated env name is dissallowed
        for env in BASE_ENVIRONMENTS:
            widget.text_name.setText(env)
            assert not widget.button_ok.isEnabled()

        # Check non repeated env name is allowed
        widget.text_name.setText('testenv')
        assert widget.button_ok.isEnabled()

        # Check accepts
        with bot.waitSignal(widget.accepted, 1000, raising=True):
            bot.mouseClick(widget.button_ok, Qt.LeftButton)

    def test_conda_env_name_file(self, botimport, tmpyamlnamefile):
        bot, widget = botimport
        environment.getopenfilename = MockGetOpenFilename(
            path=tmpyamlnamefile, selected_filer=ImportDialog.CONDA_ENV_FILES
        )

        with bot.waitSignal(widget.text_path.textChanged, 1000, raising=True):
            bot.mouseClick(widget.button_path, Qt.LeftButton)

        # Check path AND name was loaded on text box
        assert widget.text_path.text() == tmpyamlnamefile
        assert widget.text_name.text() == 'yamltest'
        assert widget.button_ok.isEnabled()

    def test_conda_env_invalid_file(self, botimport):
        bot, widget = botimport
        environment.getopenfilename = MockGetOpenFilename(
            path='whatever-invalid-path',
            selected_filer=ImportDialog.CONDA_ENV_FILES
        )

        with bot.waitSignal(widget.text_path.textChanged, 1000, raising=True):
            bot.mouseClick(widget.button_path, Qt.LeftButton)

        # Check filled name and invalid path
        widget.text_name.setText('yamltest')
        assert not widget.button_ok.isEnabled()

        # Check cancels
        with bot.waitSignal(widget.rejected, 1000, raising=True):
            bot.mouseClick(widget.button_cancel, Qt.LeftButton)


class TestCreateEnvironmentDialog:
    """Test the create environment dialog."""

    def test_refresh(self, botcreate):
        bot, widget = botcreate
        assert not widget.button_ok.isEnabled()
        bot.keyClicks(widget.text_name, 'test')
        assert widget.button_ok.isEnabled()
        widget.text_name.setText("")
        bot.keyClicks(widget.text_name, 'test1')
        assert not widget.button_ok.isEnabled()

    def test_py_r(self, botcreate):
        bot, widget = botcreate
        bot.keyClicks(widget.text_name, 'test')
        bot.mouseClick(widget.check_python, Qt.LeftButton)
        assert not widget.button_ok.isEnabled()
        assert not widget.combo_version.isEnabled()
        bot.mouseClick(widget.check_r, Qt.LeftButton)
        assert widget.button_ok.isEnabled()

    def test_create(self, botcreate):
        bot, widget = botcreate
        bot.keyClicks(widget.text_name, 'test')
        with bot.waitSignal(widget.accepted, 1000, raising=True):
            bot.mouseClick(widget.button_ok, Qt.LeftButton)

    def test_cancel(self, botcreate):
        bot, widget = botcreate
        with bot.waitSignal(widget.rejected, 1000, raising=True):
            bot.mouseClick(widget.button_cancel, Qt.LeftButton)


class TestCloneEnvironmentDialog:
    """Test the clone environment dialog."""

    def test_name(self, botclone):
        bot, widget = botclone

        # Check repeated env name is dissallowed
        for env in BASE_ENVIRONMENTS:
            widget.text_name.setText('')
            bot.keyClicks(widget.text_name, env)
            assert not widget.button_ok.isEnabled()

        widget.text_name.setText('')
        bot.keyClicks(widget.text_name, 'validenvname')
        assert widget.button_ok.isEnabled()

    def test_clone(self, botclone):
        bot, widget = botclone

        with bot.waitSignal(widget.accepted, 1000, raising=True):
            bot.mouseClick(widget.button_ok, Qt.LeftButton)

    def test_cancel(self, botclone):
        bot, widget = botclone

        with bot.waitSignal(widget.rejected, 1000, raising=True):
            bot.mouseClick(widget.button_cancel, Qt.LeftButton)


class TestRemoveEnvironmentDialog:
    """Test the remove environment dialog."""

    def test_remove(self, botremove):
        bot, widget = botremove

        with bot.waitSignal(widget.accepted, 1000, raising=True):
            bot.mouseClick(widget.button_remove, Qt.LeftButton)

    def test_cancel(self, botremove):
        bot, widget = botremove

        with bot.waitSignal(widget.rejected, 1000, raising=True):
            bot.mouseClick(widget.button_cancel, Qt.LeftButton)
