# -*- coding: utf-8 -*-
"""Tests the xonsh environment."""
from __future__ import unicode_literals, print_function
import os
import tempfile
import builtins
import itertools
from tempfile import TemporaryDirectory
from xonsh.tools import ON_WINDOWS

import pytest

from xonsh.commands_cache import CommandsCache
from xonsh.environ import Env, load_static_config, locate_binary, DEFAULT_ENSURERS, DEFAULT_VALUES

from tools import skip_if_on_unix

def test_env_normal():
    env = Env(VAR='wakka')
    assert 'wakka' == env['VAR']

def test_env_contains():
    env = Env(VAR='wakka')
    assert 'VAR' in env

@pytest.mark.parametrize('path', [['/home/wakka'], ['wakka']])
def test_env_path_list(path):
    env = Env(MYPATH=path)
    assert path == env['MYPATH'].paths

@pytest.mark.parametrize('path', [
    ['/home/wakka' + os.pathsep + '/home/jawaka'],
    ['wakka' + os.pathsep + 'jawaka']
])
def test_env_path_str(path):
    env = Env(MYPATH=path)
    assert path == env['MYPATH'].paths

def test_env_detype():
    env = Env(MYPATH=['wakka', 'jawaka'])
    assert 'wakka' + os.pathsep + 'jawaka' == env.detype()['MYPATH']

@pytest.mark.parametrize('path1, path2',[
    (['/home/wakka', '/home/jawaka'], '/home/woah'),
    (['wakka', 'jawaka'], 'woah')
])
def test_env_detype_mutable_access_clear(path1, path2):
    env = Env(MYPATH=path1)
    assert path1[0] + os.pathsep + path1[1] == env.detype()['MYPATH']
    env['MYPATH'][0] = path2
    assert env._detyped is None
    assert path2 + os.pathsep + path1[1] == env.detype()['MYPATH']

def test_env_detype_no_dict():
    env = Env(YO={'hey': 42})
    det = env.detype()
    assert 'YO' not in det

def test_histcontrol_none():
    env = Env(HISTCONTROL=None)
    assert isinstance(env['HISTCONTROL'], set)
    assert len(env['HISTCONTROL']) == 0

def test_HISTCONTROL_empty():
    env = Env(HISTCONTROL='')
    assert isinstance(env['HISTCONTROL'], set)
    assert len(env['HISTCONTROL']) == 0

def test_histcontrol_ignoredups():
    env = Env(HISTCONTROL='ignoredups')
    assert isinstance(env['HISTCONTROL'], set)
    assert len(env['HISTCONTROL']) == 1
    assert ('ignoredups' in env['HISTCONTROL'])
    assert ('ignoreerr' not in env['HISTCONTROL'])

def test_histcontrol_ignoreerr_ignoredups():
    env = Env(HISTCONTROL='ignoreerr,ignoredups,ignoreerr')
    assert len(env['HISTCONTROL']) == 2
    assert ('ignoreerr' in env['HISTCONTROL'])
    assert ('ignoredups' in env['HISTCONTROL'])

def test_swap():
    env = Env(VAR='wakka')
    assert env['VAR'] == 'wakka'

    # positional arg
    with env.swap({'VAR': 'foo'}):
        assert env['VAR'] == 'foo'

    # make sure the environment goes back outside the context manager
    assert env['VAR'] == 'wakka'

    # kwargs only
    with env.swap(VAR1='foo', VAR2='bar'):
        assert env['VAR1'] == 'foo'
        assert env['VAR2'] == 'bar'

    # positional and kwargs
    with env.swap({'VAR3': 'baz'}, VAR1='foo', VAR2='bar'):
        assert env['VAR1'] == 'foo'
        assert env['VAR2'] == 'bar'
        assert env['VAR3'] == 'baz'

    # make sure the environment goes back outside the context manager
    assert env['VAR'] == 'wakka'
    assert 'VAR1' not in env
    assert 'VAR2' not in env
    assert 'VAR3' not in env


