import os
import pytest

import perun.utils.helpers as helpers
import perun.utils.streams as streams
import perun.logic.store as store
import perun.logic.index as index
import perun.utils.exceptions as exceptions
import perun.utils.timestamps as timestamps

__author__ = 'Tomas Fiedor'


@pytest.mark.usefixtures('cleandir')
def test_malformed_indexes(tmpdir, monkeypatch, capsys):
    """Tests malformed indexes"""
    index_file = os.path.join(str(tmpdir), "index")
    index.touch_index(index_file)

    # Try different number of stuff
    old_read_int = store.read_int_from_handle
    def mocked_read_int(_):
        return 2
    monkeypatch.setattr('perun.logic.store.read_int_from_handle', mocked_read_int)
    with open(index_file, 'rb') as index_handle:
        with pytest.raises(SystemExit):
            print(list(index.walk_index(index_handle)))
    _, err = capsys.readouterr()
    assert "fatal: malformed index file: too many or too few objects registered in index" in err
    monkeypatch.setattr('perun.logic.store.read_int_from_handle', old_read_int)

    monkeypatch.setattr('perun.logic.index.INDEX_VERSION', index.INDEX_VERSION - 1)
    with open(index_file, 'rb') as index_handle:
        with pytest.raises(exceptions.MalformedIndexFileException) as exc:
            index.print_index_from_handle(index_handle)
        assert "different index version" in str(exc.value)

    index_file = os.path.join(str(tmpdir), "index2")
    index.touch_index(index_file)
    monkeypatch.setattr('perun.logic.index.INDEX_MAGIC_PREFIX', index.INDEX_MAGIC_PREFIX.upper())
    with open(index_file, 'rb') as index_handle:
        with pytest.raises(exceptions.MalformedIndexFileException) as exc:
            index.print_index_from_handle(index_handle)
        assert "not an index file" in str(exc.value)


@pytest.mark.usefixtures('cleandir')
def test_correct_index(tmpdir):
    """Test correct working with index"""
    index_file = os.path.join(str(tmpdir), "index")
    index.touch_index(index_file)
    index.print_index(index_file)


