import os

import pytest
from mock import Mock, patch

from gcasc import ClientInitializationError, GitlabConfigurationAsCode, Mode
from tests import helpers

GITLAB_CLIENT_CONFIG_FILE = ["GITLAB_CLIENT_CONFIG", "GITLAB_CLIENT_CONFIG_FILE"]
GITLAB_CLIENT_CERTIFICATE = ["GITLAB_CLIENT_CERT", "GITLAB_CLIENT_CERTIFICATE"]
GITLAB_CLIENT_KEY = "GITLAB_CLIENT_KEY"
GITLAB_CLIENT_URL = "GITLAB_CLIENT_URL"
GITLAB_CLIENT_API_VERSION = "GITLAB_CLIENT_API_VERSION"
GITLAB_CLIENT_TOKEN = "GITLAB_CLIENT_TOKEN"
GITLAB_CLIENT_SSL_VERIFY = "GITLAB_CLIENT_SSL_VERIFY"
GITLAB_CONFIG_FILE = ["GITLAB_CONFIG_FILE", "GITLAB_CONFIG_PATH"]

GITLAB_MODE = "GITLAB_MODE"

GITLAB_CONFIG_FILE_DEFAULT_PATHS = [
    "/etc/python-gitlab.cfg",
    "/etc/gitlab.cfg",
    "~/.python-gitlab.cfg",
    "~/.gitlab.cfg",
]


def __mock_gitlab(gitlab_class_mock):
    gitlab = Mock()
    gitlab.version.return_value = ('test', 'test')
    gitlab_class_mock.from_config.return_value = gitlab
    gitlab_class_mock.return_value = gitlab
    return gitlab


@pytest.fixture(scope="class", autouse=True)
def configure_shared_environment(request):
    os.environ['GITLAB_CONFIG_FILE'] = helpers.get_file_path('gitlab.yml')

    gitlab_patch = patch('gitlab.Gitlab')
    gitlab_mock_class = gitlab_patch.__enter__()
    __mock_gitlab(gitlab_mock_class)

    def unpatch():
        gitlab_patch.__exit__()

    def clean_env():
        for key, value in os.environ.items():
            if key.startswith('GITLAB_'):
                del os.environ[key]

    request.addfinalizer(unpatch)
    request.addfinalizer(clean_env)


@patch('gitlab.Gitlab')
def test_gitlab_client_created_from_config_file(gitlab_class_mock):
    # given
    config_path = helpers.get_file_path('gitlab_config_valid.cfg')
    os.environ['GITLAB_CLIENT_CONFIG_FILE'] = config_path
    __mock_gitlab(gitlab_class_mock)

    # when
    GitlabConfigurationAsCode()

    # then
    gitlab_class_mock.assert_called_once_with(private_token='my_token', url='https://my.gitlab.com',
                                              ssl_verify=False, api_version='4')


@patch('gitlab.Gitlab')
def test_gitlab_client_created_from_environment(gitlab_class_mock):
    os.environ['GITLAB_CLIENT_TOKEN'] = 'token'
    os.environ['GITLAB_CLIENT_URL'] = 'url'
    os.environ['GITLAB_CLIENT_API_VERSION'] = 'api_version'
    os.environ['GITLAB_CLIENT_SSL_VERIFY'] = 'ssl_verify'
    __mock_gitlab(gitlab_class_mock)

    # when
    GitlabConfigurationAsCode()

    # then
    gitlab_class_mock.assert_called_once_with(private_token='token', url='url', ssl_verify='ssl_verify',
                                              api_version='api_version')

@patch('gitlab.Gitlab')
def test_gitlab_client_created_from_file_and_environment(gitlab_class_mock):
    # given
    config_path = helpers.get_file_path('gitlab_config_invalid.cfg')
    os.environ['GITLAB_CLIENT_CONFIG_FILE'] = config_path
    os.environ['GITLAB_CLIENT_TOKEN'] = 'token'
    __mock_gitlab(gitlab_class_mock)

    # when
    GitlabConfigurationAsCode()

    # then
    gitlab_class_mock.assert_called_once_with(private_token='token', url='https://my.gitlab.com',
                                              ssl_verify=True, api_version='4')


def test_gitlab_config_loaded_from_file():
    # given
    os.environ['GITLAB_CLIENT_TOKEN'] = 'some_token'

    # when
    gcasc = GitlabConfigurationAsCode()

    # then
    assert gcasc.config.config == helpers.read_yaml('gitlab.yml')


def test_session_initialized_when_config_provided():
    certificate = helpers.get_file_path('dummycert.crt')
    key = helpers.get_file_path('dummykey.key')
    os.environ['GITLAB_CLIENT_TOKEN'] = 'token'
    os.environ['GITLAB_CLIENT_CERTIFICATE'] = certificate
    os.environ['GITLAB_CLIENT_KEY'] = key

    gcasc = GitlabConfigurationAsCode()

    assert gcasc.gitlab.session.cert == (certificate, key)


def test_error_raised_when_unable_to_create_gitlab_client():
    with pytest.raises(ClientInitializationError) as error:
        GitlabConfigurationAsCode()

        assert "config file" in error
        assert "environment variables" in error


def test_apply_mode_is_used_when_not_provided():
    os.environ['GITLAB_CLIENT_TOKEN'] = 'token'

    gcasc = GitlabConfigurationAsCode()

    assert gcasc.mode == Mode.APPLY
