# -*- coding: utf-8 -*-

# -----------------------------------------------------------------------------
# Copyright (c) 2016-2017 Anaconda, 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 contextlib
import os
from unittest import mock

# Third party imports
import typing

from qtpy.QtCore import QPoint, Qt
import pytest
import yaml

# Local imports
from anaconda_navigator.utils.fixtures import tmpfolder  # used as fixture
from anaconda_navigator.widgets.dialogs import environment
from anaconda_navigator.widgets.dialogs.environment import common


# yapf: enable

# Contants
BASE_ENVIRONMENTS = {
    '/usr/anaconda': 'root',
    '/usr/anaconda/envs/test1': 'test1',
    '/usr/anaconda/envs/test2': 'test2',
}
BASE_INFO = {
    '__environments': BASE_ENVIRONMENTS,
    '__envs_dirs_writable': ['/usr/anaconda/envs/'],
}

BASE_DATA = {
    'info': BASE_INFO,
    'processed_info': BASE_INFO,
    'packages': {
        'python': {
            'versions': ['2.7', '3.6', '3.9', '3.7', '3.8', '3.5'],
        },
        'r-base': {
            'versions': ['3.1.2', '3.1.3', '3.2.0', '3.2.1', '3.2.2', '3.3.1', '3.3.2', '3.4.1', '3.4.2', '3.4.3'],
        },
    }
}


# --- Helpers
# -----------------------------------------------------------------------------

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

    major = '3'
    minor = '7'


@contextlib.contextmanager
def mocked_compat(
        path,
        selected_filter,
        target='anaconda_navigator.widgets.dialogs.environment.import_dialogs.compat',
) -> typing.Iterable[None]:
    """Mock compat library """
    new_compat = mock.Mock()
    new_compat.getopenfilename.return_value = path, selected_filter

    with mock.patch(target, new_compat):
        yield


@contextlib.contextmanager
def mocked_sys(target='anaconda_navigator.widgets.dialogs.environment.create_dialogs.sys') -> typing.Iterable[None]:
    """Mock sys library."""
    new_sys = mock.Mock()
    new_sys.version_info = MockVersionInfo

    with mock.patch(target, new_sys):
        yield


def widget_pos(widget):
    return QPoint(2, widget.height() / 2)


# --- 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 = environment.RemoveDialog(name='test', prefix='/usr/anaconda')
    widget.show()
    qtbot.addWidget(widget)
    return qtbot, widget


@pytest.fixture
def botcreate(qtbot):
    """Return bot and widget."""
    with mocked_sys():
        widget = environment.CreateDialog()
        with qtbot.waitSignals(
            [package_group.sig_refreshed for package_group in widget.package_groups.values()],
            timeout=1200000,
        ):
            widget.setup(None, BASE_DATA, None)
            widget.show()

        qtbot.addWidget(widget)
        return qtbot, widget


@pytest.fixture
def botclone(qtbot):
    """Return bot and widget."""
    widget = environment.CloneDialog(parent=None, clone_from_name='test1')
    widget.setup(None, BASE_DATA, None)
    widget.show()
    qtbot.addWidget(widget)
    return qtbot, widget


@pytest.fixture
def botconflict(qtbot):
    """Return bot and widget."""
    widget = environment.ConflictDialog(parent=None, package='conflict-package')
    widget.setup(None, BASE_DATA, None)
    widget.show()
    qtbot.addWidget(widget)
    return qtbot, widget


@pytest.fixture
def botbase(qtbot):
    """Return bot and widget."""
    widget = common.EnvironmentActionsDialog(parent=None)
    widget.show()
    qtbot.addWidget(widget)
    return qtbot, widget


# --- Tests
# -----------------------------------------------------------------------------
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, 'some-env-name')
        assert widget.button_ok.isEnabled()

        widget.text_name.setText("")
        bot.keyClicks(widget.text_name, 'test1')
        assert not widget.button_ok.isEnabled()

    def test_packages(self, botcreate):
        bot, widget = botcreate

        bot.keyClicks(widget.text_name, 'some-env-name')
        assert widget.button_ok.isEnabled()
        assert widget.package_groups['python'].checked
        assert not widget.package_groups['r'].checked
        assert {item.split('=', 1)[0] for item in widget.packages} == {'python'}

        bot.mouseClick(
            widget.package_groups['python'].checkbox,
            Qt.LeftButton,
            pos=widget_pos(widget.package_groups['python'].checkbox),
        )
        assert not widget.button_ok.isEnabled()
        assert not widget.package_groups['python'].checked
        assert not widget.package_groups['r'].checked
        assert {item.split('=', 1)[0] for item in widget.packages} == set()

        bot.mouseClick(
            widget.package_groups['r'].checkbox,
            Qt.LeftButton,
            pos=widget_pos(widget.package_groups['r'].checkbox),
        )
        assert widget.button_ok.isEnabled()
        assert not widget.package_groups['python'].checked
        assert widget.package_groups['r'].checked
        assert {item.split('=', 1)[0] for item in widget.packages} == {'r-base', 'r-essentials'}

        bot.mouseClick(
            widget.package_groups['python'].checkbox,
            Qt.LeftButton,
            pos=widget_pos(widget.package_groups['python'].checkbox),
        )
        assert widget.button_ok.isEnabled()
        assert widget.package_groups['python'].checked
        assert widget.package_groups['r'].checked
        assert {item.split('=', 1)[0] for item in widget.packages} == {'python', 'r-base', 'r-essentials'}

    def test_create(self, botcreate):
        bot, widget = botcreate

        bot.keyClicks(widget.text_name, 'some-env-name')
        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_prefix, env_name in BASE_ENVIRONMENTS.items():
            widget.text_name.setText('')
            bot.keyClicks(widget.text_name, env_name)
            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
        bot.keyClicks(widget.text_name, 'validenvname')

        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_ok, 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)


class TestEnvironmentConflict:
    def test_name(self, botconflict):
        qtbot, widget = botconflict
        assert widget.name == 'conflict-package'


class TestEnvironmentDialogBase:
    def test_refresh(self, botbase):
        qtbot, widget = botbase
        with pytest.raises(NotImplementedError):
            widget.refresh()

    def test_prefix(self, botbase):
        qtbot, widget = botbase
        assert not widget.prefix
        assert not widget.name
