"""Test config file migration"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

import os
import re
import shutil
from subprocess import check_call
from tempfile import mkdtemp
try:
    from unittest.mock import patch
except ImportError:
    from mock import patch

import pytest

from ipython_genutils.path import ensure_dir_exists
try:
    from IPython.paths import locate_profile
except ImportError:
    from IPython.utils.path import locate_profile

from jupyter_core.paths import jupyter_data_dir
from jupyter_core.migrate import (
    migrate, migrate_one, migrate_config,
    migrate_dir, migrate_file, migrate_static_custom,
)
from jupyter_core import migrate as migrate_mod

pjoin = os.path.join
here = os.path.dirname(__file__)
dotipython = pjoin(here, 'dotipython')
dotipython_empty = pjoin(here, 'dotipython_empty')


@pytest.fixture
def td(request):
    """Fixture for a temporary directory"""
    td = mkdtemp()
    request.addfinalizer(lambda : shutil.rmtree(td))
    return td

@pytest.fixture
def env(request):
    """Fixture for a full testing environment"""
    td = mkdtemp()
    env = {
        'TESTDIR': td,
        'IPYTHONDIR': pjoin(td, 'ipython'),
        'JUPYTER_CONFIG_DIR': pjoin(td, 'jupyter'),
        'JUPYTER_DATA_DIR': pjoin(td, 'jupyter_data'),
        'JUPYTER_RUNTIME_DIR': pjoin(td, 'jupyter_runtime'),
        'JUPYTER_PATH': '',
    }
    env_patch = patch.dict(os.environ, env)
    env_patch.start()
    
    def fin():
        """Cleanup test env"""
        env_patch.stop()
        shutil.rmtree(td)
    request.addfinalizer(fin)
    
    return env


def touch(path, content=''):
    ensure_dir_exists(os.path.dirname(path))
    with open(path, 'w') as f:
        f.write(content)


def assert_files_equal(a, b):
    """Verify that two files match"""
    
    assert os.path.exists(b)
    with open(a) as f:
        a_txt = f.read()
    
    with open(b) as f:
        b_txt = f.read()
    
    assert a_txt == b_txt


def test_migrate_file(td):
    src = pjoin(td, 'src')
    dst = pjoin(td, 'dst')
    touch(src, 'test file')
    assert migrate_file(src, dst)
    assert_files_equal(src, dst)
    
    src2 = pjoin(td, 'src2')
    touch(src2, 'different src')
    assert not migrate_file(src2, dst)
    assert_files_equal(src, dst)


def test_migrate_dir(td):
    src = pjoin(td, 'src')
    dst = pjoin(td, 'dst')
    os.mkdir(src)
    assert not migrate_dir(src, dst)
    assert not os.path.exists(dst)
    
    touch(pjoin(src, 'f'), 'test file')
    assert migrate_dir(src, dst)
    assert_files_equal(pjoin(src, 'f'), pjoin(dst, 'f'))
    
    touch(pjoin(src, 'g'), 'other test file')
    assert not migrate_dir(src, dst)
    assert not os.path.exists(pjoin(dst, 'g'))
    
    shutil.rmtree(dst)
    os.mkdir(dst)
    assert migrate_dir(src, dst)
    assert_files_equal(pjoin(src, 'f'), pjoin(dst, 'f'))
    assert_files_equal(pjoin(src, 'g'), pjoin(dst, 'g'))


def test_migrate_one(td):
    src = pjoin(td, 'src')
    srcdir = pjoin(td, 'srcdir')
    dst = pjoin(td, 'dst')
    dstdir = pjoin(td, 'dstdir')
    
    touch(src, 'test file')
    touch(pjoin(srcdir, 'f'), 'test dir file')
    
    called = {}
    def notice_m_file(src, dst):
        called['migrate_file'] = True
        return migrate_file(src, dst)
    
    def notice_m_dir(src, dst):
        called['migrate_dir'] = True
        return migrate_dir(src, dst)
    
    with patch.object(migrate_mod, 'migrate_file', notice_m_file), \
            patch.object(migrate_mod, 'migrate_dir', notice_m_dir):
        assert migrate_one(src, dst)
        assert called == {'migrate_file': True}
        called.clear()
        assert migrate_one(srcdir, dstdir)
        assert called == {'migrate_dir': True}
        called.clear()
        assert not migrate_one(pjoin(td, 'dne'), dst)
        assert called == {}


def test_migrate_config(td):
    profile = pjoin(td, 'profile')
    jpy = pjoin(td, 'jupyter_config')
    ensure_dir_exists(profile)
    
    env = {
        'profile': profile,
        'jupyter_config': jpy,
    }
    cfg_py = pjoin(profile, 'ipython_test_config.py')
    touch(cfg_py, 'c.Klass.trait = 5\n')
    empty_cfg_py = pjoin(profile, 'ipython_empty_config.py')
    touch(empty_cfg_py, '# c.Klass.trait = 5\n')
    
    assert not migrate_config('empty', env)
    assert not os.path.exists(jpy)
    
    with patch.dict(migrate_mod.config_substitutions, {
        re.compile(r'\bKlass\b'): 'Replaced',
    }):
        assert migrate_config('test', env)
    
    assert os.path.isdir(jpy)
    assert sorted(os.listdir(jpy)) == [
        'jupyter_test_config.py',
    ]
    
    with open(pjoin(jpy, 'jupyter_test_config.py')) as f:
        text = f.read()
    assert text == 'c.Replaced.trait = 5\n'


def test_migrate_custom_default(td):
    profile = pjoin(dotipython, 'profile_default')
    src = pjoin(profile, 'static', 'custom')
    assert os.path.exists(src)
    assert not migrate_static_custom(src, td)
    
    src = pjoin(td, 'src')
    dst = pjoin(td, 'dst')
    os.mkdir(src)
    src_custom_js = pjoin(src, 'custom.js')
    src_custom_css = pjoin(src, 'custom.css')
    touch(src_custom_js, 'var a=5;')
    touch(src_custom_css, 'div { height: 5px; }')

    assert migrate_static_custom(src, dst)


def test_migrate_nothing(env):
    migrate()
    assert os.listdir(env['JUPYTER_CONFIG_DIR']) == ['migrated']
    assert not os.path.exists(env['JUPYTER_DATA_DIR'])


def test_migrate_default(env):
    shutil.copytree(dotipython_empty, env['IPYTHONDIR'])
    migrate()
    assert os.listdir(env['JUPYTER_CONFIG_DIR']) == ['migrated']
    assert not os.path.exists(env['JUPYTER_DATA_DIR'])


def test_migrate(env):
    shutil.copytree(dotipython, env['IPYTHONDIR'])
    migrate()
    assert os.path.exists(env['JUPYTER_CONFIG_DIR'])
    assert os.path.exists(env['JUPYTER_DATA_DIR'])