@pytest.mark.parametrize('s, exp, loaded',[
    (b'{"best": "awash"}', {'best': 'awash'}, True), # works
    (b'["best", "awash"]', {}, False), # fail
    (b'{"best": "awash"', {}, False) # json fail
])
def test_load_static_config(s, exp, loaded, tmpdir, xonsh_builtins):
    env = Env({'XONSH_SHOW_TRACEBACK': False})
    xonsh_builtins.__xonsh_env__ = env
    f = tmpdir.join('test_static_config')
    f.write(s)
    conf = load_static_config(env, str(f))
    assert exp == conf
    assert env['LOADED_CONFIG'] == loaded


@skip_if_on_unix
def test_locate_binary_on_windows(xonsh_builtins):
    files = ('file1.exe', 'FILE2.BAT', 'file3.txt')
    with TemporaryDirectory() as tmpdir:
        for fname in files:
            fpath = os.path.join(tmpdir, fname)
            with open(fpath, 'w') as f:
                f.write(fpath)
        xonsh_builtins.__xonsh_env__.update({
            'PATH': [tmpdir],
            'PATHEXT': ['.COM', '.EXE', '.BAT'],
        })
        xonsh_builtins.__xonsh_commands_cache__ = CommandsCache()
        assert locate_binary('file1') == os.path.join(tmpdir, 'file1.exe')
        assert locate_binary('file1.exe') == os.path.join(tmpdir, 'file1.exe')
        assert locate_binary('file2') == os.path.join(tmpdir, 'FILE2.BAT')
        assert locate_binary('file2.bat') == os.path.join(tmpdir, 'FILE2.BAT')
        assert locate_binary('file3') is None


def test_event_on_envvar_change(xonsh_builtins):
    env = Env(TEST=0)
    xonsh_builtins.__xonsh_env__ = env
    share = []
    # register
    @xonsh_builtins.events.on_envvar_change
    def handler(name, oldvalue, newvalue, **kwargs):
        share.extend((name, oldvalue, newvalue))

    # trigger
    env['TEST'] = 1

    assert share == ['TEST', 0, 1]


def test_event_on_envvar_new(xonsh_builtins):
    env = Env()
    xonsh_builtins.__xonsh_env__ = env
    share = []
    # register
    @xonsh_builtins.events.on_envvar_new
    def handler(name, value, **kwargs):
        share.extend((name, value))

    # trigger
    env['TEST'] = 1

    assert share == ['TEST', 1]

def test_event_on_envvar_change_from_none_value(xonsh_builtins):
    env = Env(TEST=None)
    xonsh_builtins.__xonsh_env__ = env
    share = []
    # register
    @xonsh_builtins.events.on_envvar_change
    def handler(name, oldvalue, newvalue, **kwargs):
        share.extend((name, oldvalue, newvalue))

    # trigger
    env['TEST'] = 1

    assert share == ['TEST', None, 1]


@pytest.mark.parametrize('val', [1, None, True, 'ok'])
def test_event_on_envvar_change_no_fire_when_value_is_same(val, xonsh_builtins):
    env = Env(TEST=val)
    xonsh_builtins.__xonsh_env__ = env
    share = []
    # register
    @xonsh_builtins.events.on_envvar_change
    def handler(name, oldvalue, newvalue, **kwargs):
        share.extend((name, oldvalue, newvalue))


    # trigger
    env['TEST'] = val

    assert share == []


def test_events_on_envvar_called_in_right_order(xonsh_builtins):
    env = Env()
    xonsh_builtins.__xonsh_env__ = env
    share = []
    # register
    @xonsh_builtins.events.on_envvar_new
    def handler(name, value, **kwargs):
        share[:] = ['new']

    @xonsh_builtins.events.on_envvar_change
    def handler(name, oldvalue, newvalue, **kwargs):
        share[:] = ['change']

    # trigger new
    env['TEST'] = 1

    assert share == ['new']

    # trigger change
    env['TEST'] = 2

    assert share == ['change']


def test_int_bool_envvars_have_ensurers():
    bool_ints = [type(envvar) in [bool, int] for envvar in DEFAULT_VALUES.values()]
    key_mask = set(itertools.compress(DEFAULT_VALUES.keys(), bool_ints))
    ensurer_keys = set(DEFAULT_ENSURERS.keys())
    assert len(key_mask.intersection(ensurer_keys)) == len(key_mask)