@pytest.mark.usefixtures('cleandir')
def test_versions(tmpdir, monkeypatch):
    """Test correct working with index"""
    monkeypatch.setattr('perun.logic.index.INDEX_VERSION', index.IndexVersion.SlowLorris.value)
    pool_path = os.path.join(os.path.split(__file__)[0], 'profiles', 'degradation_profiles')
    profile_name = os.path.join(pool_path, 'linear_base.perf')
    profile = store.load_profile_from_file(profile_name, True)

    index_file = os.path.join(str(tmpdir), "index")
    index.touch_index(index_file)

    st = timestamps.timestamp_to_str(os.stat(profile_name).st_mtime)
    sha = store.compute_checksum("Wow, such checksum".encode('utf-8'))
    basic_entry = index.BasicIndexEntry(st, sha, profile_name, index.INDEX_ENTRIES_START_OFFSET)
    index.write_entry_to_index(index_file, basic_entry)

    with pytest.raises(SystemExit):
        with open(index_file, 'rb+') as index_handle:
            index.BasicIndexEntry.read_from(index_handle, index.IndexVersion.FastSloth)

    with open(index_file, 'rb+') as index_handle:
        index_handle.seek(index.INDEX_ENTRIES_START_OFFSET)
        entry = index.BasicIndexEntry.read_from(index_handle, index.IndexVersion.SlowLorris)
        assert entry == basic_entry
    index.print_index(index_file)

    # Test update to version 2.0 index
    with open(index_file, 'rb+') as index_handle:
        index_handle.seek(4)
        version = store.read_int_from_handle(index_handle)
        assert version == index.IndexVersion.SlowLorris.value
    monkeypatch.setattr('perun.logic.index.INDEX_VERSION', index.IndexVersion.FastSloth.value)
    monkeypatch.setattr('perun.logic.store.split_object_name', lambda _, __: (None, index_file))
    monkeypatch.setattr('perun.logic.index.walk_index', lambda _: [])
    index.get_profile_list_for_minor(os.getcwd(), index_file)
    with open(index_file, 'rb+') as index_handle:
        index_handle.seek(4)
        version = store.read_int_from_handle(index_handle)
        assert version == index.IndexVersion.FastSloth.value

    # Test version 2 index
    monkeypatch.setattr('perun.logic.index.INDEX_VERSION', index.IndexVersion.FastSloth.value)
    index_v2_file = os.path.join(str(tmpdir), "index_v2")
    index.touch_index(index_v2_file)
    extended_entry = index.ExtendedIndexEntry(st, sha, profile_name, index.INDEX_ENTRIES_START_OFFSET, profile)
    index.write_entry_to_index(index_v2_file, extended_entry)
    with open(index_v2_file, 'rb+') as index_handle:
        index_handle.seek(index.INDEX_ENTRIES_START_OFFSET)
        stored = index.ExtendedIndexEntry.read_from(index_handle, index.IndexVersion.FastSloth)
        assert stored == extended_entry
    index.print_index(index_v2_file)

    # Test FastSloth with SlowLorris
    monkeypatch.setattr('perun.logic.index.INDEX_VERSION', index.IndexVersion.FastSloth.value)
    monkeypatch.setattr('perun.logic.pcs.get_object_directory', lambda: '')
    monkeypatch.setattr('perun.logic.store.load_profile_from_file', lambda *_, **__: profile)
    index_v1_2_file = os.path.join(str(tmpdir), "index_v1_2")
    index.touch_index(index_v1_2_file)
    index.write_entry_to_index(index_v1_2_file, basic_entry)
    with open(index_v1_2_file, 'rb+') as index_handle:
        index_handle.seek(index.INDEX_ENTRIES_START_OFFSET)
        stored = index.ExtendedIndexEntry.read_from(index_handle, index.IndexVersion.SlowLorris)
        assert stored.__dict__ == extended_entry.__dict__


@pytest.mark.usefixtures('cleandir')
def test_helpers(tmpdir):
    index_file = os.path.join(str(tmpdir), "index")
    index.touch_index(index_file)

    with open(index_file, 'rb+') as index_handle:
        store.write_string_to_handle(index_handle, "Hello Dolly!")
        index_handle.seek(0)
        stored_string = store.read_string_from_handle(index_handle)
        assert stored_string == "Hello Dolly!"

        current_position = index_handle.tell()
        store.write_list_to_handle(index_handle, ['hello', 'dolly'])
        index_handle.seek(current_position)
        stored_list = store.read_list_from_handle(index_handle)
        assert stored_list == ['hello', 'dolly']


@pytest.mark.usefixtures('cleandir')
def test_streams(tmpdir, monkeypatch):
    """Test various untested behaviour"""
    # Loading from nonexistant file
    yaml = streams.safely_load_yaml_from_file("nonexistant")
    assert yaml == {}

    # Load file with incorrect encoding
    tmp_file = tmpdir.mkdir("tmp").join("tmp.file")
    with open(tmp_file, 'wb') as tmp:
        tmp.write(bytearray("hello šunte", "windows-1252"))
    file = streams.safely_load_file(tmp_file)
    assert file == []

    # Safely load from string
    yaml = streams.safely_load_yaml_from_stream('"root: 1"')
    assert yaml == {'root': 1}

    # Bad yaml
    yaml = streams.safely_load_yaml_from_stream('"root: "1 "')
    assert yaml == {}

    # Nonexistant file
    with pytest.raises(exceptions.IncorrectProfileFormatException):
        store.load_profile_from_file("nonexistant", False)

    monkeypatch.setattr("perun.logic.store.read_and_deflate_chunk", lambda _: "p mixed 1\0tmp")
    with pytest.raises(exceptions.IncorrectProfileFormatException):
        store.load_profile_from_file(tmp_file, False)
